Run cargo clippy on whole project (#322)
* Run cargo clippy on plume-common Run clippy on plume-common and adjuste code accordingly * Run cargo clippy on plume-model Run clippy on plume-model and adjuste code accordingly * Reduce need for allocation in plume-common * Reduce need for allocation in plume-model add a quick compilation failure if no database backend is enabled * Run cargo clippy on plume-cli * Run cargo clippy on plume
This commit is contained in:
@@ -37,7 +37,7 @@ pub trait Notify<C> {
|
||||
|
||||
pub trait Deletable<C, A> {
|
||||
fn delete(&self, conn: &C) -> A;
|
||||
fn delete_id(id: String, actor_id: String, conn: &C);
|
||||
fn delete_id(id: &str, actor_id: &str, conn: &C);
|
||||
}
|
||||
|
||||
pub trait WithInbox {
|
||||
|
||||
@@ -15,10 +15,10 @@ pub mod inbox;
|
||||
pub mod request;
|
||||
pub mod sign;
|
||||
|
||||
pub const CONTEXT_URL: &'static str = "https://www.w3.org/ns/activitystreams";
|
||||
pub const PUBLIC_VISIBILTY: &'static str = "https://www.w3.org/ns/activitystreams#Public";
|
||||
pub const CONTEXT_URL: &str = "https://www.w3.org/ns/activitystreams";
|
||||
pub const PUBLIC_VISIBILTY: &str = "https://www.w3.org/ns/activitystreams#Public";
|
||||
|
||||
pub const AP_CONTENT_TYPE: &'static str = r#"application/ld+json; profile="https://www.w3.org/ns/activitystreams""#;
|
||||
pub const AP_CONTENT_TYPE: &str = r#"application/ld+json; profile="https://www.w3.org/ns/activitystreams""#;
|
||||
|
||||
pub fn ap_accept_header() -> Vec<&'static str> {
|
||||
vec![
|
||||
@@ -84,7 +84,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for ApRequest {
|
||||
.get_one("Accept")
|
||||
.map(|header| {
|
||||
header
|
||||
.split(",")
|
||||
.split(',')
|
||||
.map(|ct| match ct.trim() {
|
||||
// bool for Forward: true if found a valid Content-Type for Plume first (HTML), false otherwise
|
||||
"application/ld+json; profile=\"https://w3.org/ns/activitystreams\""
|
||||
@@ -95,7 +95,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for ApRequest {
|
||||
_ => Outcome::Forward(false),
|
||||
})
|
||||
.fold(Outcome::Forward(false), |out, ct| {
|
||||
if out.clone().forwarded().unwrap_or(out.is_success()) {
|
||||
if out.clone().forwarded().unwrap_or_else(|| out.is_success()) {
|
||||
out
|
||||
} else {
|
||||
ct
|
||||
@@ -114,7 +114,7 @@ pub fn broadcast<S: sign::Signer, A: Activity, T: inbox::WithInbox + Actor>(
|
||||
let boxes = to
|
||||
.into_iter()
|
||||
.filter(|u| !u.is_local())
|
||||
.map(|u| u.get_shared_inbox_url().unwrap_or(u.get_inbox_url()))
|
||||
.map(|u| u.get_shared_inbox_url().unwrap_or_else(|| u.get_inbox_url()))
|
||||
.collect::<Vec<String>>()
|
||||
.unique();
|
||||
|
||||
@@ -124,13 +124,14 @@ pub fn broadcast<S: sign::Signer, A: Activity, T: inbox::WithInbox + Actor>(
|
||||
|
||||
for inbox in boxes {
|
||||
// TODO: run it in Sidekiq or something like that
|
||||
let body = signed.to_string();
|
||||
let mut headers = request::headers();
|
||||
headers.insert("Digest", request::Digest::digest(signed.to_string()));
|
||||
headers.insert("Digest", request::Digest::digest(&body));
|
||||
let res = Client::new()
|
||||
.post(&inbox[..])
|
||||
.post(&inbox)
|
||||
.headers(headers.clone())
|
||||
.header("Signature", request::signature(sender, headers))
|
||||
.body(signed.to_string())
|
||||
.header("Signature", request::signature(sender, &headers))
|
||||
.body(body)
|
||||
.send();
|
||||
match res {
|
||||
Ok(mut r) => {
|
||||
@@ -161,6 +162,12 @@ impl Into<String> for Id {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Id {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoId {
|
||||
fn into_id(self) -> Id;
|
||||
}
|
||||
|
||||
@@ -8,28 +8,28 @@ use std::time::SystemTime;
|
||||
use activity_pub::{AP_CONTENT_TYPE, ap_accept_header};
|
||||
use activity_pub::sign::Signer;
|
||||
|
||||
const PLUME_USER_AGENT: &'static str = concat!("Plume/", env!("CARGO_PKG_VERSION"));
|
||||
const PLUME_USER_AGENT: &str = concat!("Plume/", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
pub struct Digest(String);
|
||||
|
||||
impl Digest {
|
||||
pub fn digest(body: String) -> HeaderValue {
|
||||
pub fn digest(body: &str) -> HeaderValue {
|
||||
let mut hasher =
|
||||
Hasher::new(MessageDigest::sha256()).expect("Digest::digest: initialization error");
|
||||
hasher
|
||||
.update(&body.into_bytes()[..])
|
||||
.update(body.as_bytes())
|
||||
.expect("Digest::digest: content insertion error");
|
||||
let res = base64::encode(&hasher.finish().expect("Digest::digest: finalizing error"));
|
||||
HeaderValue::from_str(&format!("SHA-256={}", res))
|
||||
.expect("Digest::digest: header creation error")
|
||||
}
|
||||
|
||||
pub fn verify(&self, body: String) -> bool {
|
||||
pub fn verify(&self, body: &str) -> bool {
|
||||
if self.algorithm() == "SHA-256" {
|
||||
let mut hasher =
|
||||
Hasher::new(MessageDigest::sha256()).expect("Digest::digest: initialization error");
|
||||
hasher
|
||||
.update(&body.into_bytes())
|
||||
.update(body.as_bytes())
|
||||
.expect("Digest::digest: content insertion error");
|
||||
self.value().deref()
|
||||
== hasher
|
||||
@@ -60,7 +60,7 @@ impl Digest {
|
||||
pub fn from_header(dig: &str) -> Result<Self, ()> {
|
||||
if let Some(pos) = dig.find('=') {
|
||||
let pos = pos + 1;
|
||||
if let Ok(_) = base64::decode(&dig[pos..]) {
|
||||
if base64::decode(&dig[pos..]).is_ok() {
|
||||
Ok(Digest(dig.to_owned()))
|
||||
} else {
|
||||
Err(())
|
||||
@@ -94,7 +94,7 @@ pub fn headers() -> HeaderMap {
|
||||
headers
|
||||
}
|
||||
|
||||
pub fn signature<S: Signer>(signer: &S, headers: HeaderMap) -> HeaderValue {
|
||||
pub fn signature<S: Signer>(signer: &S, headers: &HeaderMap) -> HeaderValue {
|
||||
let signed_string = headers
|
||||
.iter()
|
||||
.map(|(h, v)| {
|
||||
@@ -114,8 +114,8 @@ pub fn signature<S: Signer>(signer: &S, headers: HeaderMap) -> HeaderValue {
|
||||
.join(" ")
|
||||
.to_lowercase();
|
||||
|
||||
let data = signer.sign(signed_string);
|
||||
let sign = base64::encode(&data[..]);
|
||||
let data = signer.sign(&signed_string);
|
||||
let sign = base64::encode(&data);
|
||||
|
||||
HeaderValue::from_str(&format!(
|
||||
"keyId=\"{key_id}\",algorithm=\"rsa-sha256\",headers=\"{signed_headers}\",signature=\"{signature}\"",
|
||||
|
||||
@@ -24,9 +24,9 @@ pub trait Signer {
|
||||
fn get_key_id(&self) -> String;
|
||||
|
||||
/// Sign some data with the signer keypair
|
||||
fn sign(&self, to_sign: String) -> Vec<u8>;
|
||||
fn sign(&self, to_sign: &str) -> Vec<u8>;
|
||||
/// Verify if the signature is valid
|
||||
fn verify(&self, data: String, signature: Vec<u8>) -> bool;
|
||||
fn verify(&self, data: &str, signature: &[u8]) -> bool;
|
||||
}
|
||||
|
||||
pub trait Signable {
|
||||
@@ -37,9 +37,9 @@ pub trait Signable {
|
||||
where
|
||||
T: Signer;
|
||||
|
||||
fn hash(data: String) -> String {
|
||||
let bytes = data.into_bytes();
|
||||
hex::encode(sha256(&bytes[..]))
|
||||
fn hash(data: &str) -> String {
|
||||
let bytes = data.as_bytes();
|
||||
hex::encode(sha256(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,15 +53,15 @@ impl Signable for serde_json::Value {
|
||||
});
|
||||
|
||||
let options_hash = Self::hash(
|
||||
json!({
|
||||
&json!({
|
||||
"@context": "https://w3id.org/identity/v1",
|
||||
"created": creation_date
|
||||
}).to_string(),
|
||||
);
|
||||
let document_hash = Self::hash(self.to_string());
|
||||
let document_hash = Self::hash(&self.to_string());
|
||||
let to_be_signed = options_hash + &document_hash;
|
||||
|
||||
let signature = base64::encode(&creator.sign(to_be_signed));
|
||||
let signature = base64::encode(&creator.sign(&to_be_signed));
|
||||
|
||||
options["signatureValue"] = serde_json::Value::String(signature);
|
||||
self["signature"] = options;
|
||||
@@ -85,14 +85,14 @@ impl Signable for serde_json::Value {
|
||||
};
|
||||
let creation_date = &signature_obj["created"];
|
||||
let options_hash = Self::hash(
|
||||
json!({
|
||||
&json!({
|
||||
"@context": "https://w3id.org/identity/v1",
|
||||
"created": creation_date
|
||||
}).to_string(),
|
||||
);
|
||||
let document_hash = Self::hash(self.to_string());
|
||||
let document_hash = Self::hash(&self.to_string());
|
||||
let to_be_signed = options_hash + &document_hash;
|
||||
creator.verify(to_be_signed, signature)
|
||||
creator.verify(&to_be_signed, &signature)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,15 +105,15 @@ pub enum SignatureValidity {
|
||||
}
|
||||
|
||||
impl SignatureValidity {
|
||||
pub fn is_secure(&self) -> bool {
|
||||
self == &SignatureValidity::Valid
|
||||
pub fn is_secure(self) -> bool {
|
||||
self == SignatureValidity::Valid
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_http_headers<S: Signer + ::std::fmt::Debug>(
|
||||
sender: &S,
|
||||
all_headers: HeaderMap,
|
||||
data: String,
|
||||
all_headers: &HeaderMap,
|
||||
data: &str,
|
||||
) -> SignatureValidity {
|
||||
let sig_header = all_headers.get_one("Signature");
|
||||
if sig_header.is_none() {
|
||||
@@ -151,7 +151,7 @@ pub fn verify_http_headers<S: Signer + ::std::fmt::Debug>(
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
if !sender.verify(h, base64::decode(signature).unwrap_or(Vec::new())) {
|
||||
if !sender.verify(&h, &base64::decode(signature).unwrap_or_default()) {
|
||||
return SignatureValidity::Invalid;
|
||||
}
|
||||
if !headers.contains(&"digest") {
|
||||
@@ -160,7 +160,7 @@ pub fn verify_http_headers<S: Signer + ::std::fmt::Debug>(
|
||||
}
|
||||
let digest = all_headers.get_one("digest").unwrap_or("");
|
||||
let digest = request::Digest::from_header(digest);
|
||||
if !digest.map(|d| d.verify(data)).unwrap_or(false) {
|
||||
if !digest.map(|d| d.verify(&data)).unwrap_or(false) {
|
||||
// signature was valid, but body content does not match its digest
|
||||
SignatureValidity::Invalid
|
||||
} else {
|
||||
|
||||
+41
-42
@@ -16,17 +16,15 @@ pub fn random_hex() -> String {
|
||||
}
|
||||
|
||||
/// Remove non alphanumeric characters and CamelCase a string
|
||||
pub fn make_actor_id(name: String) -> String {
|
||||
name.as_str()
|
||||
.to_camel_case()
|
||||
.to_string()
|
||||
pub fn make_actor_id(name: &str) -> String {
|
||||
name.to_camel_case()
|
||||
.chars()
|
||||
.filter(|c| c.is_alphanumeric())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn requires_login(message: &str, url: Uri) -> Flash<Redirect> {
|
||||
Flash::new(Redirect::to(format!("/login?m={}", gettext(message.to_string()))), "callback", url.to_string())
|
||||
pub fn requires_login<T: Into<Uri<'static>>>(message: &str, url: T) -> Flash<Redirect> {
|
||||
Flash::new(Redirect::to(format!("/login?m={}", gettext(message.to_string()))), "callback", url.into().to_string())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -41,27 +39,26 @@ enum State {
|
||||
pub fn md_to_html(md: &str) -> (String, HashSet<String>, HashSet<String>) {
|
||||
let parser = Parser::new_ext(md, Options::all());
|
||||
|
||||
let (parser, mentions, hashtags): (Vec<Vec<Event>>, Vec<Vec<String>>, Vec<Vec<String>>) = parser.map(|evt| match evt {
|
||||
let (parser, mentions, hashtags): (Vec<Event>, Vec<String>, Vec<String>) = parser.map(|evt| match evt {
|
||||
Event::Text(txt) => {
|
||||
let (evts, _, _, _, new_mentions, new_hashtags) = txt.chars().fold((vec![], State::Ready, String::new(), 0, vec![], vec![]), |(mut events, state, text_acc, n, mut mentions, mut hashtags), c| {
|
||||
let (evts, _, _, _, new_mentions, new_hashtags) = txt.chars().fold((vec![], State::Ready, String::new(), 0, vec![], vec![]), |(mut events, state, mut text_acc, n, mut mentions, mut hashtags), c| {
|
||||
match state {
|
||||
State::Mention => {
|
||||
let char_matches = c.is_alphanumeric() || c == '@' || c == '.' || c == '-' || c == '_';
|
||||
if char_matches && (n < (txt.chars().count() - 1)) {
|
||||
(events, State::Mention, text_acc + c.to_string().as_ref(), n + 1, mentions, hashtags)
|
||||
text_acc.push(c);
|
||||
(events, State::Mention, text_acc, n + 1, mentions, hashtags)
|
||||
} else {
|
||||
let mention = if char_matches {
|
||||
text_acc + c.to_string().as_ref()
|
||||
} else {
|
||||
text_acc
|
||||
};
|
||||
let short_mention = mention.clone();
|
||||
let short_mention = short_mention.splitn(1, '@').nth(0).unwrap_or("");
|
||||
let link = Tag::Link(format!("/@/{}/", mention).into(), short_mention.to_string().into());
|
||||
if char_matches {
|
||||
text_acc.push(c)
|
||||
}
|
||||
let mention = text_acc;
|
||||
let short_mention = mention.splitn(1, '@').nth(0).unwrap_or("");
|
||||
let link = Tag::Link(format!("/@/{}/", &mention).into(), short_mention.to_owned().into());
|
||||
|
||||
mentions.push(mention);
|
||||
mentions.push(mention.clone());
|
||||
events.push(Event::Start(link.clone()));
|
||||
events.push(Event::Text(format!("@{}", short_mention).into()));
|
||||
events.push(Event::Text(format!("@{}", &short_mention).into()));
|
||||
events.push(Event::End(link));
|
||||
|
||||
(events, State::Ready, c.to_string(), n + 1, mentions, hashtags)
|
||||
@@ -70,24 +67,25 @@ pub fn md_to_html(md: &str) -> (String, HashSet<String>, HashSet<String>) {
|
||||
State::Hashtag => {
|
||||
let char_matches = c.is_alphanumeric();
|
||||
if char_matches && (n < (txt.chars().count() -1)) {
|
||||
(events, State::Hashtag, text_acc + c.to_string().as_ref(), n+1, mentions, hashtags)
|
||||
text_acc.push(c);
|
||||
(events, State::Hashtag, text_acc, n+1, mentions, hashtags)
|
||||
} else {
|
||||
let hashtag = if char_matches {
|
||||
text_acc + c.to_string().as_ref()
|
||||
} else {
|
||||
text_acc
|
||||
};
|
||||
let link = Tag::Link(format!("/tag/{}", hashtag.to_camel_case()).into(), hashtag.to_string().into());
|
||||
if char_matches {
|
||||
text_acc.push(c);
|
||||
}
|
||||
let hashtag = text_acc;
|
||||
let link = Tag::Link(format!("/tag/{}", &hashtag.to_camel_case()).into(), hashtag.to_owned().into());
|
||||
|
||||
hashtags.push(hashtag.clone());
|
||||
events.push(Event::Start(link.clone()));
|
||||
events.push(Event::Text(format!("#{}", hashtag).into()));
|
||||
events.push(Event::Text(format!("#{}", &hashtag).into()));
|
||||
events.push(Event::End(link));
|
||||
|
||||
(events, State::Ready, c.to_string(), n + 1, mentions, hashtags)
|
||||
}
|
||||
}
|
||||
State::Ready => {
|
||||
text_acc.push(c);
|
||||
if c == '@' {
|
||||
events.push(Event::Text(text_acc.into()));
|
||||
(events, State::Mention, String::new(), n + 1, mentions, hashtags)
|
||||
@@ -96,27 +94,28 @@ pub fn md_to_html(md: &str) -> (String, HashSet<String>, HashSet<String>) {
|
||||
(events, State::Hashtag, String::new(), n + 1, mentions, hashtags)
|
||||
} else if c.is_alphanumeric() {
|
||||
if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
|
||||
events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into()))
|
||||
events.push(Event::Text(text_acc.clone().into()))
|
||||
}
|
||||
(events, State::Word, text_acc + c.to_string().as_ref(), n + 1, mentions, hashtags)
|
||||
(events, State::Word, text_acc, n + 1, mentions, hashtags)
|
||||
} else {
|
||||
if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
|
||||
events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into()))
|
||||
events.push(Event::Text(text_acc.clone().into()))
|
||||
}
|
||||
(events, State::Ready, text_acc + c.to_string().as_ref(), n + 1, mentions, hashtags)
|
||||
(events, State::Ready, text_acc, n + 1, mentions, hashtags)
|
||||
}
|
||||
}
|
||||
State::Word => {
|
||||
text_acc.push(c);
|
||||
if c.is_alphanumeric() {
|
||||
if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
|
||||
events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into()))
|
||||
events.push(Event::Text(text_acc.clone().into()))
|
||||
}
|
||||
(events, State::Word, text_acc + c.to_string().as_ref(), n + 1, mentions, hashtags)
|
||||
(events, State::Word, text_acc, n + 1, mentions, hashtags)
|
||||
} else {
|
||||
if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
|
||||
events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into()))
|
||||
events.push(Event::Text(text_acc.clone().into()))
|
||||
}
|
||||
(events, State::Ready, text_acc + c.to_string().as_ref(), n + 1, mentions, hashtags)
|
||||
(events, State::Ready, text_acc, n + 1, mentions, hashtags)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,15 +123,15 @@ pub fn md_to_html(md: &str) -> (String, HashSet<String>, HashSet<String>) {
|
||||
(evts, new_mentions, new_hashtags)
|
||||
},
|
||||
_ => (vec![evt], vec![], vec![])
|
||||
}).fold((vec![],vec![],vec![]), |(mut parser, mut mention, mut hashtag), (p, m, h)| {
|
||||
parser.push(p);
|
||||
mention.push(m);
|
||||
hashtag.push(h);
|
||||
}).fold((vec![],vec![],vec![]), |(mut parser, mut mention, mut hashtag), (mut p, mut m, mut h)| {
|
||||
parser.append(&mut p);
|
||||
mention.append(&mut m);
|
||||
hashtag.append(&mut h);
|
||||
(parser, mention, hashtag)
|
||||
});
|
||||
let parser = parser.into_iter().flatten();
|
||||
let mentions = mentions.into_iter().flatten().map(|m| String::from(m.trim()));
|
||||
let hashtags = hashtags.into_iter().flatten().map(|h| String::from(h.trim()));
|
||||
let parser = parser.into_iter();
|
||||
let mentions = mentions.into_iter().map(|m| String::from(m.trim()));
|
||||
let hashtags = hashtags.into_iter().map(|h| String::from(h.trim()));
|
||||
|
||||
// TODO: fetch mentionned profiles in background, if needed
|
||||
|
||||
|
||||
Reference in New Issue
Block a user