summaryrefslogtreecommitdiffstats
path: root/server/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/lib.rs')
-rw-r--r--server/src/lib.rs333
1 files changed, 44 insertions, 289 deletions
diff --git a/server/src/lib.rs b/server/src/lib.rs
index 04f376fb..4795cf01 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -5,76 +5,34 @@ pub extern crate strum_macros;
pub extern crate lazy_static;
#[macro_use]
pub extern crate failure;
-#[macro_use]
-pub extern crate diesel;
pub extern crate actix;
pub extern crate actix_web;
pub extern crate bcrypt;
pub extern crate chrono;
-pub extern crate comrak;
+pub extern crate diesel;
pub extern crate dotenv;
pub extern crate jsonwebtoken;
-pub extern crate lettre;
-pub extern crate lettre_email;
extern crate log;
pub extern crate openssl;
-pub extern crate rand;
-pub extern crate regex;
pub extern crate rss;
pub extern crate serde;
pub extern crate serde_json;
pub extern crate sha2;
pub extern crate strum;
-pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
-where
- F: FnOnce(&diesel::PgConnection) -> T + Send + 'static,
- T: Send + 'static,
-{
- let pool = pool.clone();
- let res = actix_web::web::block(move || {
- let conn = pool.get()?;
- let res = (f)(&conn);
- Ok(res) as Result<_, LemmyError>
- })
- .await?;
-
- Ok(res)
-}
-
pub mod api;
pub mod apub;
-pub mod db;
+pub mod code_migrations;
pub mod rate_limit;
pub mod request;
pub mod routes;
-pub mod schema;
-pub mod settings;
pub mod version;
pub mod websocket;
-use crate::{
- request::{retry, RecvError},
- settings::Settings,
-};
+use crate::request::{retry, RecvError};
use actix_web::{client::Client, dev::ConnectionInfo};
-use chrono::{DateTime, FixedOffset, Local, NaiveDateTime, Utc};
-use itertools::Itertools;
-use lettre::{
- smtp::{
- authentication::{Credentials, Mechanism},
- extension::ClientId,
- ConnectionReuseParameters,
- },
- ClientSecurity,
- SmtpClient,
- Transport,
-};
-use lettre_email::Email;
use log::error;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
-use rand::{distributions::Alphanumeric, thread_rng, Rng};
-use regex::{Regex, RegexBuilder};
use serde::Deserialize;
pub type DbPool = diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
@@ -89,14 +47,6 @@ pub struct LemmyError {
inner: failure::Error,
}
-impl std::fmt::Display for LemmyError {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- self.inner.fmt(f)
- }
-}
-
-impl actix_web::error::ResponseError for LemmyError {}
-
impl<T> From<T> for LemmyError
where
T: Into<failure::Error>,
@@ -106,113 +56,13 @@ where
}
}
-pub fn to_datetime_utc(ndt: NaiveDateTime) -> DateTime<Utc> {
- DateTime::<Utc>::from_utc(ndt, Utc)
-}
-
-pub fn naive_now() -> NaiveDateTime {
- chrono::prelude::Utc::now().naive_utc()
-}
-
-pub fn naive_from_unix(time: i64) -> NaiveDateTime {
- NaiveDateTime::from_timestamp(time, 0)
-}
-
-pub fn convert_datetime(datetime: NaiveDateTime) -> DateTime<FixedOffset> {
- let now = Local::now();
- DateTime::<FixedOffset>::from_utc(datetime, *now.offset())
-}
-
-pub fn is_email_regex(test: &str) -> bool {
- EMAIL_REGEX.is_match(test)
-}
-
-pub async fn is_image_content_type(client: &Client, test: &str) -> Result<(), LemmyError> {
- let response = retry(|| client.get(test).send()).await?;
-
- if response
- .headers()
- .get("Content-Type")
- .ok_or_else(|| format_err!("No Content-Type header"))?
- .to_str()?
- .starts_with("image/")
- {
- Ok(())
- } else {
- Err(format_err!("Not an image type.").into())
- }
-}
-
-pub fn remove_slurs(test: &str) -> String {
- SLUR_REGEX.replace_all(test, "*removed*").to_string()
-}
-
-pub fn slur_check(test: &str) -> Result<(), Vec<&str>> {
- let mut matches: Vec<&str> = SLUR_REGEX.find_iter(test).map(|mat| mat.as_str()).collect();
-
- // Unique
- matches.sort_unstable();
- matches.dedup();
-
- if matches.is_empty() {
- Ok(())
- } else {
- Err(matches)
+impl std::fmt::Display for LemmyError {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ self.inner.fmt(f)
}
}
-pub fn slurs_vec_to_str(slurs: Vec<&str>) -> String {
- let start = "No slurs - ";
- let combined = &slurs.join(", ");
- [start, combined].concat()
-}
-
-pub fn generate_random_string() -> String {
- thread_rng().sample_iter(&Alphanumeric).take(30).collect()
-}
-
-pub fn send_email(
- subject: &str,
- to_email: &str,
- to_username: &str,
- html: &str,
-) -> Result<(), String> {
- let email_config = Settings::get().email.ok_or("no_email_setup")?;
-
- let email = Email::builder()
- .to((to_email, to_username))
- .from(email_config.smtp_from_address.to_owned())
- .subject(subject)
- .html(html)
- .build()
- .unwrap();
-
- let mailer = if email_config.use_tls {
- SmtpClient::new_simple(&email_config.smtp_server).unwrap()
- } else {
- SmtpClient::new(&email_config.smtp_server, ClientSecurity::None).unwrap()
- }
- .hello_name(ClientId::Domain(Settings::get().hostname))
- .smtp_utf8(true)
- .authentication_mechanism(Mechanism::Plain)
- .connection_reuse(ConnectionReuseParameters::ReuseUnlimited);
- let mailer = if let (Some(login), Some(password)) =
- (&email_config.smtp_login, &email_config.smtp_password)
- {
- mailer.credentials(Credentials::new(login.to_owned(), password.to_owned()))
- } else {
- mailer
- };
-
- let mut transport = mailer.transport();
- let result = transport.send(email.into());
- transport.close();
-
- match result {
- Ok(_) => Ok(()),
- Err(e) => Err(e.to_string()),
- }
-}
+impl actix_web::error::ResponseError for LemmyError {}
#[derive(Deserialize, Debug)]
pub struct IframelyResponse {
@@ -319,13 +169,25 @@ async fn fetch_iframely_and_pictrs_data(
}
}
-pub fn markdown_to_html(text: &str) -> String {
- comrak::markdown_to_html(text, &comrak::ComrakOptions::default())
+pub async fn is_image_content_type(client: &Client, test: &str) -> Result<(), LemmyError> {
+ let response = retry(|| client.get(test).send()).await?;
+
+ if response
+ .headers()
+ .get("Content-Type")
+ .ok_or_else(|| format_err!("No Content-Type header"))?
+ .to_str()?
+ .starts_with("image/")
+ {
+ Ok(())
+ } else {
+ Err(format_err!("Not an image type.").into())
+ }
}
pub fn get_ip(conn_info: &ConnectionInfo) -> String {
conn_info
- .remote_addr()
+ .realip_remote_addr()
.unwrap_or("127.0.0.1:12345")
.split(':')
.next()
@@ -333,127 +195,37 @@ pub fn get_ip(conn_info: &ConnectionInfo) -> String {
.to_string()
}
-// TODO nothing is done with community / group webfingers yet, so just ignore those for now
-#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct MentionData {
- pub name: String,
- pub domain: String,
-}
-
-impl MentionData {
- pub fn is_local(&self) -> bool {
- Settings::get().hostname.eq(&self.domain)
- }
- pub fn full_name(&self) -> String {
- format!("@{}@{}", &self.name, &self.domain)
- }
-}
-
-pub fn scrape_text_for_mentions(text: &str) -> Vec<MentionData> {
- let mut out: Vec<MentionData> = Vec::new();
- for caps in WEBFINGER_USER_REGEX.captures_iter(text) {
- out.push(MentionData {
- name: caps["name"].to_string(),
- domain: caps["domain"].to_string(),
- });
- }
- out.into_iter().unique().collect()
-}
-
-pub fn is_valid_username(name: &str) -> bool {
- VALID_USERNAME_REGEX.is_match(name)
-}
+pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
+where
+ F: FnOnce(&diesel::PgConnection) -> T + Send + 'static,
+ T: Send + 'static,
+{
+ let pool = pool.clone();
+ let res = actix_web::web::block(move || {
+ let conn = pool.get()?;
+ let res = (f)(&conn);
+ Ok(res) as Result<_, LemmyError>
+ })
+ .await?;
-pub fn is_valid_community_name(name: &str) -> bool {
- VALID_COMMUNITY_NAME_REGEX.is_match(name)
+ Ok(res)
}
#[cfg(test)]
mod tests {
- use crate::{
- is_email_regex,
- is_image_content_type,
- is_valid_community_name,
- is_valid_username,
- remove_slurs,
- scrape_text_for_mentions,
- slur_check,
- slurs_vec_to_str,
- };
-
- #[test]
- fn test_mentions_regex() {
- let text = "Just read a great blog post by [@tedu@honk.teduangst.com](/u/test). And another by !test_community@fish.teduangst.com . Another [@lemmy@lemmy-alpha:8540](/u/fish)";
- let mentions = scrape_text_for_mentions(text);
-
- assert_eq!(mentions[0].name, "tedu".to_string());
- assert_eq!(mentions[0].domain, "honk.teduangst.com".to_string());
- assert_eq!(mentions[1].domain, "lemmy-alpha:8540".to_string());
- }
+ use crate::is_image_content_type;
#[test]
fn test_image() {
actix_rt::System::new("tset_image").block_on(async move {
- let client = actix_web::client::Client::default();
- assert!(is_image_content_type(&client, "https://1734811051.rsc.cdn77.org/data/images/full/365645/as-virus-kills-navajos-in-their-homes-tribal-women-provide-lifeline.jpg?w=600?w=650").await.is_ok());
- assert!(is_image_content_type(&client,
- "https://twitter.com/BenjaminNorton/status/1259922424272957440?s=20"
- )
- .await.is_err()
- );
- });
- }
-
- #[test]
- fn test_email() {
- assert!(is_email_regex("gush@gmail.com"));
- assert!(!is_email_regex("nada_neutho"));
- }
-
- #[test]
- fn test_valid_register_username() {
- assert!(is_valid_username("Hello_98"));
- assert!(is_valid_username("ten"));
- assert!(!is_valid_username("Hello-98"));
- assert!(!is_valid_username("a"));
- assert!(!is_valid_username(""));
- }
-
- #[test]
- fn test_valid_community_name() {
- assert!(is_valid_community_name("example"));
- assert!(is_valid_community_name("example_community"));
- assert!(!is_valid_community_name("Example"));
- assert!(!is_valid_community_name("Ex"));
- assert!(!is_valid_community_name(""));
- }
-
- #[test]
- fn test_slur_filter() {
- let test =
- "coons test dindu ladyboy tranny retardeds. Capitalized Niggerz. This is a bunch of other safe text.";
- let slur_free = "No slurs here";
- assert_eq!(
- remove_slurs(&test),
- "*removed* test *removed* *removed* *removed* *removed*. Capitalized *removed*. This is a bunch of other safe text."
- .to_string()
- );
-
- let has_slurs_vec = vec![
- "Niggerz",
- "coons",
- "dindu",
- "ladyboy",
- "retardeds",
- "tranny",
- ];
- let has_slurs_err_str = "No slurs - Niggerz, coons, dindu, ladyboy, retardeds, tranny";
-
- assert_eq!(slur_check(test), Err(has_slurs_vec));
- assert_eq!(slur_check(slur_free), Ok(()));
- if let Err(slur_vec) = slur_check(test) {
- assert_eq!(&slurs_vec_to_str(slur_vec), has_slurs_err_str);
- }
+ let client = actix_web::client::Client::default();
+ assert!(is_image_content_type(&client, "https://1734811051.rsc.cdn77.org/data/images/full/365645/as-virus-kills-navajos-in-their-homes-tribal-women-provide-lifeline.jpg?w=600?w=650").await.is_ok());
+ assert!(is_image_content_type(&client,
+ "https://twitter.com/BenjaminNorton/status/1259922424272957440?s=20"
+ )
+ .await.is_err()
+ );
+ });
}
// These helped with testing
@@ -470,21 +242,4 @@ mod tests {
// let res_other = fetch_pictshare("https://upload.wikimedia.org/wikipedia/en/2/27/The_Mandalorian_logo.jpgaoeu");
// assert!(res_other.is_err());
// }
-
- // #[test]
- // fn test_send_email() {
- // let result = send_email("not a subject", "test_email@gmail.com", "ur user", "<h1>HI there</h1>");
- // assert!(result.is_ok());
- // }
-}
-
-lazy_static! {
- static ref EMAIL_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap();
- static ref SLUR_REGEX: Regex = RegexBuilder::new(r"(fag(g|got|tard)?|maricos?|cock\s?sucker(s|ing)?|\bn(i|1)g(\b|g?(a|er)?(s|z)?)\b|dindu(s?)|mudslime?s?|kikes?|mongoloids?|towel\s*heads?|\bspi(c|k)s?\b|\bchinks?|niglets?|beaners?|\bnips?\b|\bcoons?\b|jungle\s*bunn(y|ies?)|jigg?aboo?s?|\bpakis?\b|rag\s*heads?|gooks?|cunts?|bitch(es|ing|y)?|puss(y|ies?)|twats?|feminazis?|whor(es?|ing)|\bslut(s|t?y)?|\btr(a|@)nn?(y|ies?)|ladyboy(s?)|\b(b|re|r)tard(ed)?s?)").case_insensitive(true).build().unwrap();
- static ref USERNAME_MATCHES_REGEX: Regex = Regex::new(r"/u/[a-zA-Z][0-9a-zA-Z_]*").unwrap();
- // TODO keep this old one, it didn't work with port well tho
- // static ref WEBFINGER_USER_REGEX: Regex = Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)").unwrap();
- static ref WEBFINGER_USER_REGEX: Regex = Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._:-]+)").unwrap();
- static ref VALID_USERNAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_]{3,20}$").unwrap();
- static ref VALID_COMMUNITY_NAME_REGEX: Regex = Regex::new(r"^[a-z0-9_]{3,20}$").unwrap();
}