diff --git a/Cargo.lock b/Cargo.lock index e50f734..fb8e99c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -560,6 +560,7 @@ dependencies = [ "pdf", "percent-encoding", "rand", + "regex", "serde", "serde_json", "sha1", @@ -1953,9 +1954,9 @@ checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "regex" -version = "1.3.7" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" dependencies = [ "aho-corasick", "memchr", @@ -1965,9 +1966,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.17" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" [[package]] name = "remove_dir_all" diff --git a/Cargo.toml b/Cargo.toml index 37af7c0..1da4788 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,4 +26,5 @@ image = "0.23.5" kamadak-exif = "0.5.1" lazy_static = "1.4.0" mime_guess = "2.0.3" -pdf = "0.6.3" \ No newline at end of file +pdf = "0.6.3" +regex = "1.4.2" \ No newline at end of file diff --git a/src/api_data/mod.rs b/src/api_data/mod.rs index ed1e4f7..3fbba4a 100644 --- a/src/api_data/mod.rs +++ b/src/api_data/mod.rs @@ -54,4 +54,5 @@ pub mod entities_constructor; pub mod general_settings_api; pub mod language_settings_api; pub mod security_settings_api; -pub mod account_image_settings_api; \ No newline at end of file +pub mod account_image_settings_api; +pub mod res_create_custom_emoji; \ No newline at end of file diff --git a/src/api_data/res_create_custom_emoji.rs b/src/api_data/res_create_custom_emoji.rs new file mode 100644 index 0000000..16269ce --- /dev/null +++ b/src/api_data/res_create_custom_emoji.rs @@ -0,0 +1,17 @@ +//! # Create custom emoji result +//! +//! @author Pierre Hubert + +use serde::Serialize; + +#[allow(non_snake_case)] +#[derive(Serialize)] +pub struct ResCreateCustomEmoji { + emojiID: u64 +} + +impl ResCreateCustomEmoji { + pub fn new(emoji_id: u64) -> ResCreateCustomEmoji { + ResCreateCustomEmoji { emojiID: emoji_id } + } +} \ No newline at end of file diff --git a/src/controllers/routes.rs b/src/controllers/routes.rs index 108a178..943dc79 100644 --- a/src/controllers/routes.rs +++ b/src/controllers/routes.rs @@ -106,6 +106,7 @@ pub fn get_routes() -> Vec { Route::post("/settings/upload_account_image", Box::new(settings_controller::upload_account_image)), Route::post("/settings/delete_account_image", Box::new(settings_controller::delete_account_image)), Route::post("/settings/set_account_image_visibility", Box::new(settings_controller::set_account_image_visibility)), + Route::post("/settings/upload_custom_emoji", Box::new(settings_controller::upload_custom_emoji)), // Friends controller diff --git a/src/controllers/settings_controller.rs b/src/controllers/settings_controller.rs index 5c476ac..daae350 100644 --- a/src/controllers/settings_controller.rs +++ b/src/controllers/settings_controller.rs @@ -5,15 +5,17 @@ use crate::api_data::account_image_settings_api::AccountImageSettingsAPI; use crate::api_data::general_settings_api::GeneralSettingsAPI; use crate::api_data::language_settings_api::LanguageSettingsAPI; +use crate::api_data::res_create_custom_emoji::ResCreateCustomEmoji; use crate::api_data::security_settings_api::SecuritySettingsAPI; use crate::constants::SUPPORTED_LANGUAGES; use crate::controllers::routes::RequestResult; use crate::data::general_settings::GeneralSettings; use crate::data::http_request_handler::HttpRequestHandler; use crate::data::lang_settings::LangSettings; +use crate::data::new_custom_emoji::NewCustomEmoji; use crate::data::security_settings::{SecurityQuestion, SecuritySettings}; use crate::data::user::{AccountImageVisibility, UserPageStatus}; -use crate::helpers::{account_helper, user_helper}; +use crate::helpers::{account_helper, custom_emojies_helper, user_helper}; use crate::helpers::virtual_directory_helper::VirtualDirType; /// Get the general settings of the user @@ -171,4 +173,23 @@ pub fn set_account_image_visibility(r: &mut HttpRequestHandler) -> RequestResult account_helper::set_account_image_visibility(r.user_id_ref()?, level)?; r.success("Account image visibility level updated!") +} + +/// Upload a custom emoji +pub fn upload_custom_emoji(r: &mut HttpRequestHandler) -> RequestResult { + let shortcut = r.post_emoji_shortcut("shortcut")?; + + if custom_emojies_helper::has_user_similar_shortcut(r.user_id_ref()?, &shortcut)? { + r.forbidden("A custom emoji with the same shortcut is already defined!".to_string())?; + } + + let path = r.save_post_image("image", "custom_emojies", 72, 72)?; + + let emoji_id = custom_emojies_helper::insert(&NewCustomEmoji { + user_id: r.user_id()?, + shortcut, + path, + })?; + + r.set_response(ResCreateCustomEmoji::new(emoji_id)) } \ No newline at end of file diff --git a/src/data/http_request_handler.rs b/src/data/http_request_handler.rs index 5329fc3..6fe8d11 100644 --- a/src/data/http_request_handler.rs +++ b/src/data/http_request_handler.rs @@ -23,7 +23,7 @@ use crate::data::user::UserID; use crate::helpers::{account_helper, api_helper, comments_helper, conversations_helper, friends_helper, groups_helper, movies_helper, posts_helper, user_helper, virtual_directory_helper}; use crate::helpers::virtual_directory_helper::VirtualDirType; use crate::utils::pdf_utils::is_valid_pdf; -use crate::utils::string_utils::{check_string_before_insert, check_url, remove_html_nodes}; +use crate::utils::string_utils::{check_emoji_code, check_string_before_insert, check_url, remove_html_nodes}; use crate::utils::user_data_utils::{generate_new_user_data_file_name, prepare_file_creation, user_data_path}; use crate::utils::virtual_directories_utils::check_virtual_directory; @@ -671,4 +671,15 @@ impl HttpRequestHandler { Ok(()) } + + /// Get an emoji shortcut included in a POST request + pub fn post_emoji_shortcut(&mut self, field: &str) -> ResultBoxError { + let emoji_shortcut = self.post_string(field)?; + + if !check_emoji_code(&emoji_shortcut) { + self.bad_request("Invalid emoji shortcut code!".to_string())?; + } + + Ok(emoji_shortcut) + } } \ No newline at end of file diff --git a/src/data/mod.rs b/src/data/mod.rs index d9d0862..3e76403 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -32,4 +32,5 @@ pub mod user_like; pub mod survey_response; pub mod general_settings; pub mod lang_settings; -pub mod security_settings; \ No newline at end of file +pub mod security_settings; +pub mod new_custom_emoji; \ No newline at end of file diff --git a/src/data/new_custom_emoji.rs b/src/data/new_custom_emoji.rs new file mode 100644 index 0000000..d1e0203 --- /dev/null +++ b/src/data/new_custom_emoji.rs @@ -0,0 +1,11 @@ +//! # New custom emoji +//! +//! @author Pierre Hubert + +use crate::data::user::UserID; + +pub struct NewCustomEmoji { + pub user_id: UserID, + pub shortcut: String, + pub path: String, +} \ No newline at end of file diff --git a/src/helpers/custom_emojies_helper.rs b/src/helpers/custom_emojies_helper.rs index ca6d777..a896512 100644 --- a/src/helpers/custom_emojies_helper.rs +++ b/src/helpers/custom_emojies_helper.rs @@ -2,11 +2,12 @@ //! //! @author Pierre Hubert -use crate::data::user::UserID; -use crate::data::error::ResultBoxError; -use crate::data::custom_emoji::CustomEmoji; -use crate::helpers::database; use crate::constants::database_tables_names::EMOJIS_TABLE; +use crate::data::custom_emoji::CustomEmoji; +use crate::data::error::ResultBoxError; +use crate::data::new_custom_emoji::NewCustomEmoji; +use crate::data::user::UserID; +use crate::helpers::database; /// Get the list of emojies of a user pub fn get_list_user(user_id: &UserID) -> ResultBoxError> { @@ -15,12 +16,30 @@ pub fn get_list_user(user_id: &UserID) -> ResultBoxError> { .exec(db_to_custom_emoji) } +/// Check if a given user already as an emoji shortcut or not +pub fn has_user_similar_shortcut(user_id: &UserID, shortcut: &str) -> ResultBoxError { + database::QueryInfo::new(EMOJIS_TABLE) + .cond_user_id("user_id", user_id) + .cond("shortcut", shortcut) + .exec_count() + .map(|r| r > 0) +} + +/// Insert a new emoji in the database +pub fn insert(emoji: &NewCustomEmoji) -> ResultBoxError { + database::InsertQuery::new(EMOJIS_TABLE) + .add_user_id("user_id", &emoji.user_id) + .add_str("shortcut", &emoji.shortcut) + .add_str("path", &emoji.path) + .insert_expect_result() +} + /// Turn a database entry into a [CustomEmoji] fn db_to_custom_emoji(row: &database::RowResult) -> ResultBoxError { Ok(CustomEmoji { id: row.get_u64("id")?, user_id: row.get_user_id("user_id")?, shortcut: row.get_str("shortcut")?, - path: row.get_str("path")? + path: row.get_str("path")?, }) } \ No newline at end of file diff --git a/src/helpers/database.rs b/src/helpers/database.rs index 6a9787f..b6ccb4a 100644 --- a/src/helpers/database.rs +++ b/src/helpers/database.rs @@ -138,7 +138,7 @@ impl QueryInfo { } pub fn add_conditions(mut self, map: &HashMap) -> QueryInfo { - for (k,v) in map { + for (k, v) in map { self.conditions.insert(k.to_string(), v.clone()); } self @@ -617,6 +617,12 @@ impl InsertQuery { insert(self)?; Ok(()) } + + /// Process insert, excepting an ID in the response + pub fn insert_expect_result(self) -> ResultBoxError { + let res = insert(self)?; + res.ok_or(ExecError::boxed_new("Expected an ID in insert result!")) + } } /// Insert a new entry into the database diff --git a/src/utils/string_utils.rs b/src/utils/string_utils.rs index 5f216cd..125f737 100644 --- a/src/utils/string_utils.rs +++ b/src/utils/string_utils.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use actix_web::http::Uri; +use regex::Regex; /// Escape an HTML string /// @@ -71,4 +72,24 @@ pub fn check_youtube_id(id: &str) -> bool { && !id.contains(".") && !id.contains("'") && !id.contains("\"") +} + + +/// Check the validity of an emoji shortcut +/// +/// ``` +/// use comunic_server::utils::string_utils::check_emoji_code; +/// +/// assert_eq!(check_emoji_code(":comunic:"), true); +/// assert_eq!(check_emoji_code(":comunic"), false); +/// assert_eq!(check_emoji_code("::"), false); +/// assert_eq!(check_emoji_code("a:comunic:"), false); +/// assert_eq!(check_emoji_code(":comunic:a"), false); +/// assert_eq!(check_emoji_code(":co:munic:"), false); +/// assert_eq!(check_emoji_code(":comuni@c:"), false); +/// assert_eq!(check_emoji_code("bbb:comuni@c:123"), false); +/// ``` +pub fn check_emoji_code(shortcut: &str) -> bool { + let r = Regex::new(r"^:[a-zA-Z0-9]+:$").unwrap(); + r.is_match(shortcut) } \ No newline at end of file