edition = "2018"
[dependencies]
-diesel = { version = "1.4.2", features = ["postgres","chrono", "r2d2", "64-column-tables"] }
+diesel = { version = "1.4.4", features = ["postgres","chrono","r2d2","64-column-tables","serde_json"] }
diesel_migrations = "1.4.0"
dotenv = "0.15.0"
-bcrypt = "0.7.0"
-activitypub = "0.2.0"
+activitystreams = "0.6.2"
+activitystreams-new = { git = "https://git.asonix.dog/asonix/activitystreams-sketch" }
+activitystreams-ext = { git = "https://git.asonix.dog/asonix/activitystreams-ext" }
+bcrypt = "0.8.0"
chrono = { version = "0.4.7", features = ["serde"] }
- failure = "0.1.8"
+serde_json = { version = "1.0.48", features = ["preserve_order"]}
+ failure = "0.1.8"
-serde_json = { version = "1.0.52", features = ["preserve_order"]}
serde = { version = "1.0.105", features = ["derive"] }
actix = "0.9.0"
actix-web = "2.0.0"
-use super::*;
-use crate::is_valid_username;
+use crate::{
+ api::{APIError, Oper, Perform},
+ apub::{
+ extensions::signatures::generate_actor_keypair,
+ make_apub_endpoint,
+ ApubObjectType,
+ EndpointType,
+ },
+ db::{
+ comment::*,
+ comment_view::*,
+ community::*,
+ community_view::*,
+ moderator::*,
+ password_reset_request::*,
+ post::*,
+ post_view::*,
+ private_message::*,
+ private_message_view::*,
+ site::*,
+ site_view::*,
+ user::*,
+ user_mention::*,
+ user_mention_view::*,
+ user_view::*,
+ Crud,
+ Followable,
+ Joinable,
+ ListingType,
+ SortType,
+ },
+ generate_random_string,
++ is_valid_username,
+ naive_from_unix,
+ naive_now,
+ remove_slurs,
+ send_email,
+ settings::Settings,
+ slur_check,
+ slurs_vec_to_str,
+ websocket::{
+ server::{JoinUserRoom, SendAllMessage, SendUserRoomMessage},
+ UserOperation,
+ WebsocketInfo,
+ },
+};
use bcrypt::verify;
+use diesel::{
+ r2d2::{ConnectionManager, Pool},
+ PgConnection,
+};
+use failure::Error;
+use log::error;
+use serde::{Deserialize, Serialize};
+use std::str::FromStr;
#[derive(Serialize, Deserialize, Debug)]
pub struct Login {
return Err(APIError::err("admin_already_created").into());
}
+ let user_keypair = generate_actor_keypair()?;
+ if !is_valid_username(&data.username) {
+ return Err(APIError::err("invalid_username").into());
+ }
// Register the new user
let user_form = UserForm {
.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)
+ }
+
#[cfg(test)]
mod tests {
use crate::{
- extract_usernames, is_email_regex, is_image_content_type, is_valid_username, remove_slurs,
- slur_check, slurs_vec_to_str,
+ is_email_regex,
++ is_image_content_type,
++ 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());
+ }
+
+ #[test]
+ fn test_image() {
+ assert!(is_image_content_type("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").is_ok());
+ assert!(is_image_content_type(
+ "https://twitter.com/BenjaminNorton/status/1259922424272957440?s=20"
+ )
+ .is_err());
+ }
+
#[test]
fn test_email() {
assert!(is_email_regex("gush@gmail.com"));
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)?|nig(\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)?|\btrann?(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();
}
extern crate lemmy_server;
#[macro_use]
extern crate diesel_migrations;
+ #[macro_use]
+ pub extern crate lazy_static;
+ use crate::lemmy_server::actix_web::dev::Service;
use actix::prelude::*;
-use actix_web::body::Body;
-use actix_web::dev::{ServiceRequest, ServiceResponse};
-use actix_web::http::header::CONTENT_TYPE;
-use actix_web::http::{header::CACHE_CONTROL, HeaderValue};
--use actix_web::*;
-use diesel::r2d2::{ConnectionManager, Pool};
-use diesel::PgConnection;
++use actix_web::{
++ body::Body,
++ dev::{ServiceRequest, ServiceResponse},
++ http::{
++ header::{CACHE_CONTROL, CONTENT_TYPE},
++ HeaderValue,
++ },
++ *,
++};
+use diesel::{
+ r2d2::{ConnectionManager, Pool},
+ PgConnection,
+};
- use failure::Error;
use lemmy_server::{
+ db::code_migrations::run_advanced_migrations,
rate_limit::{rate_limiter::RateLimiter, RateLimit},
routes::{api, federation, feeds, index, nodeinfo, webfinger},
settings::Settings,
value={this.state.postForm.community_id}
onInput={linkEvent(this, this.handlePostCommunityChange)}
>
+ <option>{i18n.t('select_a_community')}</option>
{this.state.communities.map(community => (
- <option value={community.id}>{community.name}</option>
+ <option value={community.id}>
+ {community.local
+ ? community.name
+ : `${hostname(community.actor_id)}/${community.name}`}
+ </option>
))}
</select>
</div>
.join('\n');
}
+export function hostname(url: string): string {
+ let cUrl = new URL(url);
+ return window.location.port
+ ? `${cUrl.hostname}:${cUrl.port}`
+ : `${cUrl.hostname}`;
+}
++
+ function canUseWebP() {
+ // TODO pictshare might have a webp conversion bug, try disabling this
+ return false;
+
+ // var elem = document.createElement('canvas');
+
+ // if (!!(elem.getContext && elem.getContext('2d'))) {
+ // var testString = !(window.mozInnerScreenX == null) ? 'png' : 'webp';
+ // // was able or not to get WebP representation
+ // return (
+ // elem.toDataURL('image/webp').startsWith('data:image/' + testString)
+ // );
+ // }
+
+ // // very old browser like IE 8, canvas not supported
+ // return false;
+ }