1
0
mirror of https://gitlab.com/comunic/comunicapiv3 synced 2024-11-25 06:49:22 +00:00

Update dependencies

This commit is contained in:
Pierre HUBERT 2023-05-31 19:10:05 +02:00
parent c3f6c44abf
commit 3a38859544
206 changed files with 5160 additions and 2733 deletions

2296
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -8,20 +8,20 @@ edition = "2018"
[dependencies] [dependencies]
yaml-rust = "0.4.5" yaml-rust = "0.4.5"
mysql = "22.2.0" mysql = "24.0.0"
actix = "0.13.0" actix = "0.13.0"
actix-web = "4.1.0" actix-web = "4.3.1"
actix-files = "0.6.2" actix-files = "0.6.2"
actix-multipart = "0.4.0" actix-multipart = "0.6.0"
actix-web-actors = "4.1.0" actix-web-actors = "4.2.0"
actix-http = "3.0.4" actix-http = "3.3.1"
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.163", features = ["derive"] }
serde_json = "1.0.82" serde_json = "1.0.96"
futures = "0.3.21" futures = "0.3.28"
encoding_rs = "0.8.31" encoding_rs = "0.8.32"
percent-encoding = "2.1.0" percent-encoding = "2.2.0"
mailchecker = "5.0.0" mailchecker = "5.0.9"
sha1 = "0.10.1" sha1 = "0.10.5"
rand = "0.8.5" rand = "0.8.5"
chrono = "0.4.19" chrono = "0.4.19"
bytes = "1.2.1" bytes = "1.2.1"
@ -29,14 +29,14 @@ image = "0.24.3"
kamadak-exif = "0.5.4" kamadak-exif = "0.5.4"
lazy_static = "1.4.0" lazy_static = "1.4.0"
mime_guess = "2.0.4" mime_guess = "2.0.4"
pdf = "0.7.2" pdf = "0.8.1"
regex = "1.6.0" regex = "1.6.0"
dashmap = "5.3.4" dashmap = "5.3.4"
reqwest = { version = "0.11.11", features = ["json"] } reqwest = { version = "0.11.11", features = ["json"] }
webrtc-sdp = "0.3.9" webrtc-sdp = "0.3.9"
bcrypt = "0.13.0" bcrypt = "0.14.0"
mp3-metadata = "0.3.4" mp3-metadata = "0.3.4"
mp4 = "0.12.0" mp4 = "0.13.0"
zip = "0.6.2" zip = "0.6.2"
webpage = "1.4.0" webpage = "1.4.0"
gouth = "0.2.1" gouth = "0.2.1"

View File

@ -13,7 +13,7 @@ use crate::api_data::friend_api::FriendAPI;
use crate::api_data::group_api::GroupApi; use crate::api_data::group_api::GroupApi;
use crate::api_data::post_api::PostAPI; use crate::api_data::post_api::PostAPI;
use crate::api_data::survey_response_api::SurveyResponseAPI; use crate::api_data::survey_response_api::SurveyResponseAPI;
use crate::api_data::user_info::{APIUserInfo, APIAdvancedInfo}; use crate::api_data::user_info::{APIAdvancedInfo, APIUserInfo};
use crate::api_data::user_like_api::UserLikeAPI; use crate::api_data::user_like_api::UserLikeAPI;
use crate::data::account_export::AccountExport; use crate::data::account_export::AccountExport;
use crate::data::error::ResultBoxError; use crate::data::error::ResultBoxError;
@ -49,29 +49,31 @@ impl AccountExportAPI {
comments: CommentAPI::for_list(&export.comments, &curr_user_id.as_option())?, comments: CommentAPI::for_list(&export.comments, &curr_user_id.as_option())?,
likes: UserLikeAPI::for_list(&export.likes), likes: UserLikeAPI::for_list(&export.likes),
survey_responses: SurveyResponseAPI::for_list(&export.survey_responses), survey_responses: SurveyResponseAPI::for_list(&export.survey_responses),
all_conversation_messages: ConversationMessageAPI::for_list(&export.all_conversation_messages), all_conversation_messages: ConversationMessageAPI::for_list(
&export.all_conversation_messages,
),
conversations_list: ConversationAPI::for_list(&export.conversations), conversations_list: ConversationAPI::for_list(&export.conversations),
conversations_messages: export.conversation_messages conversations_messages: export
.conversation_messages
.iter() .iter()
.map(|r| (r.0.id(), ConversationMessageAPI::for_list(r.1))) .map(|r| (r.0.id(), ConversationMessageAPI::for_list(r.1)))
.collect(), .collect(),
friends_list: FriendAPI::from_list(&export.friends_list), friends_list: FriendAPI::from_list(&export.friends_list),
groups: export.groups groups: export
.groups
.iter() .iter()
.map(groups_helper::get_info) .map(groups_helper::get_info)
.collect::<Result<Vec<Group>, _>>()? .collect::<Result<Vec<Group>, _>>()?
.iter() .iter()
.map(|g| GroupApi::new(g, curr_user_id.as_option())) .map(|g| GroupApi::new(g, curr_user_id.as_option()))
.collect::<Result<Vec<GroupApi>, _>>()?, .collect::<Result<Vec<GroupApi>, _>>()?,
users_info: export.get_related_users_ids()? users_info: export
.get_related_users_ids()?
.iter() .iter()
.map(user_helper::find_user_by_id) .map(user_helper::find_user_by_id)
.collect::<Result<Vec<User>, _>>()? .collect::<Result<Vec<User>, _>>()?
.iter() .iter()
.map(|u| { .map(|u| APIUserInfo::new(&curr_user_id.as_option(), u).map(|r| (u.id.id(), r)))
APIUserInfo::new(&curr_user_id.as_option(), u)
.map(|r| (u.id.id(), r))
})
.collect::<Result<HashMap<u64, APIUserInfo>, _>>()?, .collect::<Result<HashMap<u64, APIUserInfo>, _>>()?,
}; };

View File

@ -8,21 +8,21 @@ use crate::utils::user_data_utils::user_data_url;
#[derive(Serialize)] #[derive(Serialize)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub struct AccountImageSettingsAPI pub struct AccountImageSettingsAPI {
{
has_image: bool, has_image: bool,
image_url: String, image_url: String,
visibility: String, visibility: String,
} }
impl AccountImageSettingsAPI { impl AccountImageSettingsAPI {
pub fn new(user: &User) -> AccountImageSettingsAPI pub fn new(user: &User) -> AccountImageSettingsAPI {
{ AccountImageSettingsAPI {
AccountImageSettingsAPI
{
has_image: user.has_account_image(), has_image: user.has_account_image(),
image_url: user_data_url(user.account_image_path image_url: user_data_url(
.as_ref().unwrap_or(&DEFAULT_ACCOUNT_IMAGE.to_string())), user.account_image_path
.as_ref()
.unwrap_or(&DEFAULT_ACCOUNT_IMAGE.to_string()),
),
visibility: user.account_image_visibility.to_api(), visibility: user.account_image_visibility.to_api(),
} }
} }

View File

@ -23,7 +23,14 @@ impl AdminAuthOptions {
pub fn new(admin: &Admin, keys: &[AdminKey]) -> Self { pub fn new(admin: &Admin, keys: &[AdminKey]) -> Self {
Self { Self {
reset_token: admin.reset_token.is_some(), reset_token: admin.reset_token.is_some(),
keys: keys.iter().map(|k| AuthKey { id: k.id, name: k.name.to_string(), password: k.password.is_some() }).collect(), keys: keys
.iter()
.map(|k| AuthKey {
id: k.id,
name: k.name.to_string(),
password: k.password.is_some(),
})
.collect(),
} }
} }
} }

View File

@ -8,7 +8,7 @@ use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
pub struct AdminAuthSuccess { pub struct AdminAuthSuccess {
token: String token: String,
} }
impl AdminAuthSuccess { impl AdminAuthSuccess {

View File

@ -8,13 +8,11 @@ use crate::data::admin::AdminID;
#[derive(Serialize)] #[derive(Serialize)]
pub struct AdminResCreateAccount { pub struct AdminResCreateAccount {
id: u64 id: u64,
} }
impl AdminResCreateAccount { impl AdminResCreateAccount {
pub fn new(id: AdminID) -> Self { pub fn new(id: AdminID) -> Self {
Self { Self { id: id.id() }
id: id.id()
}
} }
} }

View File

@ -10,7 +10,7 @@ pub struct AdminSearchUserResult {
first_name: String, first_name: String,
last_name: String, last_name: String,
email: String, email: String,
account_image: Option<String> account_image: Option<String>,
} }
impl AdminSearchUserResult { impl AdminSearchUserResult {

View File

@ -4,13 +4,13 @@
pub mod admin_auth_options; pub mod admin_auth_options;
pub mod admin_auth_success; pub mod admin_auth_success;
pub mod admin_create_password_reset_link_api;
pub mod admin_id_api; pub mod admin_id_api;
pub mod admin_info_api; pub mod admin_info_api;
pub mod admin_keys_api; pub mod admin_keys_api;
pub mod admin_log_api;
pub mod admin_res_create_account;
pub mod admin_res_create_reset_token; pub mod admin_res_create_reset_token;
pub mod admin_role_api; pub mod admin_role_api;
pub mod admin_res_create_account;
pub mod admin_log_api;
pub mod admin_search_user_result_api; pub mod admin_search_user_result_api;
pub mod admin_user_info_api; pub mod admin_user_info_api;
pub mod admin_create_password_reset_link_api;

View File

@ -9,8 +9,8 @@ use crate::data::config::conf;
use crate::data::error::ResultBoxError; use crate::data::error::ResultBoxError;
use crate::data::group::Group; use crate::data::group::Group;
use crate::data::user::UserID; use crate::data::user::UserID;
use crate::helpers::{conversations_helper, groups_helper, likes_helper};
use crate::helpers::likes_helper::LikeType; use crate::helpers::likes_helper::LikeType;
use crate::helpers::{conversations_helper, groups_helper, likes_helper};
#[derive(Serialize)] #[derive(Serialize)]
pub struct AdvancedGroupApi { pub struct AdvancedGroupApi {
@ -44,7 +44,11 @@ impl AdvancedGroupApi {
description: g.description.clone().unwrap_or("null".to_string()), description: g.description.clone().unwrap_or("null".to_string()),
url: g.url.clone().unwrap_or("null".to_string()), url: g.url.clone().unwrap_or("null".to_string()),
number_likes: likes_helper::count(g.id.id(), LikeType::GROUP)? as u64, number_likes: likes_helper::count(g.id.id(), LikeType::GROUP)? as u64,
is_liking: likes_helper::is_liking(&user_id.unwrap_or(UserID::invalid()), g.id.id(), LikeType::GROUP)?, is_liking: likes_helper::is_liking(
&user_id.unwrap_or(UserID::invalid()),
g.id.id(),
LikeType::GROUP,
)?,
conversations: ConversationAPI::for_list(&conversations), conversations: ConversationAPI::for_list(&conversations),
is_forez_group: conf().forez_groups.contains(&g.id), is_forez_group: conf().forez_groups.contains(&g.id),
}) })

View File

@ -49,7 +49,10 @@ impl CommentAPI {
Ok(c_api) Ok(c_api)
} }
pub fn for_list(l: &[Comment], curr_user_id: &Option<UserID>) -> ResultBoxError<Vec<CommentAPI>> { pub fn for_list(
l: &[Comment],
curr_user_id: &Option<UserID>,
) -> ResultBoxError<Vec<CommentAPI>> {
l.iter().map(|c| Self::new(c, curr_user_id)).collect() l.iter().map(|c| Self::new(c, curr_user_id)).collect()
} }
} }

View File

@ -52,13 +52,19 @@ impl ConversationAPI {
id: conv.id.id(), id: conv.id.id(),
last_activity: conv.last_activity, last_activity: conv.last_activity,
name: conv.name.clone(), name: conv.name.clone(),
members: conv.members.iter().map(ConversationMembersAPI::new).collect(), members: conv
.members
.iter()
.map(ConversationMembersAPI::new)
.collect(),
can_everyone_add_members: conv.can_everyone_add_members, can_everyone_add_members: conv.can_everyone_add_members,
color: conv.color.clone(), color: conv.color.clone(),
logo: conv.logo.as_ref().map(|s| user_data_url(s)), logo: conv.logo.as_ref().map(|s| user_data_url(s)),
group_id: conv.group_id.as_ref().map(|i| i.id()), group_id: conv.group_id.as_ref().map(|i| i.id()),
group_min_membership_level: conv.min_group_membership_level.as_ref() group_min_membership_level: conv
.min_group_membership_level
.as_ref()
.map(|f| f.to_api()), .map(|f| f.to_api()),
can_have_call: calls_helper::can_have_call(conv), can_have_call: calls_helper::can_have_call(conv),

View File

@ -41,7 +41,7 @@ impl ConversationMessageAPI {
name: file.name.clone(), name: file.name.clone(),
thumbnail: file.thumbnail.as_ref().map(|u| user_data_url(u)), thumbnail: file.thumbnail.as_ref().map(|u| user_data_url(u)),
r#type: file.r#type.clone(), r#type: file.r#type.clone(),
}) }),
}; };
let server_message = match &msg.server_message { let server_message = match &msg.server_message {
@ -57,7 +57,10 @@ impl ConversationMessageAPI {
ConversationServerMessageType::UserAddedAnotherUserToConversation(msg) => { ConversationServerMessageType::UserAddedAnotherUserToConversation(msg) => {
value.insert("type".to_string(), Value::from("user_added_another")); value.insert("type".to_string(), Value::from("user_added_another"));
value.insert("user_who_added".to_string(), Value::from(msg.user_who_added.id())); value.insert(
"user_who_added".to_string(),
Value::from(msg.user_who_added.id()),
);
value.insert("user_added".to_string(), Value::from(msg.user_added.id())); value.insert("user_added".to_string(), Value::from(msg.user_added.id()));
} }
@ -68,8 +71,14 @@ impl ConversationMessageAPI {
ConversationServerMessageType::UserRemovedFromConversation(msg) => { ConversationServerMessageType::UserRemovedFromConversation(msg) => {
value.insert("type".to_string(), Value::from("user_removed_another")); value.insert("type".to_string(), Value::from("user_removed_another"));
value.insert("user_who_removed".to_string(), Value::from(msg.user_who_removed.id())); value.insert(
value.insert("user_removed".to_string(), Value::from(msg.user_removed.id())); "user_who_removed".to_string(),
Value::from(msg.user_who_removed.id()),
);
value.insert(
"user_removed".to_string(),
Value::from(msg.user_removed.id()),
);
} }
} }
@ -90,8 +99,6 @@ impl ConversationMessageAPI {
/// Turn a list of conversation messages into API entries /// Turn a list of conversation messages into API entries
pub fn for_list(l: &Vec<ConversationMessage>) -> Vec<ConversationMessageAPI> { pub fn for_list(l: &Vec<ConversationMessage>) -> Vec<ConversationMessageAPI> {
l.iter() l.iter().map(|m| ConversationMessageAPI::new(m)).collect()
.map(|m| ConversationMessageAPI::new(m))
.collect()
} }
} }

View File

@ -6,13 +6,11 @@ use crate::data::user::UserID;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub struct CurrentUserID { pub struct CurrentUserID {
userID: u64 userID: u64,
} }
impl CurrentUserID { impl CurrentUserID {
pub fn new(id: &UserID) -> CurrentUserID { pub fn new(id: &UserID) -> CurrentUserID {
CurrentUserID { CurrentUserID { userID: id.id() }
userID: id.id()
}
} }
} }

View File

@ -10,7 +10,9 @@ pub trait EntitiesConstructor {
/// Parse a list of it /// Parse a list of it
fn for_list(l: &[Self::Item]) -> Vec<Self> fn for_list(l: &[Self::Item]) -> Vec<Self>
where Self: std::marker::Sized { where
Self: std::marker::Sized,
{
l.iter().map(Self::new).collect() l.iter().map(Self::new).collect()
} }
} }

View File

@ -29,8 +29,6 @@ impl GlobalSearchResultAPI {
/// Construct a list of results /// Construct a list of results
pub fn for_list(l: &Vec<GlobalSearchResult>) -> Vec<GlobalSearchResultAPI> { pub fn for_list(l: &Vec<GlobalSearchResult>) -> Vec<GlobalSearchResultAPI> {
l.iter() l.iter().map(|f| Self::new(f)).collect()
.map(|f| Self::new(f))
.collect()
} }
} }

View File

@ -11,7 +11,7 @@ pub struct InnerHTTPError {
#[derive(Serialize)] #[derive(Serialize)]
pub struct HttpError { pub struct HttpError {
pub error: InnerHTTPError pub error: InnerHTTPError,
} }
impl HttpError { impl HttpError {
@ -21,7 +21,7 @@ impl HttpError {
error: InnerHTTPError { error: InnerHTTPError {
code, code,
message: message.to_string(), message: message.to_string(),
} },
} }
} }

View File

@ -15,7 +15,7 @@ pub struct LanguageSettingsAPI {
impl LanguageSettingsAPI { impl LanguageSettingsAPI {
pub fn new(user: &User) -> LanguageSettingsAPI { pub fn new(user: &User) -> LanguageSettingsAPI {
LanguageSettingsAPI { LanguageSettingsAPI {
lang: user.lang.clone() lang: user.lang.clone(),
} }
} }
} }

View File

@ -3,15 +3,16 @@
//! true => 1 //! true => 1
//! false => 0 //! false => 0
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
/// Special implementation of conversation name (false if none / the name otherwise) /// Special implementation of conversation name (false if none / the name otherwise)
pub struct LegacyBool(pub bool); pub struct LegacyBool(pub bool);
impl Serialize for LegacyBool { impl Serialize for LegacyBool {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
S: Serializer { where
S: Serializer,
{
match &self.0 { match &self.0 {
true => serializer.serialize_i8(1), true => serializer.serialize_i8(1),
false => serializer.serialize_i8(0), false => serializer.serialize_i8(0),

View File

@ -27,8 +27,6 @@ impl UnreadConversationAPI {
/// Turn a list of unread conversation into API conversations /// Turn a list of unread conversation into API conversations
pub fn for_list(l: &Vec<ConvID>) -> Res<Vec<UnreadConversationAPI>> { pub fn for_list(l: &Vec<ConvID>) -> Res<Vec<UnreadConversationAPI>> {
l.iter() l.iter().map(|row| Self::new(row.clone())).collect()
.map(|row| Self::new(row.clone()))
.collect()
} }
} }

View File

@ -11,7 +11,7 @@ struct LoginTokens {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct LoginSuccess { pub struct LoginSuccess {
token: String token: String,
} }
impl LoginSuccess { impl LoginSuccess {

View File

@ -9,68 +9,68 @@
pub mod admin; pub mod admin;
pub mod server_config; pub mod account_export_api;
pub mod http_error; pub mod account_image_settings_api;
pub mod login_success;
pub mod current_user_id;
pub mod user_mail_address;
pub mod user_info;
pub mod custom_emoji;
pub mod res_find_user_by_virtual_directory;
pub mod res_find_virtual_directory;
pub mod res_create_conversation;
pub mod conversation_api;
mod legacy_api_bool;
pub mod res_find_private_conversations;
pub mod conversation_message_api;
pub mod res_count_unread_conversations;
pub mod list_unread_conversations_api;
pub mod global_search_result_api;
pub mod res_create_group;
pub mod group_api;
pub mod advanced_group_api; pub mod advanced_group_api;
pub mod res_change_group_logo; pub mod call_member_info;
pub mod group_member_api; pub mod call_peer_interrupted_streaming;
pub mod call_peer_ready;
pub mod comment_api;
pub mod conversation_api;
pub mod conversation_message_api;
pub mod current_user_id;
pub mod custom_emoji;
pub mod data_conservation_settings_api;
pub mod entities_constructor;
pub mod friend_api; pub mod friend_api;
pub mod friendship_status_api; pub mod friendship_status_api;
pub mod post_api;
pub mod survey_choice_api;
pub mod survey_api;
pub mod comment_api;
pub mod res_create_post;
pub mod posts_targets_api;
pub mod res_create_comment;
pub mod res_number_unread_notifications;
pub mod res_count_all_unreads;
pub mod notification_api;
pub mod user_membership_api;
mod type_container_api;
pub mod res_check_email_exists;
pub mod res_check_security_questions_exists;
pub mod res_get_security_questions;
pub mod res_check_security_answers;
pub mod account_export_api;
pub mod user_like_api;
pub mod survey_response_api;
pub mod entities_constructor;
pub mod general_settings_api; pub mod general_settings_api;
pub mod language_settings_api; pub mod global_search_result_api;
pub mod security_settings_api; pub mod group_api;
pub mod account_image_settings_api; pub mod group_member_api;
pub mod data_conservation_settings_api; pub mod http_error;
pub mod res_create_custom_emoji;
pub mod res_get_ws_token;
pub mod user_calls_config;
pub mod joined_call_message; pub mod joined_call_message;
pub mod call_member_info; pub mod language_settings_api;
pub mod left_call_message; pub mod left_call_message;
mod legacy_api_bool;
pub mod list_unread_conversations_api;
pub mod login_success;
pub mod new_call_signal; pub mod new_call_signal;
pub mod call_peer_ready; pub mod notification_api;
pub mod call_peer_interrupted_streaming;
pub mod res_check_password_token;
pub mod removed_user_from_conv_message;
pub mod user_is_writing_message_in_conversation;
pub mod res_create_conversation_for_group;
pub mod notification_settings_api; pub mod notification_settings_api;
pub mod post_api;
pub mod posts_targets_api;
pub mod push_notifications_status_api; pub mod push_notifications_status_api;
pub mod removed_user_from_conv_message;
pub mod res_change_group_logo;
pub mod res_check_email_exists;
pub mod res_check_password_token;
pub mod res_check_security_answers;
pub mod res_check_security_questions_exists;
pub mod res_count_all_unreads;
pub mod res_count_unread_conversations;
pub mod res_create_comment;
pub mod res_create_conversation;
pub mod res_create_conversation_for_group;
pub mod res_create_custom_emoji;
pub mod res_create_group;
pub mod res_create_post;
pub mod res_find_private_conversations;
pub mod res_find_user_by_virtual_directory;
pub mod res_find_virtual_directory;
pub mod res_get_security_questions;
pub mod res_get_ws_token;
pub mod res_number_unread_notifications;
pub mod security_settings_api;
pub mod server_config;
pub mod submit_report_result_api; pub mod submit_report_result_api;
pub mod survey_api;
pub mod survey_choice_api;
pub mod survey_response_api;
mod type_container_api;
pub mod user_calls_config;
pub mod user_info;
pub mod user_is_writing_message_in_conversation;
pub mod user_like_api;
pub mod user_mail_address;
pub mod user_membership_api;

View File

@ -8,8 +8,8 @@ use crate::api_data::survey_api::SurveyAPI;
use crate::data::error::ResultBoxError; use crate::data::error::ResultBoxError;
use crate::data::post::{Post, PostKind}; use crate::data::post::{Post, PostKind};
use crate::data::user::UserID; use crate::data::user::UserID;
use crate::helpers::{comments_helper, likes_helper, posts_helper, survey_helper};
use crate::helpers::likes_helper::LikeType; use crate::helpers::likes_helper::LikeType;
use crate::helpers::{comments_helper, likes_helper, posts_helper, survey_helper};
use crate::utils::user_data_utils::user_data_url; use crate::utils::user_data_utils::user_data_url;
#[derive(Serialize)] #[derive(Serialize)]
@ -113,8 +113,9 @@ impl PostAPI {
PostKind::POST_KIND_COUNTDOWN(time_end) => post.time_end = Some(*time_end), PostKind::POST_KIND_COUNTDOWN(time_end) => post.time_end = Some(*time_end),
PostKind::POST_KIND_SURVEY => PostKind::POST_KIND_SURVEY => {
post.data_survey = Some(SurveyAPI::new(&survey_helper::get_info(p.id)?, *user)?), post.data_survey = Some(SurveyAPI::new(&survey_helper::get_info(p.id)?, *user)?)
}
PostKind::POST_KIND_YOUTUBE(id) => { PostKind::POST_KIND_YOUTUBE(id) => {
post.file_path = Some(id.clone()); post.file_path = Some(id.clone());

View File

@ -19,11 +19,13 @@ impl PushNotificationsStatusAPI {
PushNotificationToken::NONE => "disabled", PushNotificationToken::NONE => "disabled",
PushNotificationToken::INDEPENDENT(_) => "independent", PushNotificationToken::INDEPENDENT(_) => "independent",
PushNotificationToken::FIREBASE(_) => "firebase", PushNotificationToken::FIREBASE(_) => "firebase",
}.to_string(), }
.to_string(),
independent_push_url: match (t, &conf().independent_push_service) { independent_push_url: match (t, &conf().independent_push_service) {
(PushNotificationToken::INDEPENDENT(i), Some(conf)) => (PushNotificationToken::INDEPENDENT(i), Some(conf)) => {
Some(conf.public_url.replace("{TOKEN_URL}", i)), Some(conf.public_url.replace("{TOKEN_URL}", i))
}
_ => None, _ => None,
}, },
} }

View File

@ -5,7 +5,7 @@ use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
pub struct ResCheckEmailExists { pub struct ResCheckEmailExists {
exists: bool exists: bool,
} }
impl ResCheckEmailExists { impl ResCheckEmailExists {

View File

@ -7,7 +7,7 @@ use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
pub struct ResCheckSecurityAnswers { pub struct ResCheckSecurityAnswers {
reset_token: String reset_token: String,
} }
impl ResCheckSecurityAnswers { impl ResCheckSecurityAnswers {

View File

@ -5,7 +5,7 @@ use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
pub struct ResCheckSecurityQuestionsExists { pub struct ResCheckSecurityQuestionsExists {
defined: bool defined: bool,
} }
impl ResCheckSecurityQuestionsExists { impl ResCheckSecurityQuestionsExists {

View File

@ -20,11 +20,17 @@ pub struct ResCountAllUnread {
} }
impl ResCountAllUnread { impl ResCountAllUnread {
pub fn new(notifications: u64, conversations: u64, friends_requests: Option<u64>) -> ResCountAllUnread { pub fn new(
notifications: u64,
conversations: u64,
friends_requests: Option<u64>,
) -> ResCountAllUnread {
ResCountAllUnread { ResCountAllUnread {
notifications, notifications,
conversations, conversations,
friends: friends_requests.map(|n| CountFriendsRequests { friends_requests: n }), friends: friends_requests.map(|n| CountFriendsRequests {
friends_requests: n,
}),
} }
} }
} }

View File

@ -5,14 +5,12 @@ use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
pub struct ResultCountUnreadConversations { pub struct ResultCountUnreadConversations {
nb_unread: usize nb_unread: usize,
} }
impl ResultCountUnreadConversations { impl ResultCountUnreadConversations {
/// Generate an new instance /// Generate an new instance
pub fn new(count: usize) -> ResultCountUnreadConversations { pub fn new(count: usize) -> ResultCountUnreadConversations {
ResultCountUnreadConversations { ResultCountUnreadConversations { nb_unread: count }
nb_unread: count
}
} }
} }

View File

@ -2,8 +2,8 @@
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use serde::{Serialize};
use crate::data::conversation::ConvID; use crate::data::conversation::ConvID;
use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
#[allow(non_snake_case)] #[allow(non_snake_case)]

View File

@ -8,13 +8,13 @@ use crate::data::conversation::ConvID;
#[derive(Serialize)] #[derive(Serialize)]
pub struct ResCreateConversationForGroup { pub struct ResCreateConversationForGroup {
conv_id: u64 conv_id: u64,
} }
impl ResCreateConversationForGroup { impl ResCreateConversationForGroup {
pub fn new(conv_id: ConvID) -> Self { pub fn new(conv_id: ConvID) -> Self {
Self { Self {
conv_id: conv_id.id() conv_id: conv_id.id(),
} }
} }
} }

View File

@ -7,7 +7,7 @@ use serde::Serialize;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Serialize)] #[derive(Serialize)]
pub struct ResCreateCustomEmoji { pub struct ResCreateCustomEmoji {
emojiID: u64 emojiID: u64,
} }
impl ResCreateCustomEmoji { impl ResCreateCustomEmoji {

View File

@ -9,14 +9,14 @@ use crate::data::conversation::ConvID;
#[derive(Serialize)] #[derive(Serialize)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub struct ResFindPrivateConversations { pub struct ResFindPrivateConversations {
conversationsID: Vec<u64> conversationsID: Vec<u64>,
} }
impl ResFindPrivateConversations { impl ResFindPrivateConversations {
/// Construct a new instance of this structure /// Construct a new instance of this structure
pub fn new(list: Vec<ConvID>) -> ResFindPrivateConversations { pub fn new(list: Vec<ConvID>) -> ResFindPrivateConversations {
ResFindPrivateConversations { ResFindPrivateConversations {
conversationsID: list.iter().map(|i| i.id()).collect() conversationsID: list.iter().map(|i| i.id()).collect(),
} }
} }
} }

View File

@ -5,18 +5,17 @@
use crate::data::user::UserID; use crate::data::user::UserID;
use serde::Serialize; use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub struct FindUserByVirtualDirectoryAPIResult { pub struct FindUserByVirtualDirectoryAPIResult {
userID: u64 userID: u64,
} }
impl FindUserByVirtualDirectoryAPIResult { impl FindUserByVirtualDirectoryAPIResult {
/// Construct a new `FindUserByVirtualDirectoryAPIResult` instance /// Construct a new `FindUserByVirtualDirectoryAPIResult` instance
pub fn new(user_id: UserID) -> FindUserByVirtualDirectoryAPIResult { pub fn new(user_id: UserID) -> FindUserByVirtualDirectoryAPIResult {
FindUserByVirtualDirectoryAPIResult { FindUserByVirtualDirectoryAPIResult {
userID: user_id.id() userID: user_id.id(),
} }
} }
} }

View File

@ -2,10 +2,10 @@
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use serde::{Serialize};
use crate::data::error::ResultBoxError; use crate::data::error::ResultBoxError;
use crate::data::user::User;
use crate::data::group_id::GroupID; use crate::data::group_id::GroupID;
use crate::data::user::User;
use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
pub struct ResultFindVirtualDirectory { pub struct ResultFindVirtualDirectory {
@ -15,16 +15,24 @@ pub struct ResultFindVirtualDirectory {
impl ResultFindVirtualDirectory { impl ResultFindVirtualDirectory {
/// Construct a new instance /// Construct a new instance
pub fn new(user: ResultBoxError<User>, group: ResultBoxError<GroupID>) -> ResultFindVirtualDirectory { pub fn new(
user: ResultBoxError<User>,
group: ResultBoxError<GroupID>,
) -> ResultFindVirtualDirectory {
match (user, group) { match (user, group) {
// User // User
(Ok(u), _) => ResultFindVirtualDirectory { kind: "user".to_string(), id: u.id.id() }, (Ok(u), _) => ResultFindVirtualDirectory {
kind: "user".to_string(),
id: u.id.id(),
},
// Group // Group
(_, Ok(g)) => ResultFindVirtualDirectory { kind: "group".to_string(), id: g.id() }, (_, Ok(g)) => ResultFindVirtualDirectory {
kind: "group".to_string(),
id: g.id(),
},
_ => unreachable!() _ => unreachable!(),
} }
} }
} }

View File

@ -6,7 +6,7 @@ use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
pub struct ResNumberUnreadNotifications { pub struct ResNumberUnreadNotifications {
number: u64 number: u64,
} }
impl ResNumberUnreadNotifications { impl ResNumberUnreadNotifications {

View File

@ -5,12 +5,23 @@ use std::collections::HashMap;
use serde::Serialize; use serde::Serialize;
use crate::constants::{conservation_policy, MIN_SUPPORTED_MOBILE_VERSION, password_policy, reports}; use crate::constants::accounts_info_policy::{
use crate::constants::accounts_info_policy::{MAX_FIRST_NAME_LENGTH, MAX_LAST_NAME_LENGTH, MAX_LOCATION_LENGTH, MIN_FIRST_NAME_LENGTH, MIN_LAST_NAME_LENGTH}; MAX_FIRST_NAME_LENGTH, MAX_LAST_NAME_LENGTH, MAX_LOCATION_LENGTH, MIN_FIRST_NAME_LENGTH,
use crate::constants::conversations::{ALLOWED_CONVERSATION_FILES_TYPES, CONVERSATION_FILES_MAX_SIZE, CONVERSATION_WRITING_EVENT_INTERVAL, CONVERSATION_WRITING_EVENT_LIFETIME, MAX_CONV_IMAGE_MESSAGE_WIDTH, MAX_CONV_LOGO_HEIGHT, MAX_CONV_LOGO_WIDTH, MAX_CONV_MESSAGE_THUMBNAIL_HEIGHT, MAX_CONV_MESSAGE_THUMBNAIL_WIDTH, MAX_CONVERSATION_MESSAGE_LENGTH, MAX_CONVERSATION_NAME_LENGTH, MIN_CONVERSATION_MESSAGE_LENGTH}; MIN_LAST_NAME_LENGTH,
};
use crate::constants::conversations::{
ALLOWED_CONVERSATION_FILES_TYPES, CONVERSATION_FILES_MAX_SIZE,
CONVERSATION_WRITING_EVENT_INTERVAL, CONVERSATION_WRITING_EVENT_LIFETIME,
MAX_CONVERSATION_MESSAGE_LENGTH, MAX_CONVERSATION_NAME_LENGTH, MAX_CONV_IMAGE_MESSAGE_WIDTH,
MAX_CONV_LOGO_HEIGHT, MAX_CONV_LOGO_WIDTH, MAX_CONV_MESSAGE_THUMBNAIL_HEIGHT,
MAX_CONV_MESSAGE_THUMBNAIL_WIDTH, MIN_CONVERSATION_MESSAGE_LENGTH,
};
use crate::constants::reports::CAN_USER_REPORT_ITS_OWN_CONTENT; use crate::constants::reports::CAN_USER_REPORT_ITS_OWN_CONTENT;
use crate::constants::{
conservation_policy, password_policy, reports, MIN_SUPPORTED_MOBILE_VERSION,
};
use crate::data::api_client::APIClient; use crate::data::api_client::APIClient;
use crate::data::config::{Banner, conf}; use crate::data::config::{conf, Banner};
use crate::data::report::REPORT_CAUSES; use crate::data::report::REPORT_CAUSES;
#[derive(Serialize)] #[derive(Serialize)]
@ -108,7 +119,12 @@ impl ServerConfig {
play_store_url: &conf().play_store_url, play_store_url: &conf().play_store_url,
android_direct_download_url: conf().android_direct_download_url.clone(), android_direct_download_url: conf().android_direct_download_url.clone(),
banner: match conf().banner.as_ref().map(|b| b.is_visible()).unwrap_or(false) { banner: match conf()
.banner
.as_ref()
.map(|b| b.is_visible())
.unwrap_or(false)
{
true => conf().banner.as_ref(), true => conf().banner.as_ref(),
false => None, false => None,
}, },
@ -130,11 +146,14 @@ impl ServerConfig {
}, },
data_conservation_policy: DataConservationPolicy { data_conservation_policy: DataConservationPolicy {
min_inactive_account_lifetime: conservation_policy::MIN_INACTIVE_ACCOUNT_LIFETIME.as_secs(), min_inactive_account_lifetime: conservation_policy::MIN_INACTIVE_ACCOUNT_LIFETIME
min_notification_lifetime: conservation_policy::MIN_NOTIFICATIONS_LIFETIME.as_secs(), .as_secs(),
min_notification_lifetime: conservation_policy::MIN_NOTIFICATIONS_LIFETIME
.as_secs(),
min_comments_lifetime: conservation_policy::MIN_COMMENTS_LIFETIME.as_secs(), min_comments_lifetime: conservation_policy::MIN_COMMENTS_LIFETIME.as_secs(),
min_posts_lifetime: conservation_policy::MIN_POSTS_LIFETIME.as_secs(), min_posts_lifetime: conservation_policy::MIN_POSTS_LIFETIME.as_secs(),
min_conversation_messages_lifetime: conservation_policy::MIN_CONVERSATION_MESSAGES_LIFETIME.as_secs(), min_conversation_messages_lifetime:
conservation_policy::MIN_CONVERSATION_MESSAGES_LIFETIME.as_secs(),
min_likes_lifetime: conservation_policy::MIN_LIKES_LIFETIME.as_secs(), min_likes_lifetime: conservation_policy::MIN_LIKES_LIFETIME.as_secs(),
}, },
@ -164,17 +183,17 @@ impl ServerConfig {
report_policy: match conf().allow_reporting { report_policy: match conf().allow_reporting {
true => Some(ReportPolicy { true => Some(ReportPolicy {
causes: REPORT_CAUSES.iter().map(|r| ReportCause { causes: REPORT_CAUSES
.iter()
.map(|r| ReportCause {
id: r.id().id(), id: r.id().id(),
label: HashMap::from([ label: HashMap::from([("fr", r.label_fr), ("en", r.label_en)]),
("fr", r.label_fr), })
("en", r.label_en) .collect(),
]),
}).collect(),
max_comment_length: reports::MAX_COMMENT_LENGTH, max_comment_length: reports::MAX_COMMENT_LENGTH,
can_user_report_his_own_content: CAN_USER_REPORT_ITS_OWN_CONTENT, can_user_report_his_own_content: CAN_USER_REPORT_ITS_OWN_CONTENT,
}), }),
false => None false => None,
}, },
} }
} }

View File

@ -27,7 +27,9 @@ impl SurveyChoiceAPI {
pub fn for_list(c: &[SurveyChoice]) -> HashMap<u64, SurveyChoiceAPI> { pub fn for_list(c: &[SurveyChoice]) -> HashMap<u64, SurveyChoiceAPI> {
let mut map = HashMap::with_capacity(c.len()); let mut map = HashMap::with_capacity(c.len());
c.iter().for_each(|c| { map.insert(c.id, Self::new(c)); }); c.iter().for_each(|c| {
map.insert(c.id, Self::new(c));
});
map map
} }
} }

View File

@ -1,8 +1,8 @@
//! Use this structure for all structures that needs a "type" field //! Use this structure for all structures that needs a "type" field
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use serde::{Serialize, Serializer};
use serde::ser::SerializeMap; use serde::ser::SerializeMap;
use serde::{Serialize, Serializer};
pub struct TypeContainerAPI { pub struct TypeContainerAPI {
t: String, t: String,
@ -15,8 +15,10 @@ impl TypeContainerAPI {
} }
impl Serialize for TypeContainerAPI { impl Serialize for TypeContainerAPI {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
S: Serializer { where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(1))?; let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("type", &self.t)?; map.serialize_entry("type", &self.t)?;
map.end() map.end()

View File

@ -14,6 +14,8 @@ pub struct UserCallsConfig {
impl UserCallsConfig { impl UserCallsConfig {
pub fn new(conf: &RtcRelayConfig) -> Self { pub fn new(conf: &RtcRelayConfig) -> Self {
Self { iceServers: conf.ice_servers.clone() } Self {
iceServers: conf.ice_servers.clone(),
}
} }
} }

View File

@ -5,10 +5,12 @@ use serde::Serialize;
use crate::api_data::custom_emoji::CustomEmojiAPI; use crate::api_data::custom_emoji::CustomEmojiAPI;
use crate::data::error::ResultBoxError; use crate::data::error::ResultBoxError;
use crate::data::user::{User, UserID, UserPageVisibility};
use crate::data::user::AccountImageVisibility::{COMUNIC_USERS, EVERYONE}; use crate::data::user::AccountImageVisibility::{COMUNIC_USERS, EVERYONE};
use crate::helpers::{background_image_helper, custom_emojies_helper, friends_helper, likes_helper, user_helper}; use crate::data::user::{User, UserID, UserPageVisibility};
use crate::helpers::likes_helper::LikeType; use crate::helpers::likes_helper::LikeType;
use crate::helpers::{
background_image_helper, custom_emojies_helper, friends_helper, likes_helper, user_helper,
};
use crate::utils::user_data_utils::user_data_url; use crate::utils::user_data_utils::user_data_url;
#[derive(Serialize)] #[derive(Serialize)]
@ -65,7 +67,10 @@ impl APIUserInfo {
} }
/// Get advanced user information /// Get advanced user information
pub fn new_advanced_info(user_id: &Option<UserID>, info: &User) -> ResultBoxError<APIAdvancedInfo> { pub fn new_advanced_info(
user_id: &Option<UserID>,
info: &User,
) -> ResultBoxError<APIAdvancedInfo> {
let user = APIUserInfo::new(&user_id, info)?; let user = APIUserInfo::new(&user_id, info)?;
let curr_user_id = user_id.clone().unwrap_or(UserID::new(0)); let curr_user_id = user_id.clone().unwrap_or(UserID::new(0));
let signed_in = user_id.is_some(); let signed_in = user_id.is_some();
@ -73,11 +78,15 @@ impl APIUserInfo {
// Check if we can return the number of friends of the user // Check if we can return the number of friends of the user
let number_friends = if info.public_friends_list || curr_user_id == info.id { let number_friends = if info.public_friends_list || curr_user_id == info.id {
friends_helper::count_friends(&info.id)? friends_helper::count_friends(&info.id)?
} else { 0 }; } else {
0
};
let likes_page = if signed_in { let likes_page = if signed_in {
likes_helper::is_liking(&curr_user_id, info.id.id(), LikeType::USER)? likes_helper::is_liking(&curr_user_id, info.id.id(), LikeType::USER)?
} else { false }; } else {
false
};
// Set advanced user information // Set advanced user information
Ok(APIAdvancedInfo { Ok(APIAdvancedInfo {
@ -113,16 +122,17 @@ impl APIUserInfo {
return user_account_image; return user_account_image;
} }
if user_id.is_none() { // User is not signed in if user_id.is_none() {
// User is not signed in
return Ok(User::error_account_image_url()); return Ok(User::error_account_image_url());
} }
if user.account_image_visibility == COMUNIC_USERS || if user.account_image_visibility == COMUNIC_USERS
friends_helper::are_friend(&user_id.clone().unwrap(), &user.id)? { || friends_helper::are_friend(&user_id.clone().unwrap(), &user.id)?
{
return user_account_image; return user_account_image;
} }
Ok(User::error_account_image_url()) Ok(User::error_account_image_url())
} }
} }

View File

@ -14,7 +14,7 @@ pub struct UserMailAddressAPI {
impl UserMailAddressAPI { impl UserMailAddressAPI {
pub fn new(user: &User) -> Self { pub fn new(user: &User) -> Self {
Self { Self {
mail: user.email.to_string() mail: user.email.to_string(),
} }
} }
} }

View File

@ -44,7 +44,7 @@ impl UserMembershipAPI {
friend: None, friend: None,
last_activity: None, last_activity: None,
conv: Some(ConversationAPI::new(conversation)), conv: Some(ConversationAPI::new(conversation)),
} },
} }
} }

View File

@ -37,13 +37,11 @@ fn clean_up_thread_handler() {
/// Do the cleanup /// Do the cleanup
async fn do_clean() -> Res { async fn do_clean() -> Res {
// Clean old login tokens // Clean old login tokens
account_helper::clean_up_old_access_tokens().await?; account_helper::clean_up_old_access_tokens().await?;
// Automatic account cleanup // Automatic account cleanup
for user in user_helper::get_all_users()? { for user in user_helper::get_all_users()? {
// Clean old likes // Clean old likes
likes_helper::clean_old_user_likes(&user)?; likes_helper::clean_old_user_likes(&user)?;

View File

@ -209,25 +209,27 @@ pub mod conversations {
/// Allowed files type in conversations /// Allowed files type in conversations
pub const ALLOWED_CONVERSATION_FILES_TYPES: [&str; 18] = [ pub const ALLOWED_CONVERSATION_FILES_TYPES: [&str; 18] = [
"image/png", "image/jpeg", "image/gif", "image/bmp", "image/png",
"image/jpeg",
"image/gif",
"image/bmp",
"application/pdf", "application/pdf",
"audio/mpeg", "audio/mpeg",
"video/mp4", "video/mp4",
"video/quicktime", "video/quicktime",
"application/zip", "application/zip",
// MS Office docs // MS Office docs
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.openxmlformats-officedocument.presentationml.presentation", "application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
// Open Office docs // Open Office docs
"application/vnd.oasis.opendocument.text", "application/vnd.oasis.opendocument.text",
"application/vnd.oasis.opendocument.presentation", "application/vnd.oasis.opendocument.presentation",
"application/vnd.oasis.opendocument.spreadsheet", "application/vnd.oasis.opendocument.spreadsheet",
// Source code docs (UTF-8 encoded) // Source code docs (UTF-8 encoded)
"text/x-csrc", "text/plain", "text/x-c++src" "text/x-csrc",
"text/plain",
"text/x-c++src",
]; ];
/// File maximum size in conversations (10 Mb) /// File maximum size in conversations (10 Mb)
@ -267,7 +269,8 @@ pub mod accounts_info_policy {
} }
/// Url where Firebase push notifications can be sent /// Url where Firebase push notifications can be sent
pub const FIREBASE_PUSH_MESSAGE_URL: &str = "https://fcm.googleapis.com/v1/projects/{PROJECT_ID}/messages:send"; pub const FIREBASE_PUSH_MESSAGE_URL: &str =
"https://fcm.googleapis.com/v1/projects/{PROJECT_ID}/messages:send";
/// Reports constants /// Reports constants
pub mod reports { pub mod reports {
@ -313,6 +316,6 @@ pub mod admin {
id: "access_all_admins_logs", id: "access_all_admins_logs",
name: "Access all admins logs", name: "Access all admins logs",
description: "Allow the admin to access the action history (log) of all admins", description: "Allow the admin to access the action history (log) of all admins",
} },
]; ];
} }

View File

@ -35,7 +35,10 @@ impl HttpRequestHandler {
} }
/// Get the ID of the user associated with a password reset token /// Get the ID of the user associated with a password reset token
pub fn post_user_id_from_password_reset_token(&mut self, field: &str) -> ResultBoxError<UserID> { pub fn post_user_id_from_password_reset_token(
&mut self,
field: &str,
) -> ResultBoxError<UserID> {
let token = self.post_string_opt(field, PASSWORD_RESET_TOKEN_LENGTH, true)?; let token = self.post_string_opt(field, PASSWORD_RESET_TOKEN_LENGTH, true)?;
let user_id = self.ok_or_forbidden( let user_id = self.ok_or_forbidden(
account_helper::get_user_id_from_password_reset_token(&token), account_helper::get_user_id_from_password_reset_token(&token),
@ -48,7 +51,6 @@ impl HttpRequestHandler {
/// Create a new account /// Create a new account
pub async fn create(r: &mut HttpRequestHandler) -> RequestResult { pub async fn create(r: &mut HttpRequestHandler) -> RequestResult {
// Get & check email // Get & check email
let email = r.post_email("emailAddress")?; let email = r.post_email("emailAddress")?;
if account_helper::exists_mail(&email)? { if account_helper::exists_mail(&email)? {
@ -77,13 +79,10 @@ pub async fn login_user(request: &mut HttpRequestHandler) -> RequestResult {
} }
// Authenticate user // Authenticate user
let token = account_helper::login_user( let token = account_helper::login_user(&email, &password, request.api_client());
&email, &password, request.api_client());
match token { match token {
Ok(t) => { Ok(t) => request.set_response(LoginSuccess::new(&t)),
request.set_response(LoginSuccess::new(&t))
}
Err(e) => { Err(e) => {
println!("Error on login: {}", e); println!("Error on login: {}", e);
request.forbidden("Invalid email address / password!".to_string()) request.forbidden("Invalid email address / password!".to_string())
@ -122,14 +121,18 @@ pub async fn get_mail(r: &mut HttpRequestHandler) -> RequestResult {
pub async fn exists_mail(r: &mut HttpRequestHandler) -> RequestResult { pub async fn exists_mail(r: &mut HttpRequestHandler) -> RequestResult {
let email = r.post_email("email")?; let email = r.post_email("email")?;
r.set_response(ResCheckEmailExists::new(account_helper::exists_mail(&email)?)) r.set_response(ResCheckEmailExists::new(account_helper::exists_mail(
&email,
)?))
} }
/// Check out whether a given user has set security questions or not /// Check out whether a given user has set security questions or not
pub async fn has_security_questions(r: &mut HttpRequestHandler) -> RequestResult { pub async fn has_security_questions(r: &mut HttpRequestHandler) -> RequestResult {
let user = r.post_user_info_from_email("email")?; let user = r.post_user_info_from_email("email")?;
r.set_response(ResCheckSecurityQuestionsExists::new(user.has_security_questions())) r.set_response(ResCheckSecurityQuestionsExists::new(
user.has_security_questions(),
))
} }
/// Get the security questions of a user /// Get the security questions of a user
@ -151,17 +154,25 @@ pub async fn check_security_answers(r: &mut HttpRequestHandler) -> RequestResult
r.forbidden("Specified user has not setup security questions !".to_string())?; r.forbidden("Specified user has not setup security questions !".to_string())?;
} }
let answers: Vec<String> = r.post_string_opt("answers", 3, true)? let answers: Vec<String> = r
.post_string_opt("answers", 3, true)?
.split("&") .split("&")
.map(|s| percent_decode_str(s).decode_utf8_lossy().to_lowercase().trim().to_string()) .map(|s| {
percent_decode_str(s)
.decode_utf8_lossy()
.to_lowercase()
.trim()
.to_string()
})
.collect::<Vec<String>>(); .collect::<Vec<String>>();
if answers.len() != 2 { if answers.len() != 2 {
r.forbidden("Please specify two answers!".to_string())?; r.forbidden("Please specify two answers!".to_string())?;
} }
if answers[0] != user.security_answer_1.unwrap().to_lowercase().trim() || if answers[0] != user.security_answer_1.unwrap().to_lowercase().trim()
answers[1] != user.security_answer_2.unwrap().to_lowercase().trim() { || answers[1] != user.security_answer_2.unwrap().to_lowercase().trim()
{
r.forbidden("Invalid security answers!".to_string())?; r.forbidden("Invalid security answers!".to_string())?;
} }

View File

@ -2,7 +2,6 @@
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use crate::api_data::admin::admin_auth_options::AdminAuthOptions; use crate::api_data::admin::admin_auth_options::AdminAuthOptions;
use crate::api_data::admin::admin_auth_success::AdminAuthSuccess; use crate::api_data::admin::admin_auth_success::AdminAuthSuccess;
use crate::api_data::admin::admin_id_api::AdminIDAPI; use crate::api_data::admin::admin_id_api::AdminIDAPI;
@ -14,8 +13,8 @@ use crate::data::admin::{NewAdmin, NewAdminGeneralSettings};
use crate::data::admin_action_log::AdminAction; use crate::data::admin_action_log::AdminAction;
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
use crate::helpers::{admin_access_token_helper, admin_account_helper, admin_account_key_helper};
use crate::helpers::admin_log_helper::log_admin_action; use crate::helpers::admin_log_helper::log_admin_action;
use crate::helpers::{admin_access_token_helper, admin_account_helper, admin_account_key_helper};
use crate::routes::RequestResult; use crate::routes::RequestResult;
use crate::utils::date_utils::time; use crate::utils::date_utils::time;
@ -27,8 +26,15 @@ pub async fn create(r: &mut HttpRequestHandler) -> RequestResult {
let new_admin = NewAdmin { name, email }; let new_admin = NewAdmin { name, email };
let admin_id = admin_account_helper::create(&new_admin)?; let admin_id = admin_account_helper::create(&new_admin)?;
log_admin_action(r.admin_id()?, &r.remote_ip(), log_admin_action(
AdminAction::CreatedAdmin { id: admin_id, name: new_admin.name, email: new_admin.email })?; r.admin_id()?,
&r.remote_ip(),
AdminAction::CreatedAdmin {
id: admin_id,
name: new_admin.name,
email: new_admin.email,
},
)?;
r.set_response(AdminResCreateAccount::new(admin_id)) r.set_response(AdminResCreateAccount::new(admin_id))
} }
@ -58,8 +64,7 @@ pub async fn auth_with_reset_token(r: &mut HttpRequestHandler) -> RequestResult
let token = admin_access_token_helper::create(admin.id)?; let token = admin_access_token_helper::create(admin.id)?;
log_admin_action(admin.id, &r.remote_ip(), log_admin_action(admin.id, &r.remote_ip(), AdminAction::AuthWithResetToken)?;
AdminAction::AuthWithResetToken)?;
r.set_response(AdminAuthSuccess::new(token)) r.set_response(AdminAuthSuccess::new(token))
} }
@ -115,8 +120,15 @@ pub async fn update_general_settings(r: &mut HttpRequestHandler) -> RequestResul
email: new_email.to_string(), email: new_email.to_string(),
})?; })?;
log_admin_action(r.admin_id()?, &r.remote_ip(), log_admin_action(
AdminAction::UpdatedAdminGeneralSettings { target: admin_id, new_name, new_email })?; r.admin_id()?,
&r.remote_ip(),
AdminAction::UpdatedAdminGeneralSettings {
target: admin_id,
new_name,
new_email,
},
)?;
r.ok() r.ok()
} }
@ -131,8 +143,11 @@ pub async fn generate_reset_token(r: &mut HttpRequestHandler) -> RequestResult {
let token = admin_account_helper::create_new_reset_token(admin_id)?; let token = admin_account_helper::create_new_reset_token(admin_id)?;
log_admin_action(r.admin_id()?, &r.remote_ip(), log_admin_action(
AdminAction::GeneratedAdminResetToken { target: admin_id })?; r.admin_id()?,
&r.remote_ip(),
AdminAction::GeneratedAdminResetToken { target: admin_id },
)?;
r.set_response(AdminResCreateResetToken::new(token)) r.set_response(AdminResCreateResetToken::new(token))
} }

View File

@ -2,7 +2,6 @@
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use bcrypt::verify; use bcrypt::verify;
use webauthn_rs::prelude::Uuid; use webauthn_rs::prelude::Uuid;
@ -15,8 +14,11 @@ use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::error::Res; use crate::data::error::Res;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::webauthn_config::get_wan; use crate::data::webauthn_config::get_wan;
use crate::helpers::{admin_access_token_helper, admin_account_helper, admin_account_key_helper, admin_key_authentication_challenges_helper, admin_key_registration_challenges_helper};
use crate::helpers::admin_log_helper::log_admin_action; use crate::helpers::admin_log_helper::log_admin_action;
use crate::helpers::{
admin_access_token_helper, admin_account_helper, admin_account_key_helper,
admin_key_authentication_challenges_helper, admin_key_registration_challenges_helper,
};
use crate::routes::RequestResult; use crate::routes::RequestResult;
impl HttpRequestHandler { impl HttpRequestHandler {
@ -27,12 +29,13 @@ impl HttpRequestHandler {
let admin = admin_account_helper::find_admin_by_email(&mail)?; let admin = admin_account_helper::find_admin_by_email(&mail)?;
let keys = admin_account_key_helper::get_admin_keys(admin.id)?; let keys = admin_account_key_helper::get_admin_keys(admin.id)?;
let key = keys.into_iter() let key = keys.into_iter().find(|k| k.id == key_id);
.find(|k| k.id == key_id);
match key { match key {
Some(key) => Ok(key), Some(key) => Ok(key),
None => Err(self.bad_request("The key is not associated with this account!".to_string()).unwrap_err()) None => Err(self
.bad_request("The key is not associated with this account!".to_string())
.unwrap_err()),
} }
} }
} }
@ -47,21 +50,21 @@ pub async fn get_keys_list(r: &mut HttpRequestHandler) -> RequestResult {
let keys = admin_account_key_helper::get_admin_keys(admin_id)?; let keys = admin_account_key_helper::get_admin_keys(admin_id)?;
r.set_response(keys.iter().map(AdminKeyAPI::new).collect::<Vec<AdminKeyAPI>>()) r.set_response(
keys.iter()
.map(AdminKeyAPI::new)
.collect::<Vec<AdminKeyAPI>>(),
)
} }
/// Generate a challenge to register a new key /// Generate a challenge to register a new key
pub async fn challenge_register_key(r: &mut HttpRequestHandler) -> RequestResult { pub async fn challenge_register_key(r: &mut HttpRequestHandler) -> RequestResult {
let wan = get_wan(); let wan = get_wan();
let user_info = admin_account_helper::find_admin_by_id(r.admin_id()?)?; let user_info = admin_account_helper::find_admin_by_id(r.admin_id()?)?;
let (res, state) = wan.start_passkey_registration( let (res, state) =
Uuid::new_v4(), wan.start_passkey_registration(Uuid::new_v4(), &user_info.name, &user_info.name, None)?;
&user_info.name,
&user_info.name,
None)?;
admin_key_registration_challenges_helper::set(r.admin_id()?, state)?; admin_key_registration_challenges_helper::set(r.admin_id()?, state)?;
@ -84,12 +87,15 @@ pub async fn register_key(r: &mut HttpRequestHandler) -> RequestResult {
let key_id = admin_account_key_helper::add_key(r.admin_id()?, &key_name, key, key_password)?; let key_id = admin_account_key_helper::add_key(r.admin_id()?, &key_name, key, key_password)?;
log_admin_action(r.admin_id()?, &r.remote_ip(), log_admin_action(
r.admin_id()?,
&r.remote_ip(),
AdminAction::RegisteredAdminKey { AdminAction::RegisteredAdminKey {
key_id, key_id,
key_name, key_name,
target: r.admin_id()?, target: r.admin_id()?,
})?; },
)?;
r.ok() r.ok()
} }
@ -105,12 +111,15 @@ pub async fn delete_auth_key(r: &mut HttpRequestHandler) -> RequestResult {
for key in admin_account_key_helper::get_admin_keys(admin_id)? { for key in admin_account_key_helper::get_admin_keys(admin_id)? {
if key.id == key_id { if key.id == key_id {
log_admin_action(r.admin_id()?, &r.remote_ip(), log_admin_action(
r.admin_id()?,
&r.remote_ip(),
AdminAction::DeletedAdminKey { AdminAction::DeletedAdminKey {
key_id, key_id,
key_name: key.name.to_string(), key_name: key.name.to_string(),
target: admin_id, target: admin_id,
})?; },
)?;
admin_account_key_helper::delete_key(key)?; admin_account_key_helper::delete_key(key)?;
@ -125,8 +134,7 @@ pub async fn delete_auth_key(r: &mut HttpRequestHandler) -> RequestResult {
pub async fn challenge_auth_with_key(r: &mut HttpRequestHandler) -> RequestResult { pub async fn challenge_auth_with_key(r: &mut HttpRequestHandler) -> RequestResult {
let key = r.post_admin_auth_key("mail", "key_id")?; let key = r.post_admin_auth_key("mail", "key_id")?;
let (challenge_response, auth_state) = let (challenge_response, auth_state) = get_wan().start_passkey_authentication(&[key.key])?;
get_wan().start_passkey_authentication(&[key.key])?;
admin_key_authentication_challenges_helper::set(key.id, auth_state)?; admin_key_authentication_challenges_helper::set(key.id, auth_state)?;
@ -158,8 +166,14 @@ pub async fn auth_with_key(r: &mut HttpRequestHandler) -> RequestResult {
// Generate access token // Generate access token
let token = admin_access_token_helper::create(key.admin_id)?; let token = admin_access_token_helper::create(key.admin_id)?;
log_admin_action(key.admin_id, &r.remote_ip(), log_admin_action(
AdminAction::AuthWithAccessKey { key: key.name, key_id: key.id })?; key.admin_id,
&r.remote_ip(),
AdminAction::AuthWithAccessKey {
key: key.name,
key_id: key.id,
},
)?;
r.set_response(AdminAuthSuccess::new(token)) r.set_response(AdminAuthSuccess::new(token))
} }

View File

@ -2,18 +2,19 @@
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use crate::routes::RequestResult;
use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::base_request_handler::BaseRequestHandler;
use crate::helpers::{admin_roles_helper, admin_log_helper};
use crate::constants::admin::AdminRole;
use crate::api_data::admin::admin_log_api::AdminLogAPI; use crate::api_data::admin::admin_log_api::AdminLogAPI;
use crate::constants::admin::AdminRole;
use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::http_request_handler::HttpRequestHandler;
use crate::helpers::{admin_log_helper, admin_roles_helper};
use crate::routes::RequestResult;
/// Get the list of logs of the user /// Get the list of logs of the user
pub async fn get_list(r: &mut HttpRequestHandler) -> RequestResult { pub async fn get_list(r: &mut HttpRequestHandler) -> RequestResult {
let logs = match admin_roles_helper::has_role(r.admin_id()?, AdminRole::ACCESS_ALL_ADMINS_LOGS)? { let logs = match admin_roles_helper::has_role(r.admin_id()?, AdminRole::ACCESS_ALL_ADMINS_LOGS)?
{
true => admin_log_helper::get_all_admin_logs(), true => admin_log_helper::get_all_admin_logs(),
false => admin_log_helper::get_admin_logs(r.admin_id()?) false => admin_log_helper::get_admin_logs(r.admin_id()?),
}?; }?;
r.set_response(logs.iter().map(AdminLogAPI::new).collect::<Vec<_>>()) r.set_response(logs.iter().map(AdminLogAPI::new).collect::<Vec<_>>())

View File

@ -3,7 +3,7 @@
//! @author Pierre Hubert //! @author Pierre Hubert
use crate::api_data::admin::admin_role_api::AdminRoleDetailsAPI; use crate::api_data::admin::admin_role_api::AdminRoleDetailsAPI;
use crate::constants::admin::{ADMIN_ROLES_LIST, AdminRole}; use crate::constants::admin::{AdminRole, ADMIN_ROLES_LIST};
use crate::data::admin_action_log::AdminAction; use crate::data::admin_action_log::AdminAction;
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
@ -13,7 +13,8 @@ use crate::routes::RequestResult;
/// Get the list of roles embedded in the code /// Get the list of roles embedded in the code
pub async fn get_list(r: &mut HttpRequestHandler) -> RequestResult { pub async fn get_list(r: &mut HttpRequestHandler) -> RequestResult {
let res = ADMIN_ROLES_LIST.iter() let res = ADMIN_ROLES_LIST
.iter()
.map(AdminRoleDetailsAPI::new) .map(AdminRoleDetailsAPI::new)
.collect::<Vec<AdminRoleDetailsAPI>>(); .collect::<Vec<AdminRoleDetailsAPI>>();
@ -32,13 +33,25 @@ pub async fn toggle(r: &mut HttpRequestHandler) -> RequestResult {
if !enable { if !enable {
admin_roles_helper::remove_role(admin_id, role)?; admin_roles_helper::remove_role(admin_id, role)?;
log_admin_action(r.admin_id()?, &r.remote_ip(), log_admin_action(
AdminAction::RemoveAdminRole { target: admin_id, role: role_str })?; r.admin_id()?,
&r.remote_ip(),
AdminAction::RemoveAdminRole {
target: admin_id,
role: role_str,
},
)?;
} else if !admin_roles_helper::has_role(admin_id, role)? { } else if !admin_roles_helper::has_role(admin_id, role)? {
admin_roles_helper::add_role(admin_id, role)?; admin_roles_helper::add_role(admin_id, role)?;
log_admin_action(r.admin_id()?, &r.remote_ip(), log_admin_action(
AdminAction::AddAdminRole { target: admin_id, role: role_str })?; r.admin_id()?,
&r.remote_ip(),
AdminAction::AddAdminRole {
target: admin_id,
role: role_str,
},
)?;
} }
r.ok() r.ok()

View File

@ -10,8 +10,8 @@ use crate::data::admin_action_log::AdminAction;
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::config::conf; use crate::data::config::conf;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
use crate::helpers::{account_helper, user_helper};
use crate::helpers::admin_log_helper::log_admin_action; use crate::helpers::admin_log_helper::log_admin_action;
use crate::helpers::{account_helper, user_helper};
use crate::routes::RequestResult; use crate::routes::RequestResult;
/// Search for user /// Search for user
@ -23,7 +23,12 @@ pub async fn search(r: &mut HttpRequestHandler) -> RequestResult {
let results = user_helper::search_user_admin(&name, &email, 50)?; let results = user_helper::search_user_admin(&name, &email, 50)?;
r.set_response(results.into_iter().map(AdminSearchUserResult::new).collect::<Vec<_>>()) r.set_response(
results
.into_iter()
.map(AdminSearchUserResult::new)
.collect::<Vec<_>>(),
)
} }
/// Get information about a single user /// Get information about a single user
@ -33,8 +38,14 @@ pub async fn get_single(r: &mut HttpRequestHandler) -> RequestResult {
let user_id = r.post_user_id("user_id")?; let user_id = r.post_user_id("user_id")?;
let user = user_helper::find_user_by_id(&user_id)?; let user = user_helper::find_user_by_id(&user_id)?;
log_admin_action(r.admin_id()?, &r.remote_ip(), log_admin_action(
AdminAction::AccessUserPage { user_id, user_name: user.full_name() })?; r.admin_id()?,
&r.remote_ip(),
AdminAction::AccessUserPage {
user_id,
user_name: user.full_name(),
},
)?;
r.set_response(AdminUserInfoAPI::new(user)) r.set_response(AdminUserInfoAPI::new(user))
} }
@ -49,19 +60,25 @@ pub async fn change_email_address(r: &mut HttpRequestHandler) -> RequestResult {
// We check if the email address is already used // We check if the email address is already used
if user_helper::find_user_by_email(&new_mail).is_ok() { if user_helper::find_user_by_email(&new_mail).is_ok() {
r.bad_request(format!("The email address {} is already attributed!", new_mail))?; r.bad_request(format!(
"The email address {} is already attributed!",
new_mail
))?;
} }
// Do the update // Do the update
account_helper::set_email(user_id, &new_mail)?; account_helper::set_email(user_id, &new_mail)?;
log_admin_action(r.admin_id()?, &r.remote_ip(), log_admin_action(
r.admin_id()?,
&r.remote_ip(),
AdminAction::ChangedEmailAddress { AdminAction::ChangedEmailAddress {
user_id, user_id,
user_name: user.full_name(), user_name: user.full_name(),
old_mail: user.email, old_mail: user.email,
new_mail, new_mail,
})?; },
)?;
r.ok() r.ok()
} }
@ -76,8 +93,14 @@ pub async fn create_password_reset_link(r: &mut HttpRequestHandler) -> RequestRe
let token = account_helper::generate_password_reset_token(&user_id)?; let token = account_helper::generate_password_reset_token(&user_id)?;
let reset_link = conf().password_reset_url.replace("{TOKEN}", &token); let reset_link = conf().password_reset_url.replace("{TOKEN}", &token);
log_admin_action(r.admin_id()?, &r.remote_ip(), log_admin_action(
AdminAction::CreatePasswordRecoveryLink { user_id, user_name: user.full_name() })?; r.admin_id()?,
&r.remote_ip(),
AdminAction::CreatePasswordRecoveryLink {
user_id,
user_name: user.full_name(),
},
)?;
r.set_response(AdminCreatedPasswordResetLinkApi::new(reset_link)) r.set_response(AdminCreatedPasswordResetLinkApi::new(reset_link))
} }

View File

@ -4,6 +4,6 @@
pub mod admin_account_controller; pub mod admin_account_controller;
pub mod admin_keys_controller; pub mod admin_keys_controller;
pub mod admin_roles_controller;
pub mod admin_logs_controller; pub mod admin_logs_controller;
pub mod admin_roles_controller;
pub mod admin_users_controller; pub mod admin_users_controller;

View File

@ -13,7 +13,9 @@ use crate::api_data::new_call_signal::NewCallSignalAPI;
use crate::api_data::user_calls_config::UserCallsConfig; use crate::api_data::user_calls_config::UserCallsConfig;
use crate::controllers::user_ws_controller; use crate::controllers::user_ws_controller;
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::call_signal::{CallSignal, CloseCallStream, IceCandidate, NewUserCallSignal, SdpType, UserCallOfferRequest}; use crate::data::call_signal::{
CallSignal, CloseCallStream, IceCandidate, NewUserCallSignal, SdpType, UserCallOfferRequest,
};
use crate::data::config::conf; use crate::data::config::conf;
use crate::data::conversation::ConvID; use crate::data::conversation::ConvID;
use crate::data::error::{ExecError, Res}; use crate::data::error::{ExecError, Res};
@ -21,8 +23,8 @@ use crate::data::user::UserID;
use crate::data::user_ws_connection::{ActiveCall, UserWsConnection}; use crate::data::user_ws_connection::{ActiveCall, UserWsConnection};
use crate::data::user_ws_message::UserWsMessage; use crate::data::user_ws_message::UserWsMessage;
use crate::data::user_ws_request_handler::UserWsRequestHandler; use crate::data::user_ws_request_handler::UserWsRequestHandler;
use crate::helpers::{calls_helper, conversations_helper, events_helper};
use crate::helpers::events_helper::Event; use crate::helpers::events_helper::Event;
use crate::helpers::{calls_helper, conversations_helper, events_helper};
use crate::routes::RequestResult; use crate::routes::RequestResult;
impl UserWsRequestHandler { impl UserWsRequestHandler {
@ -63,7 +65,8 @@ impl UserWsRequestHandler {
/// Get call information, if available /// Get call information, if available
fn call_info(&mut self, call_id: &ConvID) -> Res<ActiveCall> { fn call_info(&mut self, call_id: &ConvID) -> Res<ActiveCall> {
let call = self.get_conn() let call = self
.get_conn()
.active_call .active_call
.clone() .clone()
.ok_or(ExecError::new("No call found !"))?; .ok_or(ExecError::new("No call found !"))?;
@ -76,7 +79,10 @@ impl UserWsRequestHandler {
} }
/// Update call information /// Update call information
fn update_call<F>(&mut self, update: F) -> Res where F: FnOnce(&mut ActiveCall) { fn update_call<F>(&mut self, update: F) -> Res
where
F: FnOnce(&mut ActiveCall),
{
self.update_conn(|conn| { self.update_conn(|conn| {
if let Some(call) = &mut conn.active_call { if let Some(call) = &mut conn.active_call {
update(call); update(call);
@ -95,8 +101,7 @@ pub async fn get_config(r: &mut UserWsRequestHandler) -> RequestResult {
} }
if conf().is_rtc_relay_enabled() { if conf().is_rtc_relay_enabled() {
if let Some(conf) = conf().rtc_relay.as_ref() if let Some(conf) = conf().rtc_relay.as_ref() {
{
return r.set_response(UserCallsConfig::new(conf)); return r.set_response(UserCallsConfig::new(conf));
} }
} }
@ -117,7 +122,11 @@ pub fn is_conversation_having_call(conv_id: &ConvID) -> bool {
}); });
if let Err(e) = res { if let Err(e) = res {
eprintln!("Failed to check if a conversation is having call! Conversation: {} / Error: {:#?}", conv_id.id(), e); eprintln!(
"Failed to check if a conversation is having call! Conversation: {} / Error: {:#?}",
conv_id.id(),
e
);
} }
found found
@ -149,12 +158,14 @@ pub async fn join_call(r: &mut UserWsRequestHandler) -> RequestResult {
make_user_leave_call(&call.conv_id, &conn).await?; make_user_leave_call(&call.conv_id, &conn).await?;
} }
} }
}; }
r.update_conn(|r| r.active_call = Some(ActiveCall { r.update_conn(|r| {
r.active_call = Some(ActiveCall {
conv_id, conv_id,
ready: false, ready: false,
}))?; })
})?;
// Propagate event // Propagate event
events_helper::propagate_event(Event::UserJoinedCall(conv_id, r.user_id()?)).await?; events_helper::propagate_event(Event::UserJoinedCall(conv_id, r.user_id()?)).await?;
@ -187,7 +198,10 @@ pub async fn get_members_list(r: &mut UserWsRequestHandler) -> RequestResult {
user_ws_controller::foreach_connection(|conn| { user_ws_controller::foreach_connection(|conn| {
if conn.is_having_call_with_conversation(&conv_id) { if conn.is_having_call_with_conversation(&conv_id) {
list.push(CallMemberInfo::new(conn.user_id(), &conn.active_call.as_ref().unwrap())); list.push(CallMemberInfo::new(
conn.user_id(),
&conn.active_call.as_ref().unwrap(),
));
} }
Ok(()) Ok(())
@ -215,10 +229,11 @@ pub async fn on_client_signal(r: &mut UserWsRequestHandler) -> RequestResult {
let signal = match sig_type.as_str() { let signal = match sig_type.as_str() {
"SDP" => { "SDP" => {
// Get SDP type // Get SDP type
let sdp_type = SdpType::from_str(data.get("type") let sdp_type = SdpType::from_str(
data.get("type")
.unwrap_or(&serde_json::Value::Null) .unwrap_or(&serde_json::Value::Null)
.as_str() .as_str()
.ok_or(ExecError::boxed_new("Missing SDP type !"))? .ok_or(ExecError::boxed_new("Missing SDP type !"))?,
)?; )?;
let sdp = data let sdp = data
@ -237,7 +252,9 @@ pub async fn on_client_signal(r: &mut UserWsRequestHandler) -> RequestResult {
.get("candidate") .get("candidate")
.unwrap_or(&serde_json::Value::Null) .unwrap_or(&serde_json::Value::Null)
.as_str() .as_str()
.ok_or(ExecError::boxed_new("Failed to get candidate message data!"))? .ok_or(ExecError::boxed_new(
"Failed to get candidate message data!",
))?
.to_string(); .to_string();
let sdp_m_line_index = data let sdp_m_line_index = data
@ -253,14 +270,16 @@ pub async fn on_client_signal(r: &mut UserWsRequestHandler) -> RequestResult {
.ok_or(ExecError::boxed_new("Failed to get sdpMid message data!"))? .ok_or(ExecError::boxed_new("Failed to get sdpMid message data!"))?
.to_string(); .to_string();
let parsed_candidate: SdpAttribute = candidate.trim().parse()?; let parsed_candidate: SdpAttribute = candidate.trim().parse()?;
CallSignal::Candidate(IceCandidate { CallSignal::Candidate(
IceCandidate {
candidate, candidate,
sdp_m_line_index, sdp_m_line_index,
sdp_mid, sdp_mid,
}, parsed_candidate) },
parsed_candidate,
)
} }
_ => { _ => {
@ -271,10 +290,15 @@ pub async fn on_client_signal(r: &mut UserWsRequestHandler) -> RequestResult {
events_helper::propagate_event(Event::NewUserCallSignal(NewUserCallSignal { events_helper::propagate_event(Event::NewUserCallSignal(NewUserCallSignal {
call_hash: gen_call_hash(&call_id, &peer_id), call_hash: gen_call_hash(&call_id, &peer_id),
user_id: if r.user_id_ref()? == &peer_id { None } else { Some(r.user_id()?) }, user_id: if r.user_id_ref()? == &peer_id {
None
} else {
Some(r.user_id()?)
},
signal, signal,
raw_data: r.post_string("data")?, raw_data: r.post_string("data")?,
})).await?; }))
.await?;
r.success("Signal sent") r.success("Signal sent")
} }
@ -288,7 +312,12 @@ pub async fn mark_user_ready(r: &mut UserWsRequestHandler) -> Res {
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|c| c.user_id() != &user_id && c.is_having_call_with_conversation(&call_id), |c| c.user_id() != &user_id && c.is_having_call_with_conversation(&call_id),
|_| UserWsMessage::no_id_message("call_peer_ready", CallPeerReadyAPI::new(&call_id, r.user_id_ref()?)), |_| {
UserWsMessage::no_id_message(
"call_peer_ready",
CallPeerReadyAPI::new(&call_id, r.user_id_ref()?),
)
},
)?; )?;
r.success("Information propagated.") r.success("Information propagated.")
@ -308,7 +337,8 @@ pub async fn request_offer(r: &mut UserWsRequestHandler) -> Res {
events_helper::propagate_event(Event::UserRequestedCallOffer(UserCallOfferRequest { events_helper::propagate_event(Event::UserRequestedCallOffer(UserCallOfferRequest {
call_hash: gen_call_hash(&call_id, &peer_id), call_hash: gen_call_hash(&call_id, &peer_id),
user_id: r.user_id()?, user_id: r.user_id()?,
})).await?; }))
.await?;
r.success("Request sent") r.success("Request sent")
} }
@ -325,7 +355,12 @@ pub async fn stop_streaming(r: &mut UserWsRequestHandler) -> Res {
// Notify all other users // Notify all other users
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|c| c.is_having_call_with_conversation(&call_id) && c.user_id() != &user_id, |c| c.is_having_call_with_conversation(&call_id) && c.user_id() != &user_id,
|_| UserWsMessage::no_id_message("call_peer_interrupted_streaming", CallPeerInterruptedStreamingAPI::new(&call_id, &user_id)), |_| {
UserWsMessage::no_id_message(
"call_peer_interrupted_streaming",
CallPeerInterruptedStreamingAPI::new(&call_id, &user_id),
)
},
)?; )?;
} }
@ -333,42 +368,50 @@ pub async fn stop_streaming(r: &mut UserWsRequestHandler) -> Res {
events_helper::propagate_event(Event::CloseCallStream(CloseCallStream { events_helper::propagate_event(Event::CloseCallStream(CloseCallStream {
call_hash: gen_call_hash(&call_id, &user_id), call_hash: gen_call_hash(&call_id, &user_id),
peer_id: None, peer_id: None,
})).await?; }))
.await?;
r.success("ok") r.success("ok")
} }
/// Make the user leave the call /// Make the user leave the call
pub async fn make_user_leave_call(conv_id: &ConvID, connection: &UserWsConnection) -> Res { pub async fn make_user_leave_call(conv_id: &ConvID, connection: &UserWsConnection) -> Res {
connection.clone().replace(|c| c.active_call = None); connection.clone().replace(|c| c.active_call = None);
// Notify user (if possible) // Notify user (if possible)
if connection.session.connected() { if connection.session.connected() {
user_ws_controller::send_to_client(connection, &UserWsMessage::no_id_message("call_closed", conv_id.id())?)?; user_ws_controller::send_to_client(
connection,
&UserWsMessage::no_id_message("call_closed", conv_id.id())?,
)?;
} }
// Close main stream (sender) // Close main stream (sender)
events_helper::propagate_event(Event::CloseCallStream(CloseCallStream { events_helper::propagate_event(Event::CloseCallStream(CloseCallStream {
call_hash: gen_call_hash(&conv_id, connection.user_id()), call_hash: gen_call_hash(&conv_id, connection.user_id()),
peer_id: None, peer_id: None,
})).await?; }))
.await?;
// Close receiver streams (other users streams) // Close receiver streams (other users streams)
for peer_conn in user_ws_controller::get_all_connections()? { for peer_conn in user_ws_controller::get_all_connections()? {
if peer_conn.is_having_call_with_conversation(conv_id) && peer_conn.user_id() != connection.user_id() { if peer_conn.is_having_call_with_conversation(conv_id)
&& peer_conn.user_id() != connection.user_id()
{
events_helper::propagate_event(Event::CloseCallStream(CloseCallStream { events_helper::propagate_event(Event::CloseCallStream(CloseCallStream {
call_hash: gen_call_hash(&conv_id, peer_conn.user_id()), call_hash: gen_call_hash(&conv_id, peer_conn.user_id()),
peer_id: Some(connection.user_id().clone()), peer_id: Some(connection.user_id().clone()),
})).await?; }))
.await?;
}
} }
};
// Create a notification // Create a notification
events_helper::propagate_event(Event::UserLeftCall( events_helper::propagate_event(Event::UserLeftCall(
conv_id.clone(), conv_id.clone(),
connection.user_id().clone(), connection.user_id().clone(),
)).await?; ))
.await?;
Ok(()) Ok(())
} }
@ -379,14 +422,24 @@ pub async fn handle_event(e: &events_helper::Event) -> Res {
Event::UserJoinedCall(conv_id, user_id) => { Event::UserJoinedCall(conv_id, user_id) => {
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|c| c.is_having_call_with_conversation(conv_id) && c.user_id() != user_id, |c| c.is_having_call_with_conversation(conv_id) && c.user_id() != user_id,
|_| UserWsMessage::no_id_message("user_joined_call", JoinedCallMessage::new(conv_id, user_id)), |_| {
UserWsMessage::no_id_message(
"user_joined_call",
JoinedCallMessage::new(conv_id, user_id),
)
},
)?; )?;
} }
Event::UserLeftCall(conv_id, user_id) => { Event::UserLeftCall(conv_id, user_id) => {
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|c| c.is_having_call_with_conversation(conv_id), |c| c.is_having_call_with_conversation(conv_id),
|_| UserWsMessage::no_id_message("user_left_call", LeftCallMessage::new(conv_id, user_id)), |_| {
UserWsMessage::no_id_message(
"user_left_call",
LeftCallMessage::new(conv_id, user_id),
)
},
)?; )?;
} }
@ -409,13 +462,17 @@ pub async fn handle_event(e: &events_helper::Event) -> Res {
let target_user = UserID::new(msg.peer_id.parse::<u64>()?); let target_user = UserID::new(msg.peer_id.parse::<u64>()?);
let target_user = match target_user.is_valid() { let target_user = match target_user.is_valid() {
true => target_user, true => target_user,
false => peer_id.clone() false => peer_id.clone(),
}; };
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|c| c.user_id() == target_user && c.is_having_call_with_conversation(&call_id), |c| c.user_id() == target_user && c.is_having_call_with_conversation(&call_id),
|_| UserWsMessage::no_id_message("new_call_signal", NewCallSignalAPI::new(&call_id, &peer_id, &msg.data)?), |_| {
UserWsMessage::no_id_message(
"new_call_signal",
NewCallSignalAPI::new(&call_id, &peer_id, &msg.data)?,
)
},
)?; )?;
} }
@ -426,7 +483,7 @@ pub async fn handle_event(e: &events_helper::Event) -> Res {
if let Some(call) = &f.active_call { if let Some(call) = &f.active_call {
make_user_leave_call(&call.conv_id, &f).await?; make_user_leave_call(&call.conv_id, &f).await?;
} }
}; }
} }
// Call active call of user (if any) // Call active call of user (if any)

View File

@ -5,7 +5,6 @@
use crate::api_data::comment_api::CommentAPI; use crate::api_data::comment_api::CommentAPI;
use crate::api_data::res_create_comment::ResCreateComment; use crate::api_data::res_create_comment::ResCreateComment;
use crate::constants::PATH_COMMENTS_IMAGES; use crate::constants::PATH_COMMENTS_IMAGES;
use crate::routes::RequestResult;
use crate::controllers::user_ws_controller; use crate::controllers::user_ws_controller;
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::comment::Comment; use crate::data::comment::Comment;
@ -14,8 +13,9 @@ use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::notification::NotifEventType; use crate::data::notification::NotifEventType;
use crate::data::post::PostAccessLevel; use crate::data::post::PostAccessLevel;
use crate::data::user_ws_message::UserWsMessage; use crate::data::user_ws_message::UserWsMessage;
use crate::helpers::{comments_helper, events_helper, notifications_helper, posts_helper};
use crate::helpers::events_helper::Event; use crate::helpers::events_helper::Event;
use crate::helpers::{comments_helper, events_helper, notifications_helper, posts_helper};
use crate::routes::RequestResult;
use crate::utils::date_utils::time; use crate::utils::date_utils::time;
use crate::utils::string_utils::remove_html_nodes; use crate::utils::string_utils::remove_html_nodes;
@ -30,16 +30,12 @@ pub async fn create(r: &mut HttpRequestHandler) -> RequestResult {
let (content, image) = if r.has_file("image") { let (content, image) = if r.has_file("image") {
( (
remove_html_nodes(&r.post_string_opt("content", 0, false)?), remove_html_nodes(&r.post_string_opt("content", 0, false)?),
Some(r.save_post_image("image", PATH_COMMENTS_IMAGES, 700, 700)?) Some(r.save_post_image("image", PATH_COMMENTS_IMAGES, 700, 700)?),
) )
} else { } else {
( (r.post_content("content", 2, true)?, None)
r.post_content("content", 2, true)?,
None
)
}; };
let comment = Comment { let comment = Comment {
id: 0, id: 0,
time_sent: time(), time_sent: time(),
@ -52,10 +48,16 @@ pub async fn create(r: &mut HttpRequestHandler) -> RequestResult {
let comment_id = comments_helper::create(&comment).await?; let comment_id = comments_helper::create(&comment).await?;
// Create notifications // Create notifications
notifications_helper::create_post_notification(&r.user_id()?, post.id, NotifEventType::COMMENT_CREATED).await?; notifications_helper::create_post_notification(
&r.user_id()?,
post.id,
NotifEventType::COMMENT_CREATED,
)
.await?;
// Remove notifications targeting current user about the post // Remove notifications targeting current user about the post
notifications_helper::delete_all_post_notifications_targeting_user(r.user_id_ref()?, post.id).await?; notifications_helper::delete_all_post_notifications_targeting_user(r.user_id_ref()?, post.id)
.await?;
r.set_response(ResCreateComment::new(comment_id)) r.set_response(ResCreateComment::new(comment_id))
} }
@ -92,30 +94,31 @@ pub async fn handle_event(e: &events_helper::Event) -> Res {
Event::NewComment(comment) => { Event::NewComment(comment) => {
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|c| c.posts.contains(&comment.post_id), |c| c.posts.contains(&comment.post_id),
|c| UserWsMessage::no_id_message( |c| {
UserWsMessage::no_id_message(
"new_comment", "new_comment",
CommentAPI::new(comment, &Some(c.user_id().clone()))?, CommentAPI::new(comment, &Some(c.user_id().clone()))?,
) )
},
)?; )?;
} }
Event::UpdatedComment(comment) => { Event::UpdatedComment(comment) => {
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|c| c.posts.contains(&comment.post_id), |c| c.posts.contains(&comment.post_id),
|c| UserWsMessage::no_id_message( |c| {
UserWsMessage::no_id_message(
"comment_updated", "comment_updated",
CommentAPI::new(comment, &Some(c.user_id().clone()))?, CommentAPI::new(comment, &Some(c.user_id().clone()))?,
) )
},
)?; )?;
} }
Event::DeletedComment(comment) => { Event::DeletedComment(comment) => {
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|c| c.posts.contains(&comment.post_id), |c| c.posts.contains(&comment.post_id),
|_| UserWsMessage::no_id_message( |_| UserWsMessage::no_id_message("comment_deleted", comment.id.clone()),
"comment_deleted",
comment.id.clone(),
),
)?; )?;
} }

View File

@ -12,7 +12,12 @@ use crate::api_data::res_count_unread_conversations::ResultCountUnreadConversati
use crate::api_data::res_create_conversation::ResCreateConversation; use crate::api_data::res_create_conversation::ResCreateConversation;
use crate::api_data::res_find_private_conversations::ResFindPrivateConversations; use crate::api_data::res_find_private_conversations::ResFindPrivateConversations;
use crate::api_data::user_is_writing_message_in_conversation::UserIsWritingMessageInConversation; use crate::api_data::user_is_writing_message_in_conversation::UserIsWritingMessageInConversation;
use crate::constants::conversations::{ALLOWED_CONVERSATION_FILES_TYPES, CONVERSATION_FILES_MAX_SIZE, MAX_CONV_IMAGE_MESSAGE_HEIGHT, MAX_CONV_IMAGE_MESSAGE_WIDTH, MAX_CONV_LOGO_HEIGHT, MAX_CONV_LOGO_WIDTH, MAX_CONV_MESSAGE_THUMBNAIL_HEIGHT, MAX_CONV_MESSAGE_THUMBNAIL_WIDTH, MAX_CONVERSATION_MESSAGE_LENGTH, MIN_CONVERSATION_MESSAGE_LENGTH}; use crate::constants::conversations::{
ALLOWED_CONVERSATION_FILES_TYPES, CONVERSATION_FILES_MAX_SIZE, MAX_CONVERSATION_MESSAGE_LENGTH,
MAX_CONV_IMAGE_MESSAGE_HEIGHT, MAX_CONV_IMAGE_MESSAGE_WIDTH, MAX_CONV_LOGO_HEIGHT,
MAX_CONV_LOGO_WIDTH, MAX_CONV_MESSAGE_THUMBNAIL_HEIGHT, MAX_CONV_MESSAGE_THUMBNAIL_WIDTH,
MIN_CONVERSATION_MESSAGE_LENGTH,
};
use crate::controllers::user_ws_controller; use crate::controllers::user_ws_controller;
use crate::data::base_request_handler::{BaseRequestHandler, RequestValue}; use crate::data::base_request_handler::{BaseRequestHandler, RequestValue};
use crate::data::conversation::NewConversationSettings; use crate::data::conversation::NewConversationSettings;
@ -23,8 +28,8 @@ use crate::data::new_conversation::NewConversation;
use crate::data::new_conversation_message::NewConversationMessage; use crate::data::new_conversation_message::NewConversationMessage;
use crate::data::user_ws_message::UserWsMessage; use crate::data::user_ws_message::UserWsMessage;
use crate::data::user_ws_request_handler::UserWsRequestHandler; use crate::data::user_ws_request_handler::UserWsRequestHandler;
use crate::helpers::{conversations_helper, events_helper};
use crate::helpers::events_helper::Event; use crate::helpers::events_helper::Event;
use crate::helpers::{conversations_helper, events_helper};
use crate::routes::RequestResult; use crate::routes::RequestResult;
use crate::utils::string_utils::remove_html_nodes; use crate::utils::string_utils::remove_html_nodes;
use crate::utils::user_data_utils::{delete_user_data_file_if_exists, user_data_path}; use crate::utils::user_data_utils::{delete_user_data_file_if_exists, user_data_path};
@ -57,7 +62,11 @@ pub async fn create(r: &mut HttpRequestHandler) -> RequestResult {
pub async fn get_list(r: &mut HttpRequestHandler) -> RequestResult { pub async fn get_list(r: &mut HttpRequestHandler) -> RequestResult {
let list = conversations_helper::get_list_user(&r.user_id()?)?; let list = conversations_helper::get_list_user(&r.user_id()?)?;
r.set_response(list.iter().map(|c| ConversationAPI::new(c)).collect::<Vec<ConversationAPI>>()) r.set_response(
list.iter()
.map(|c| ConversationAPI::new(c))
.collect::<Vec<ConversationAPI>>(),
)
} }
/// Get information about a single conversation /// Get information about a single conversation
@ -84,15 +93,19 @@ pub async fn update_settings(r: &mut HttpRequestHandler) -> RequestResult {
// Change moderator settings // Change moderator settings
if r.has_post_parameter("name") if r.has_post_parameter("name")
&& r.has_post_parameter("canEveryoneAddMembers") && r.has_post_parameter("canEveryoneAddMembers")
&& r.has_post_parameter("color") { && r.has_post_parameter("color")
{
if !conv_membership.is_admin { if !conv_membership.is_admin {
r.forbidden("You are not allowed to perform changes on this conversation!".to_string())?; r.forbidden(
"You are not allowed to perform changes on this conversation!".to_string(),
)?;
} }
let new_settings = NewConversationSettings { let new_settings = NewConversationSettings {
conv_id: conv_membership.conv_id, conv_id: conv_membership.conv_id,
name: r.post_string_optional("name").map(|s| remove_html_nodes(&s)), name: r
.post_string_optional("name")
.map(|s| remove_html_nodes(&s)),
color: r.post_color_opt("color")?, color: r.post_color_opt("color")?,
can_everyone_add_members: r.post_bool("canEveryoneAddMembers")?, can_everyone_add_members: r.post_bool("canEveryoneAddMembers")?,
}; };
@ -108,7 +121,12 @@ pub async fn change_image(r: &mut HttpRequestHandler) -> RequestResult {
let conv_membership = r.post_conv_admin("convID")?; let conv_membership = r.post_conv_admin("convID")?;
let conv = conversations_helper::get_single(conv_membership.conv_id)?; let conv = conversations_helper::get_single(conv_membership.conv_id)?;
let new_image = r.save_post_image("file", "conv-image", MAX_CONV_LOGO_WIDTH, MAX_CONV_LOGO_HEIGHT)?; let new_image = r.save_post_image(
"file",
"conv-image",
MAX_CONV_LOGO_WIDTH,
MAX_CONV_LOGO_HEIGHT,
)?;
conversations_helper::remove_conversation_image(&conv)?; conversations_helper::remove_conversation_image(&conv)?;
@ -135,7 +153,9 @@ pub async fn add_member(r: &mut HttpRequestHandler) -> RequestResult {
let user_to_add = r.post_user_id("userID")?; let user_to_add = r.post_user_id("userID")?;
if conv.is_managed() { if conv.is_managed() {
r.bad_request("This conversation is managed, you can not manually change its members!".to_string())?; r.bad_request(
"This conversation is managed, you can not manually change its members!".to_string(),
)?;
} }
if !conv.can_user_add_members(r.user_id_ref()?) { if !conv.can_user_add_members(r.user_id_ref()?) {
@ -146,7 +166,8 @@ pub async fn add_member(r: &mut HttpRequestHandler) -> RequestResult {
r.bad_request("This user is already a member of this conversation!".to_string())?; r.bad_request("This user is already a member of this conversation!".to_string())?;
} }
conversations_helper::add_member(conv.id, &user_to_add, true, false, Some(r.user_id_ref()?)).await?; conversations_helper::add_member(conv.id, &user_to_add, true, false, Some(r.user_id_ref()?))
.await?;
r.success("The user was added to the conversation!") r.success("The user was added to the conversation!")
} }
@ -159,7 +180,9 @@ pub async fn set_admin(r: &mut HttpRequestHandler) -> RequestResult {
let set_admin = r.post_bool("setAdmin")?; let set_admin = r.post_bool("setAdmin")?;
if conv.is_managed() { if conv.is_managed() {
r.bad_request("This conversation is managed, you can not manually change its members!".to_string())?; r.bad_request(
"This conversation is managed, you can not manually change its members!".to_string(),
)?;
} }
if !conv.can_mark_other_users_admin(r.user_id_ref()?) { if !conv.can_mark_other_users_admin(r.user_id_ref()?) {
@ -183,7 +206,9 @@ pub async fn remove_member(r: &mut HttpRequestHandler) -> RequestResult {
let user_to_remove = r.post_user_id("userID")?; let user_to_remove = r.post_user_id("userID")?;
if conv.is_managed() { if conv.is_managed() {
r.bad_request("This conversation is managed, you can not manually change its members!".to_string())?; r.bad_request(
"This conversation is managed, you can not manually change its members!".to_string(),
)?;
} }
if !conv.can_user_remove_members(r.user_id_ref()?) { if !conv.can_user_remove_members(r.user_id_ref()?) {
@ -254,7 +279,8 @@ pub async fn refresh_single(r: &mut HttpRequestHandler) -> RequestResult {
conv.conv_id, conv.conv_id,
r.user_id_ref()?, r.user_id_ref()?,
&messages.last().unwrap(), &messages.last().unwrap(),
).await?; )
.await?;
} }
r.set_response(ConversationMessageAPI::for_list(&messages)) r.set_response(ConversationMessageAPI::for_list(&messages))
@ -270,7 +296,7 @@ pub async fn get_older_messages(r: &mut HttpRequestHandler) -> RequestResult {
let limit = match limit { let limit = match limit {
0 => 1, 0 => 1,
1..=60 => limit, 1..=60 => limit,
_ => 60 _ => 60,
}; };
let messages = conversations_helper::get_older_messages(conv.conv_id, max_id, limit)?; let messages = conversations_helper::get_older_messages(conv.conv_id, max_id, limit)?;
@ -285,7 +311,6 @@ pub async fn send_message(r: &mut HttpRequestHandler) -> RequestResult {
// Get associated file // Get associated file
let file = match r.post_parameter_opt("file") { let file = match r.post_parameter_opt("file") {
Some(RequestValue::File(file)) => { Some(RequestValue::File(file)) => {
// File name // File name
let mut name = file.name.to_string(); let mut name = file.name.to_string();
@ -299,7 +324,7 @@ pub async fn send_message(r: &mut HttpRequestHandler) -> RequestResult {
// Check for thumbnail // Check for thumbnail
let mut thumbnail = match r.has_file("thumbnail") { let mut thumbnail = match r.has_file("thumbnail") {
false => None, false => None,
true => Some("thumbnail".to_string()) true => Some("thumbnail".to_string()),
}; };
let path; let path;
@ -314,56 +339,58 @@ pub async fn send_message(r: &mut HttpRequestHandler) -> RequestResult {
thumbnail = Some("file".to_string()); thumbnail = Some("file".to_string());
} }
path = r.save_post_image("file", "conversation", MAX_CONV_IMAGE_MESSAGE_WIDTH, MAX_CONV_IMAGE_MESSAGE_HEIGHT)?; path = r.save_post_image(
"file",
"conversation",
MAX_CONV_IMAGE_MESSAGE_WIDTH,
MAX_CONV_IMAGE_MESSAGE_HEIGHT,
)?;
mime_type = "image/png".to_string(); mime_type = "image/png".to_string();
name = "picture.png".to_string(); name = "picture.png".to_string();
} }
// PDF // PDF
else if mime_type.eq("application/pdf") { else if mime_type.eq("application/pdf") {
path = r.save_post_pdf("file", "conversation")?; path = r.save_post_pdf("file", "conversation")?;
} }
// MP3 // MP3
else if mime_type.eq("audio/mpeg") { else if mime_type.eq("audio/mpeg") {
path = r.save_post_mp3("file", "conversation")?; path = r.save_post_mp3("file", "conversation")?;
} }
// MP4 // MP4
else if mime_type.eq("video/mp4") || mime_type.eq("video/quicktime") { else if mime_type.eq("video/mp4") || mime_type.eq("video/quicktime") {
path = r.save_post_mp4("file", "conversation")?; path = r.save_post_mp4("file", "conversation")?;
} }
// ZIP archive // ZIP archive
else if mime_type.eq("application/zip") { else if mime_type.eq("application/zip") {
path = r.save_post_zip("file", "conversation")?; path = r.save_post_zip("file", "conversation")?;
} }
// Office document // Office document
else if mime_type.starts_with("application/") { else if mime_type.starts_with("application/") {
path = r.save_post_office_doc("file", "conversation")?; path = r.save_post_office_doc("file", "conversation")?;
} }
// Text files // Text files
else { else {
path = r.save_post_txt_doc("file", "conversation")?; path = r.save_post_txt_doc("file", "conversation")?;
} }
// Attempt to save thumbnail, if it fails we can not save message // Attempt to save thumbnail, if it fails we can not save message
let thumbnail = match thumbnail { let thumbnail = match thumbnail {
None => None, None => None,
Some(f) => Some(match r.save_post_image(&f, "conversations-thumb", MAX_CONV_MESSAGE_THUMBNAIL_WIDTH, MAX_CONV_MESSAGE_THUMBNAIL_HEIGHT) { Some(f) => Some(match r.save_post_image(
&f,
"conversations-thumb",
MAX_CONV_MESSAGE_THUMBNAIL_WIDTH,
MAX_CONV_MESSAGE_THUMBNAIL_HEIGHT,
) {
Ok(s) => Ok(s), Ok(s) => Ok(s),
Err(e) => { Err(e) => {
eprintln!("Failed to save conversation thumbnail! {:#?}", e); eprintln!("Failed to save conversation thumbnail! {:#?}", e);
delete_user_data_file_if_exists(&path).unwrap(); delete_user_data_file_if_exists(&path).unwrap();
Err(e) Err(e)
} }
}?) }?),
}; };
Some(ConversationMessageFile { Some(ConversationMessageFile {
path: path.clone(), path: path.clone(),
size: std::fs::metadata(user_data_path(path.as_ref()))?.len(), size: std::fs::metadata(user_data_path(path.as_ref()))?.len(),
@ -394,7 +421,8 @@ pub async fn send_message(r: &mut HttpRequestHandler) -> RequestResult {
message, message,
file, file,
server_message: None, server_message: None,
}).await?; })
.await?;
r.success("Conversation message was successfully sent!") r.success("Conversation message was successfully sent!")
} }
@ -419,10 +447,13 @@ pub async fn delete_conversation(r: &mut HttpRequestHandler) -> RequestResult {
let conv = conversations_helper::get_single(conv_membership.conv_id)?; let conv = conversations_helper::get_single(conv_membership.conv_id)?;
if conv.is_managed() { if conv.is_managed() {
r.bad_request("This conversation is managed, it can not be deleted by this way!".to_string())?; r.bad_request(
"This conversation is managed, it can not be deleted by this way!".to_string(),
)?;
} }
conversations_helper::remove_user_from_conversation(&r.user_id()?, &conv, r.user_id_ref()?).await?; conversations_helper::remove_user_from_conversation(&r.user_id()?, &conv, r.user_id_ref()?)
.await?;
r.success("The conversation has been deleted") r.success("The conversation has been deleted")
} }
@ -469,9 +500,11 @@ pub async fn member_is_writing(r: &mut UserWsRequestHandler) -> RequestResult {
let conv_id = r.post_registered_conv_id("convID")?; let conv_id = r.post_registered_conv_id("convID")?;
// Propagate event // Propagate event
events_helper::propagate_event( events_helper::propagate_event(Event::UserIsWritingMessageInConversation(
Event::UserIsWritingMessageInConversation(r.user_id()?, conv_id) r.user_id()?,
).await?; conv_id,
))
.await?;
r.ok() r.ok()
} }
@ -496,14 +529,24 @@ pub async fn handle_event(e: &events_helper::Event) -> Res {
Event::UserIsWritingMessageInConversation(user_id, conv_id) => { Event::UserIsWritingMessageInConversation(user_id, conv_id) => {
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|s| s.conversations.contains(conv_id) && s.user_id() != user_id, |s| s.conversations.contains(conv_id) && s.user_id() != user_id,
|_| UserWsMessage::no_id_message("writing_message_in_conv", UserIsWritingMessageInConversation::new(user_id, *conv_id)), |_| {
UserWsMessage::no_id_message(
"writing_message_in_conv",
UserIsWritingMessageInConversation::new(user_id, *conv_id),
)
},
)?; )?;
} }
Event::NewConversationMessage(msg) => { Event::NewConversationMessage(msg) => {
for conn in user_ws_controller::send_message_to_specific_connections( for conn in user_ws_controller::send_message_to_specific_connections(
|f| f.conversations.contains(&msg.conv_id), |f| f.conversations.contains(&msg.conv_id),
|_| UserWsMessage::no_id_message("new_conv_message", ConversationMessageAPI::new(msg)), |_| {
UserWsMessage::no_id_message(
"new_conv_message",
ConversationMessageAPI::new(msg),
)
},
)? { )? {
conversations_helper::mark_user_seen(msg.conv_id, conn.user_id(), msg).await?; conversations_helper::mark_user_seen(msg.conv_id, conn.user_id(), msg).await?;
} }
@ -512,14 +555,24 @@ pub async fn handle_event(e: &events_helper::Event) -> Res {
Event::UpdatedConversationMessage(msg) => { Event::UpdatedConversationMessage(msg) => {
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|f| f.conversations.contains(&msg.conv_id), |f| f.conversations.contains(&msg.conv_id),
|_| UserWsMessage::no_id_message("updated_conv_message", ConversationMessageAPI::new(msg)), |_| {
UserWsMessage::no_id_message(
"updated_conv_message",
ConversationMessageAPI::new(msg),
)
},
)?; )?;
} }
Event::DeleteConversationMessage(msg) => { Event::DeleteConversationMessage(msg) => {
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|f| f.conversations.contains(&msg.conv_id), |f| f.conversations.contains(&msg.conv_id),
|_| UserWsMessage::no_id_message("deleted_conv_message", ConversationMessageAPI::new(msg)), |_| {
UserWsMessage::no_id_message(
"deleted_conv_message",
ConversationMessageAPI::new(msg),
)
},
)?; )?;
} }
@ -527,7 +580,12 @@ pub async fn handle_event(e: &events_helper::Event) -> Res {
// Notify users // Notify users
user_ws_controller::send_message_to_specific_connections( user_ws_controller::send_message_to_specific_connections(
|f| f.conversations.contains(conv_id), |f| f.conversations.contains(conv_id),
|_| UserWsMessage::no_id_message("removed_user_from_conv", RemovedUserFromConversationMessage::new(user_id, *conv_id)), |_| {
UserWsMessage::no_id_message(
"removed_user_from_conv",
RemovedUserFromConversationMessage::new(user_id, *conv_id),
)
},
)?; )?;
// Disconnect user from conversation // Disconnect user from conversation

View File

@ -20,7 +20,10 @@ pub async fn get_list_groups(r: &mut HttpRequestHandler) -> RequestResult {
let mut list = vec![]; let mut list = vec![];
for group in &conf().forez_groups { for group in &conf().forez_groups {
list.push(GroupApi::new(&groups_helper::get_info(group)?, r.user_id_opt())?); list.push(GroupApi::new(
&groups_helper::get_info(group)?,
r.user_id_opt(),
)?);
} }
r.set_response(list) r.set_response(list)

View File

@ -21,13 +21,9 @@ pub async fn get_list(r: &mut HttpRequestHandler) -> RequestResult {
pub async fn get_single_friendship_info(r: &mut HttpRequestHandler) -> RequestResult { pub async fn get_single_friendship_info(r: &mut HttpRequestHandler) -> RequestResult {
let friend_id = r.post_user_id("friendID")?; let friend_id = r.post_user_id("friendID")?;
let info = friends_helper::GetFriendsQuery::new(&r.user_id()?) let info = friends_helper::GetFriendsQuery::new(&r.user_id()?).get_single_friend(&friend_id);
.get_single_friend(&friend_id);
let info = r.ok_or_not_found( let info = r.ok_or_not_found(info, "The friendship was not found!")?;
info,
"The friendship was not found!",
)?;
r.set_response(FriendAPI::new(&info)) r.set_response(FriendAPI::new(&info))
} }
@ -48,7 +44,12 @@ pub async fn get_other_user_list(r: &mut HttpRequestHandler) -> RequestResult {
.set_only_accepted(true) .set_only_accepted(true)
.exec()?; .exec()?;
r.set_response(friends.iter().map(|f| f.friend_id.id()).collect::<Vec<u64>>()) r.set_response(
friends
.iter()
.map(|f| f.friend_id.id())
.collect::<Vec<u64>>(),
)
} }
/// Get the status of a friendship /// Get the status of a friendship
@ -85,7 +86,9 @@ pub async fn send_request(r: &mut HttpRequestHandler) -> RequestResult {
notifications_helper::create_friends_notification( notifications_helper::create_friends_notification(
r.user_id_ref()?, r.user_id_ref()?,
&friend_id, &friend_id,
NotifEventType::SENT_FRIEND_REQUEST).await?; NotifEventType::SENT_FRIEND_REQUEST,
)
.await?;
r.success("The friendship request was successfully sent!") r.success("The friendship request was successfully sent!")
} }
@ -101,7 +104,8 @@ pub async fn cancel_request(r: &mut HttpRequestHandler) -> RequestResult {
friends_helper::remove_request(&r.user_id()?, &friend_id)?; friends_helper::remove_request(&r.user_id()?, &friend_id)?;
// Delete related notifications // Delete related notifications
notifications_helper::delete_all_related_with_friendship_request(r.user_id_ref()?, &friend_id).await?; notifications_helper::delete_all_related_with_friendship_request(r.user_id_ref()?, &friend_id)
.await?;
r.success("Friendship request removed!") r.success("Friendship request removed!")
} }
@ -123,9 +127,10 @@ pub async fn respond_request(r: &mut HttpRequestHandler) -> RequestResult {
&friend_id, &friend_id,
match accept { match accept {
true => NotifEventType::ACCEPTED_FRIEND_REQUEST, true => NotifEventType::ACCEPTED_FRIEND_REQUEST,
false => NotifEventType::REJECTED_FRIEND_REQUEST false => NotifEventType::REJECTED_FRIEND_REQUEST,
}, },
).await?; )
.await?;
r.set_response("Response to the friendship request successfully saved!") r.set_response("Response to the friendship request successfully saved!")
} }
@ -137,7 +142,8 @@ pub async fn remove_friend(r: &mut HttpRequestHandler) -> RequestResult {
friends_helper::remove_friendship(r.user_id_ref()?, &friend_id)?; friends_helper::remove_friendship(r.user_id_ref()?, &friend_id)?;
// Delete any related notification // Delete any related notification
notifications_helper::delete_all_related_with_friendship_request(r.user_id_ref()?, &friend_id).await?; notifications_helper::delete_all_related_with_friendship_request(r.user_id_ref()?, &friend_id)
.await?;
r.success("The friend was removed from the list!") r.success("The friend was removed from the list!")
} }

View File

@ -12,22 +12,29 @@ use crate::api_data::res_create_conversation_for_group::ResCreateConversationFor
use crate::api_data::res_create_group::GroupCreationResult; use crate::api_data::res_create_group::GroupCreationResult;
use crate::constants::{DEFAULT_GROUP_LOGO, PATH_GROUPS_LOGOS}; use crate::constants::{DEFAULT_GROUP_LOGO, PATH_GROUPS_LOGOS};
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::conversation::Conversation;
use crate::data::error::Res; use crate::data::error::Res;
use crate::data::group::{Group, GroupAccessLevel, GroupPostsCreationLevel, GroupRegistrationLevel, GroupVisibilityLevel}; use crate::data::group::{
Group, GroupAccessLevel, GroupPostsCreationLevel, GroupRegistrationLevel, GroupVisibilityLevel,
};
use crate::data::group_id::GroupID; use crate::data::group_id::GroupID;
use crate::data::group_member::{GroupMember, GroupMembershipLevel}; use crate::data::group_member::{GroupMember, GroupMembershipLevel};
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::new_group::NewGroup; use crate::data::new_group::NewGroup;
use crate::data::notification::NotifEventType; use crate::data::notification::NotifEventType;
use crate::helpers::{conversations_helper, groups_helper, notifications_helper, virtual_directory_helper};
use crate::helpers::virtual_directory_helper::VirtualDirType; use crate::helpers::virtual_directory_helper::VirtualDirType;
use crate::helpers::{
conversations_helper, groups_helper, notifications_helper, virtual_directory_helper,
};
use crate::routes::RequestResult; use crate::routes::RequestResult;
use crate::utils::date_utils::time; use crate::utils::date_utils::time;
use crate::data::conversation::Conversation;
impl HttpRequestHandler { impl HttpRequestHandler {
/// Get membership level for a conversation /// Get membership level for a conversation
pub fn post_group_membership_level_for_conversation(&mut self, name: &str) -> Res<GroupMembershipLevel> { pub fn post_group_membership_level_for_conversation(
&mut self,
name: &str,
) -> Res<GroupMembershipLevel> {
let level = GroupMembershipLevel::from_api(&self.post_string(name)?); let level = GroupMembershipLevel::from_api(&self.post_string(name)?);
if !level.is_at_least_member() { if !level.is_at_least_member() {
@ -43,7 +50,10 @@ impl HttpRequestHandler {
let conv = conversations_helper::get_single(conv_membership.conv_id)?; let conv = conversations_helper::get_single(conv_membership.conv_id)?;
if !conv.is_linked_to_group() { if !conv.is_linked_to_group() {
self.bad_request("This conversation is not linked to a group, it can not be updated this way!".to_string())?; self.bad_request(
"This conversation is not linked to a group, it can not be updated this way!"
.to_string(),
)?;
} }
Ok(conv) Ok(conv)
@ -89,8 +99,10 @@ pub async fn get_info_multiple(r: &mut HttpRequestHandler) -> RequestResult {
for id in groups_id { for id in groups_id {
let id = GroupID::new(id as u64); let id = GroupID::new(id as u64);
if !groups_helper::exists(&id)? || if !groups_helper::exists(&id)?
groups_helper::get_access_level(&id, r.user_id_opt())? < GroupAccessLevel::LIMITED_ACCESS { || groups_helper::get_access_level(&id, r.user_id_opt())?
< GroupAccessLevel::LIMITED_ACCESS
{
r.not_found(format!("Group {} not found!", id.id()))?; r.not_found(format!("Group {} not found!", id.id()))?;
} }
@ -132,7 +144,11 @@ pub async fn set_settings(r: &mut HttpRequestHandler) -> RequestResult {
posts_creation_level: GroupPostsCreationLevel::from_api(&r.post_string("posts_level")?), posts_creation_level: GroupPostsCreationLevel::from_api(&r.post_string("posts_level")?),
is_members_list_public: r.post_bool("is_members_list_public")?, is_members_list_public: r.post_bool("is_members_list_public")?,
logo: None, logo: None,
virtual_directory: r.post_checked_virtual_directory_opt("virtual_directory", group_id.id(), VirtualDirType::GROUP)?, virtual_directory: r.post_checked_virtual_directory_opt(
"virtual_directory",
group_id.id(),
VirtualDirType::GROUP,
)?,
time_create: 0, time_create: 0,
description: r.post_string_without_html_opt("description", 0)?, description: r.post_string_without_html_opt("description", 0)?,
url: r.post_url_opt("url", false)?, url: r.post_url_opt("url", false)?,
@ -184,10 +200,13 @@ pub async fn delete_logo(r: &mut HttpRequestHandler) -> RequestResult {
/// Create a new group's conversation /// Create a new group's conversation
pub async fn create_conversation(r: &mut HttpRequestHandler) -> RequestResult { pub async fn create_conversation(r: &mut HttpRequestHandler) -> RequestResult {
let group = r.post_group_id_with_access("group_id", GroupAccessLevel::ADMIN_ACCESS)?; let group = r.post_group_id_with_access("group_id", GroupAccessLevel::ADMIN_ACCESS)?;
let min_membership_level = r.post_group_membership_level_for_conversation("min_membership_level")?; let min_membership_level =
r.post_group_membership_level_for_conversation("min_membership_level")?;
let name = r.post_string("name")?; let name = r.post_string("name")?;
let conv_id = conversations_helper::create_conversation_for_group(group, min_membership_level, &name).await?; let conv_id =
conversations_helper::create_conversation_for_group(group, min_membership_level, &name)
.await?;
r.set_response(ResCreateConversationForGroup::new(conv_id)) r.set_response(ResCreateConversationForGroup::new(conv_id))
} }
@ -218,7 +237,9 @@ pub async fn get_members(r: &mut HttpRequestHandler) -> RequestResult {
let group = groups_helper::get_info(&group_id)?; let group = groups_helper::get_info(&group_id)?;
if group_access_level < GroupAccessLevel::VIEW_ACCESS if group_access_level < GroupAccessLevel::VIEW_ACCESS
|| (!group.is_members_list_public && group_access_level < GroupAccessLevel::MODERATOR_ACCESS) { || (!group.is_members_list_public
&& group_access_level < GroupAccessLevel::MODERATOR_ACCESS)
{
r.forbidden("You can not access the list of members of this group!".to_string())?; r.forbidden("You can not access the list of members of this group!".to_string())?;
} }
@ -232,14 +253,17 @@ pub async fn cancel_invitation(r: &mut HttpRequestHandler) -> RequestResult {
let group_id = r.post_group_id_with_access("groupID", GroupAccessLevel::MODERATOR_ACCESS)?; let group_id = r.post_group_id_with_access("groupID", GroupAccessLevel::MODERATOR_ACCESS)?;
let user_id = r.post_user_id("userID")?; let user_id = r.post_user_id("userID")?;
if groups_helper::get_membership_level(&group_id, Some(user_id.clone()))? != GroupMembershipLevel::INVITED { if groups_helper::get_membership_level(&group_id, Some(user_id.clone()))?
!= GroupMembershipLevel::INVITED
{
r.forbidden("This user has not been invited to join this group!".to_string())?; r.forbidden("This user has not been invited to join this group!".to_string())?;
} }
groups_helper::delete_member(&group_id, &user_id).await?; groups_helper::delete_member(&group_id, &user_id).await?;
// Delete related notifications // Delete related notifications
notifications_helper::delete_all_related_to_group_membership_notifications(&user_id, &group_id).await?; notifications_helper::delete_all_related_to_group_membership_notifications(&user_id, &group_id)
.await?;
r.success("Membership invitation has been cancelled!") r.success("Membership invitation has been cancelled!")
} }
@ -249,7 +273,9 @@ pub async fn invite_user(r: &mut HttpRequestHandler) -> RequestResult {
let group_id = r.post_group_id_with_access("group_id", GroupAccessLevel::MODERATOR_ACCESS)?; let group_id = r.post_group_id_with_access("group_id", GroupAccessLevel::MODERATOR_ACCESS)?;
let user_id = r.post_user_id("userID")?; let user_id = r.post_user_id("userID")?;
if groups_helper::get_membership_level(&group_id, Some(user_id.clone()))? != GroupMembershipLevel::VISITOR { if groups_helper::get_membership_level(&group_id, Some(user_id.clone()))?
!= GroupMembershipLevel::VISITOR
{
r.bad_request("The user is not a visitor of the group!".to_string())?; r.bad_request("The user is not a visitor of the group!".to_string())?;
} }
@ -257,7 +283,12 @@ pub async fn invite_user(r: &mut HttpRequestHandler) -> RequestResult {
// Send a notification // Send a notification
notifications_helper::create_group_membership_notification( notifications_helper::create_group_membership_notification(
&user_id, Some(r.user_id_ref()?), &group_id, NotifEventType::SENT_GROUP_MEMBERSHIP_INVITATION).await?; &user_id,
Some(r.user_id_ref()?),
&group_id,
NotifEventType::SENT_GROUP_MEMBERSHIP_INVITATION,
)
.await?;
r.success("The user has been successfully invited to join the group!") r.success("The user has been successfully invited to join the group!")
} }
@ -278,10 +309,16 @@ pub async fn respond_invitation(r: &mut HttpRequestHandler) -> RequestResult {
} }
// Create a notification // Create a notification
notifications_helper::create_group_membership_notification(r.user_id_ref()?, None, &group_id, match accept { notifications_helper::create_group_membership_notification(
r.user_id_ref()?,
None,
&group_id,
match accept {
true => NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_INVITATION, true => NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_INVITATION,
false => NotifEventType::REJECTED_GROUP_MEMBERSHIP_INVITATION false => NotifEventType::REJECTED_GROUP_MEMBERSHIP_INVITATION,
}).await?; },
)
.await?;
r.success("Response to the invitation was successfully saved!") r.success("Response to the invitation was successfully saved!")
} }
@ -290,7 +327,9 @@ pub async fn respond_invitation(r: &mut HttpRequestHandler) -> RequestResult {
pub async fn send_request(r: &mut HttpRequestHandler) -> RequestResult { pub async fn send_request(r: &mut HttpRequestHandler) -> RequestResult {
let group_id = r.post_group_id_with_access("id", GroupAccessLevel::LIMITED_ACCESS)?; let group_id = r.post_group_id_with_access("id", GroupAccessLevel::LIMITED_ACCESS)?;
if groups_helper::get_membership_level(&group_id, r.user_id_opt())? != GroupMembershipLevel::VISITOR { if groups_helper::get_membership_level(&group_id, r.user_id_opt())?
!= GroupMembershipLevel::VISITOR
{
r.forbidden("You are not currently a visitor of the group!".to_string())?; r.forbidden("You are not currently a visitor of the group!".to_string())?;
} }
@ -300,7 +339,9 @@ pub async fn send_request(r: &mut HttpRequestHandler) -> RequestResult {
GroupRegistrationLevel::OPEN_REGISTRATION => GroupMembershipLevel::MEMBER, GroupRegistrationLevel::OPEN_REGISTRATION => GroupMembershipLevel::MEMBER,
GroupRegistrationLevel::MODERATED_REGISTRATION => GroupMembershipLevel::PENDING, GroupRegistrationLevel::MODERATED_REGISTRATION => GroupMembershipLevel::PENDING,
GroupRegistrationLevel::CLOSED_REGISTRATION => { GroupRegistrationLevel::CLOSED_REGISTRATION => {
r.forbidden("You are not authorized to send a registration request for this group!".to_string())?; r.forbidden(
"You are not authorized to send a registration request for this group!".to_string(),
)?;
unreachable!(); unreachable!();
} }
}; };
@ -312,15 +353,23 @@ pub async fn send_request(r: &mut HttpRequestHandler) -> RequestResult {
time_create: time(), time_create: time(),
level, level,
following: true, following: true,
}).await?; })
.await?;
// Send a notification, if required // Send a notification, if required
if matches!(group.registration_level, GroupRegistrationLevel::MODERATED_REGISTRATION) { if matches!(
notifications_helper::create_group_membership_notification(r.user_id_ref()?, None, group.registration_level,
&group_id, NotifEventType::SENT_GROUP_MEMBERSHIP_REQUEST).await?; GroupRegistrationLevel::MODERATED_REGISTRATION
) {
notifications_helper::create_group_membership_notification(
r.user_id_ref()?,
None,
&group_id,
NotifEventType::SENT_GROUP_MEMBERSHIP_REQUEST,
)
.await?;
} }
r.success("The membership has been successfully saved!") r.success("The membership has been successfully saved!")
} }
@ -328,14 +377,20 @@ pub async fn send_request(r: &mut HttpRequestHandler) -> RequestResult {
pub async fn cancel_request(r: &mut HttpRequestHandler) -> RequestResult { pub async fn cancel_request(r: &mut HttpRequestHandler) -> RequestResult {
let group_id = r.post_group_id_with_access("id", GroupAccessLevel::LIMITED_ACCESS)?; let group_id = r.post_group_id_with_access("id", GroupAccessLevel::LIMITED_ACCESS)?;
if groups_helper::get_membership_level(&group_id, r.user_id_opt())? != GroupMembershipLevel::PENDING { if groups_helper::get_membership_level(&group_id, r.user_id_opt())?
!= GroupMembershipLevel::PENDING
{
r.forbidden("You did not send a membership request to this group!".to_string())?; r.forbidden("You did not send a membership request to this group!".to_string())?;
} }
groups_helper::delete_member(&group_id, &r.user_id()?).await?; groups_helper::delete_member(&group_id, &r.user_id()?).await?;
// Delete any related notification // Delete any related notification
notifications_helper::delete_all_related_to_group_membership_notifications(r.user_id_ref()?, &group_id).await?; notifications_helper::delete_all_related_to_group_membership_notifications(
r.user_id_ref()?,
&group_id,
)
.await?;
r.success("The request has been successfully cancelled!") r.success("The request has been successfully cancelled!")
} }
@ -356,14 +411,17 @@ pub async fn delete_member(r: &mut HttpRequestHandler) -> RequestResult {
} }
// Only administrator can delete members that are more than members (moderators & administrators) // Only administrator can delete members that are more than members (moderators & administrators)
if membership.level < GroupMembershipLevel::MEMBER && curr_user_membership.level != GroupMembershipLevel::ADMINISTRATOR { if membership.level < GroupMembershipLevel::MEMBER
&& curr_user_membership.level != GroupMembershipLevel::ADMINISTRATOR
{
r.forbidden("Only administrators can delete this membership!".to_string())?; r.forbidden("Only administrators can delete this membership!".to_string())?;
} }
groups_helper::delete_member(&group_id, &user_id).await?; groups_helper::delete_member(&group_id, &user_id).await?;
// Delete related notifications // Delete related notifications
notifications_helper::delete_all_related_to_group_membership_notifications(&user_id, &group_id).await?; notifications_helper::delete_all_related_to_group_membership_notifications(&user_id, &group_id)
.await?;
r.success("Membership of the user has been successfully deleted!") r.success("Membership of the user has been successfully deleted!")
} }
@ -400,17 +458,25 @@ pub async fn respond_request(r: &mut HttpRequestHandler) -> RequestResult {
let user_id = r.post_user_id("userID")?; let user_id = r.post_user_id("userID")?;
let accept = r.post_bool("accept")?; let accept = r.post_bool("accept")?;
if groups_helper::get_membership_level(&group_id, Some(user_id.clone()))? != GroupMembershipLevel::PENDING { if groups_helper::get_membership_level(&group_id, Some(user_id.clone()))?
!= GroupMembershipLevel::PENDING
{
r.forbidden("This user has not requested a membership for this group!".to_string())?; r.forbidden("This user has not requested a membership for this group!".to_string())?;
} }
groups_helper::respond_request(&group_id, &user_id, accept).await?; groups_helper::respond_request(&group_id, &user_id, accept).await?;
// Create a notification // Create a notification
notifications_helper::create_group_membership_notification(&user_id, Some(r.user_id_ref()?), &group_id, match accept { notifications_helper::create_group_membership_notification(
&user_id,
Some(r.user_id_ref()?),
&group_id,
match accept {
true => NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_REQUEST, true => NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_REQUEST,
false => NotifEventType::REJECTED_GROUP_MEMBERSHIP_REQUEST false => NotifEventType::REJECTED_GROUP_MEMBERSHIP_REQUEST,
}).await?; },
)
.await?;
r.success("The response to the request has been successfully saved!") r.success("The response to the request has been successfully saved!")
} }
@ -436,7 +502,11 @@ pub async fn remove_membership(r: &mut HttpRequestHandler) -> RequestResult {
groups_helper::delete_member(&group_id, &r.user_id()?).await?; groups_helper::delete_member(&group_id, &r.user_id()?).await?;
// Delete group membership notifications // Delete group membership notifications
notifications_helper::delete_all_related_to_group_membership_notifications(r.user_id_ref()?, &group_id).await?; notifications_helper::delete_all_related_to_group_membership_notifications(
r.user_id_ref()?,
&group_id,
)
.await?;
r.success("Your membership has been successfully deleted!") r.success("Your membership has been successfully deleted!")
} }

View File

@ -6,8 +6,8 @@ use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::error::ExecError; use crate::data::error::ExecError;
use crate::data::group::GroupAccessLevel; use crate::data::group::GroupAccessLevel;
use crate::data::post::PostAccessLevel; use crate::data::post::PostAccessLevel;
use crate::helpers::{likes_helper, notifications_helper, user_helper};
use crate::helpers::likes_helper::LikeType; use crate::helpers::likes_helper::LikeType;
use crate::helpers::{likes_helper, notifications_helper, user_helper};
use crate::routes::RequestResult; use crate::routes::RequestResult;
struct LikeTarget(u64, LikeType); struct LikeTarget(u64, LikeType);
@ -18,7 +18,6 @@ pub async fn update<H: BaseRequestHandler>(r: &mut H) -> RequestResult {
let is_liking = r.post_bool("like")?; let is_liking = r.post_bool("like")?;
let target = match req_type.as_str() { let target = match req_type.as_str() {
// In case of user // In case of user
"user" => { "user" => {
let user_id = r.post_user_id("id")?; let user_id = r.post_user_id("id")?;
@ -30,13 +29,16 @@ pub async fn update<H: BaseRequestHandler>(r: &mut H) -> RequestResult {
LikeTarget(user_id.id(), LikeType::USER) LikeTarget(user_id.id(), LikeType::USER)
} }
// In case of post // In case of post
"post" => { "post" => {
let post = r.post_post_with_access("id", PostAccessLevel::BASIC_ACCESS)?; let post = r.post_post_with_access("id", PostAccessLevel::BASIC_ACCESS)?;
// Delete any notification targeting this user about the post // Delete any notification targeting this user about the post
notifications_helper::delete_all_post_notifications_targeting_user(r.user_id_ref()?, post.id).await?; notifications_helper::delete_all_post_notifications_targeting_user(
r.user_id_ref()?,
post.id,
)
.await?;
LikeTarget(post.id, LikeType::POST) LikeTarget(post.id, LikeType::POST)
} }
@ -46,7 +48,11 @@ pub async fn update<H: BaseRequestHandler>(r: &mut H) -> RequestResult {
let comment = r.post_comment_with_access("id")?; let comment = r.post_comment_with_access("id")?;
// Delete any notification targeting this user about the post // Delete any notification targeting this user about the post
notifications_helper::delete_all_post_notifications_targeting_user(r.user_id_ref()?, comment.post_id).await?; notifications_helper::delete_all_post_notifications_targeting_user(
r.user_id_ref()?,
comment.post_id,
)
.await?;
LikeTarget(comment.id, LikeType::COMMENT) LikeTarget(comment.id, LikeType::COMMENT)
} }

View File

@ -1,24 +1,24 @@
pub mod admin; pub mod admin;
pub mod server_controller;
pub mod user_ws_controller;
pub mod rtc_relay_controller;
pub mod account_controller; pub mod account_controller;
pub mod user_controller;
pub mod settings_controller;
pub mod friends_controller;
pub mod conversations_controller;
pub mod search_controller;
pub mod groups_controller;
pub mod posts_controller;
pub mod comments_controller;
pub mod likes_controller;
pub mod surveys_controller;
pub mod notifications_controller;
pub mod virtual_directory_controller;
pub mod web_app_controller;
pub mod calls_controller; pub mod calls_controller;
pub mod user_ws_actions; pub mod comments_controller;
pub mod conversations_controller;
pub mod forez_controller;
pub mod friends_controller;
pub mod groups_controller;
pub mod likes_controller;
pub mod notifications_controller;
pub mod posts_controller;
pub mod push_notifications_controller; pub mod push_notifications_controller;
pub mod report_controller; pub mod report_controller;
pub mod forez_controller; pub mod rtc_relay_controller;
pub mod search_controller;
pub mod server_controller;
pub mod settings_controller;
pub mod surveys_controller;
pub mod user_controller;
pub mod user_ws_actions;
pub mod user_ws_controller;
pub mod virtual_directory_controller;
pub mod web_app_controller;

View File

@ -5,7 +5,6 @@
use crate::api_data::notification_api::NotificationAPI; use crate::api_data::notification_api::NotificationAPI;
use crate::api_data::res_count_all_unreads::ResCountAllUnread; use crate::api_data::res_count_all_unreads::ResCountAllUnread;
use crate::api_data::res_number_unread_notifications::ResNumberUnreadNotifications; use crate::api_data::res_number_unread_notifications::ResNumberUnreadNotifications;
use crate::routes::RequestResult;
use crate::controllers::user_ws_controller; use crate::controllers::user_ws_controller;
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::error::{Res, ResultBoxError}; use crate::data::error::{Res, ResultBoxError};
@ -13,8 +12,9 @@ use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::notification::Notification; use crate::data::notification::Notification;
use crate::data::user::UserID; use crate::data::user::UserID;
use crate::data::user_ws_message::UserWsMessage; use crate::data::user_ws_message::UserWsMessage;
use crate::helpers::{conversations_helper, events_helper, friends_helper, notifications_helper};
use crate::helpers::events_helper::Event; use crate::helpers::events_helper::Event;
use crate::helpers::{conversations_helper, events_helper, friends_helper, notifications_helper};
use crate::routes::RequestResult;
impl HttpRequestHandler { impl HttpRequestHandler {
/// Get the id of a notification included in the request /// Get the id of a notification included in the request
@ -46,10 +46,14 @@ pub async fn count_all_news(r: &mut HttpRequestHandler) -> RequestResult {
let conversations = conversations_helper::count_unread_for_user(r.user_id_ref()?)?; let conversations = conversations_helper::count_unread_for_user(r.user_id_ref()?)?;
let friends_requests = match r.post_bool_opt("friends_request", false) { let friends_requests = match r.post_bool_opt("friends_request", false) {
true => Some(friends_helper::count_requests(r.user_id_ref()?)?), true => Some(friends_helper::count_requests(r.user_id_ref()?)?),
false => None false => None,
}; };
r.set_response(ResCountAllUnread::new(notifications, conversations as u64, friends_requests)) r.set_response(ResCountAllUnread::new(
notifications,
conversations as u64,
friends_requests,
))
} }
/// Get the list of unread notifications /// Get the list of unread notifications
@ -92,7 +96,10 @@ pub fn send_new_notifications_number(user_id: &UserID) -> Res {
} }
user_ws_controller::send_message_to_user( user_ws_controller::send_message_to_user(
&UserWsMessage::no_id_message("number_notifs", notifications_helper::count_unread(user_id)?)?, &UserWsMessage::no_id_message(
"number_notifs",
notifications_helper::count_unread(user_id)?,
)?,
user_id, user_id,
) )
} }

View File

@ -12,8 +12,12 @@ use crate::data::group::GroupAccessLevel;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::new_survey::NewSurvey; use crate::data::new_survey::NewSurvey;
use crate::data::notification::NotifEventType; use crate::data::notification::NotifEventType;
use crate::data::post::{Post, PostAccessLevel, PostFile, PostKind, PostPageKind, PostVisibilityLevel, PostWebLink}; use crate::data::post::{
use crate::helpers::{friends_helper, groups_helper, notifications_helper, posts_helper, survey_helper, user_helper}; Post, PostAccessLevel, PostFile, PostKind, PostPageKind, PostVisibilityLevel, PostWebLink,
};
use crate::helpers::{
friends_helper, groups_helper, notifications_helper, posts_helper, survey_helper, user_helper,
};
use crate::routes::RequestResult; use crate::routes::RequestResult;
use crate::utils::date_utils::time; use crate::utils::date_utils::time;
use crate::utils::string_utils::{check_string_before_insert, check_youtube_id}; use crate::utils::string_utils::{check_string_before_insert, check_youtube_id};
@ -96,7 +100,8 @@ pub async fn create_post(r: &mut HttpRequestHandler) -> RequestResult {
} }
"group" => { "group" => {
let group_id = r.post_group_id_with_access("kind-id", GroupAccessLevel::MEMBER_ACCESS)?; let group_id =
r.post_group_id_with_access("kind-id", GroupAccessLevel::MEMBER_ACCESS)?;
if !groups_helper::can_user_create_posts(&group_id, r.user_id_ref()?)? { if !groups_helper::can_user_create_posts(&group_id, r.user_id_ref()?)? {
r.forbidden("You are not allowed to create posts on this group!".to_string())?; r.forbidden("You are not allowed to create posts on this group!".to_string())?;
@ -126,7 +131,6 @@ pub async fn create_post(r: &mut HttpRequestHandler) -> RequestResult {
// Handle different post types // Handle different post types
post.kind = match r.post_string("kind")?.as_str() { post.kind = match r.post_string("kind")?.as_str() {
// Text posts // Text posts
"text" => { "text" => {
if !check_string_before_insert(post.content.as_ref().unwrap_or(&String::new())) { if !check_string_before_insert(post.content.as_ref().unwrap_or(&String::new())) {
@ -136,7 +140,6 @@ pub async fn create_post(r: &mut HttpRequestHandler) -> RequestResult {
PostKind::POST_KIND_TEXT PostKind::POST_KIND_TEXT
} }
// Image post // Image post
"image" => { "image" => {
if !r.has_file("image") { if !r.has_file("image") {
@ -161,7 +164,8 @@ pub async fn create_post(r: &mut HttpRequestHandler) -> RequestResult {
// Weblink posts // Weblink posts
"weblink" => { "weblink" => {
let url = r.post_url_opt("url", true)? let url = r
.post_url_opt("url", true)?
.ok_or(ExecError::new("Missing url!"))?; .ok_or(ExecError::new("Missing url!"))?;
match get_post_web_link(&url) { match get_post_web_link(&url) {
@ -203,7 +207,8 @@ pub async fn create_post(r: &mut HttpRequestHandler) -> RequestResult {
post_id: 0, post_id: 0,
user_id: r.user_id()?, user_id: r.user_id()?,
question: r.post_string("question")?, question: r.post_string("question")?,
choices: r.post_string("answers")? choices: r
.post_string("answers")?
.split("<>") .split("<>")
.filter(|a| a.len() > 0) .filter(|a| a.len() > 0)
.map(|a| a.to_string()) .map(|a| a.to_string())
@ -235,7 +240,12 @@ pub async fn create_post(r: &mut HttpRequestHandler) -> RequestResult {
} }
// Create a notification // Create a notification
notifications_helper::create_post_notification(r.user_id_ref()?, post_id, NotifEventType::ELEM_CREATED).await?; notifications_helper::create_post_notification(
r.user_id_ref()?,
post_id,
NotifEventType::ELEM_CREATED,
)
.await?;
r.set_response(ResCreatePost::new(post_id)) r.set_response(ResCreatePost::new(post_id))
} }
@ -263,7 +273,8 @@ pub async fn update_content(r: &mut HttpRequestHandler) -> RequestResult {
posts_helper::set_content(post.id, &new_content)?; posts_helper::set_content(post.id, &new_content)?;
// Delete the notifications targeting the current user about this post // Delete the notifications targeting the current user about this post
notifications_helper::delete_all_post_notifications_targeting_user(r.user_id_ref()?, post.id).await?; notifications_helper::delete_all_post_notifications_targeting_user(r.user_id_ref()?, post.id)
.await?;
r.success("Content updated") r.success("Content updated")
} }

View File

@ -12,7 +12,11 @@ use crate::routes::RequestResult;
/// Get current push notifications status for a connection /// Get current push notifications status for a connection
pub async fn get_status(r: &mut HttpRequestHandler) -> RequestResult { pub async fn get_status(r: &mut HttpRequestHandler) -> RequestResult {
let status = &r.user_access_token().unwrap().push_notifications_token.clone(); let status = &r
.user_access_token()
.unwrap()
.push_notifications_token
.clone();
r.set_response(PushNotificationsStatusAPI::new(status)) r.set_response(PushNotificationsStatusAPI::new(status))
} }

View File

@ -6,7 +6,7 @@ use crate::data::config::conf;
use crate::data::group::GroupAccessLevel; use crate::data::group::GroupAccessLevel;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::post::PostAccessLevel; use crate::data::post::PostAccessLevel;
use crate::data::report::{Report, REPORT_CAUSES, ReportID, ReportTarget}; use crate::data::report::{Report, ReportID, ReportTarget, REPORT_CAUSES};
use crate::helpers::reports_helper; use crate::helpers::reports_helper;
use crate::routes::RequestResult; use crate::routes::RequestResult;
use crate::utils::date_utils::time; use crate::utils::date_utils::time;
@ -29,29 +29,23 @@ pub async fn report(r: &mut HttpRequestHandler) -> RequestResult {
)?; )?;
let target = match r.post_string("target_type")?.as_str() { let target = match r.post_string("target_type")?.as_str() {
"post" => "post" => ReportTarget::Post(
ReportTarget::Post( r.post_post_with_access("target_id", PostAccessLevel::BASIC_ACCESS)?
r.post_post_with_access("target_id", PostAccessLevel::BASIC_ACCESS)?.id .id,
), ),
"comment" => ReportTarget::Comment( "comment" => ReportTarget::Comment(r.post_comment_with_access("target_id")?.id),
r.post_comment_with_access("target_id")?.id
),
"conversation" => ReportTarget::Conversation( "conversation" => ReportTarget::Conversation(r.post_conv("target_id")?.conv_id),
r.post_conv("target_id")?.conv_id
),
"conversation_message" => ReportTarget::ConversationMessage( "conversation_message" => {
r.post_conv_message_id("target_id")?.id ReportTarget::ConversationMessage(r.post_conv_message_id("target_id")?.id)
), }
"user" => ReportTarget::User( "user" => ReportTarget::User(r.post_user_id("target_id")?),
r.post_user_id("target_id")?
),
"group" => ReportTarget::Group( "group" => ReportTarget::Group(
r.post_group_id_with_access("target_id", GroupAccessLevel::LIMITED_ACCESS)? r.post_group_id_with_access("target_id", GroupAccessLevel::LIMITED_ACCESS)?,
), ),
_ => { _ => {
@ -71,7 +65,10 @@ pub async fn report(r: &mut HttpRequestHandler) -> RequestResult {
// Check for duplicate // Check for duplicate
if reports_helper::already_exists(&report)? { if reports_helper::already_exists(&report)? {
r.set_error(HttpError::new(409, "You have already submitted a report for this resource!")); r.set_error(HttpError::new(
409,
"You have already submitted a report for this resource!",
));
return Ok(()); return Ok(());
} }

View File

@ -2,8 +2,8 @@
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use actix::{ActorContext, Addr, AsyncContext, Handler, StreamHandler};
use actix::prelude::*; use actix::prelude::*;
use actix::{ActorContext, Addr, AsyncContext, Handler, StreamHandler};
use actix_http::ws::Item; use actix_http::ws::Item;
use actix_web_actors::ws::{Message, ProtocolError}; use actix_web_actors::ws::{Message, ProtocolError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -90,13 +90,16 @@ impl actix::Actor for RtcRelayActor {
println!("Started new WebSocket connection to RTC relay!"); println!("Started new WebSocket connection to RTC relay!");
// Send calls configuration to server // Send calls configuration to server
ctx.text(serde_json::to_string(&CallsConfigWrapper { ctx.text(
serde_json::to_string(&CallsConfigWrapper {
title: "config".to_string(), title: "config".to_string(),
data: CallsConfig { data: CallsConfig {
allowVideo: conf().rtc_relay.as_ref().unwrap().allow_video, allowVideo: conf().rtc_relay.as_ref().unwrap().allow_video,
iceServers: conf().rtc_relay.as_ref().unwrap().ice_servers.clone(), iceServers: conf().rtc_relay.as_ref().unwrap().ice_servers.clone(),
}, },
}).unwrap()) })
.unwrap(),
)
} }
fn stopping(&mut self, _: &mut Self::Context) -> Running { fn stopping(&mut self, _: &mut Self::Context) -> Running {
@ -136,14 +139,16 @@ impl RtcRelayActor {
} }
} }
impl StreamHandler<Result<actix_web_actors::ws::Message, actix_web_actors::ws::ProtocolError>> for RtcRelayActor { impl StreamHandler<Result<actix_web_actors::ws::Message, actix_web_actors::ws::ProtocolError>>
for RtcRelayActor
{
fn handle(&mut self, msg: Result<Message, ProtocolError>, ctx: &mut Self::Context) { fn handle(&mut self, msg: Result<Message, ProtocolError>, ctx: &mut Self::Context) {
let msg = match msg { let msg = match msg {
Err(_) => { Err(_) => {
ctx.stop(); ctx.stop();
return; return;
} }
Ok(msg) => msg Ok(msg) => msg,
}; };
if conf().verbose_mode { if conf().verbose_mode {
@ -201,7 +206,8 @@ async fn process_message_from_relay(msg: &RTCSocketMessage) -> Res {
peer_id: msg.peerId.clone().unwrap_or("0".to_string()), peer_id: msg.peerId.clone().unwrap_or("0".to_string()),
data: serde_json::to_string(msg.data.as_ref().unwrap_or(&Value::Null)) data: serde_json::to_string(msg.data.as_ref().unwrap_or(&Value::Null))
.unwrap_or("failed to serialize signal data".to_string()), .unwrap_or("failed to serialize signal data".to_string()),
})).await?; }))
.await?;
} }
title => { title => {
@ -217,15 +223,11 @@ impl Handler<RTCMessages> for RtcRelayActor {
fn handle(&mut self, msg: RTCMessages, ctx: &mut Self::Context) -> Self::Result { fn handle(&mut self, msg: RTCMessages, ctx: &mut Self::Context) -> Self::Result {
match msg { match msg {
// Close connection // Close connection
RTCMessages::Close => { RTCMessages::Close => ctx.close(None),
ctx.close(None)
}
// Send message // Send message
RTCMessages::SendMessage(msg) => { RTCMessages::SendMessage(msg) => match serde_json::to_string(&msg) {
match serde_json::to_string(&msg) {
Ok(txt) => { Ok(txt) => {
if conf().verbose_mode { if conf().verbose_mode {
println!("API => RTC WS : {}", txt); println!("API => RTC WS : {}", txt);
@ -236,8 +238,7 @@ impl Handler<RTCMessages> for RtcRelayActor {
Err(e) => { Err(e) => {
eprintln!("Failed to send message to RTC relay ! {:#?}", e); eprintln!("Failed to send message to RTC relay ! {:#?}", e);
} }
} },
}
} }
} }
} }
@ -270,13 +271,18 @@ pub fn is_connected() -> bool {
/// ws.onerror = (e) => console.log("WS ERROR !", e); /// ws.onerror = (e) => console.log("WS ERROR !", e);
/// ws.onclose = (e) => console.log("WS CLOSED!"); /// ws.onclose = (e) => console.log("WS CLOSED!");
/// ``` /// ```
pub async fn open_ws(req: actix_web::HttpRequest, pub async fn open_ws(
stream: actix_web::web::Payload) -> Result<actix_web::HttpResponse, actix_web::Error> { req: actix_web::HttpRequest,
stream: actix_web::web::Payload,
) -> Result<actix_web::HttpResponse, actix_web::Error> {
let ip = req.peer_addr().unwrap(); let ip = req.peer_addr().unwrap();
// Check if video calls are enabled // Check if video calls are enabled
if !conf().is_rtc_relay_enabled() { if !conf().is_rtc_relay_enabled() {
eprintln!("A relay from {} tried to connect to the server but the relay is disabled!", ip); eprintln!(
"A relay from {} tried to connect to the server but the relay is disabled!",
ip
);
return Ok(actix_web::HttpResponse::BadRequest().body("RTC Relay not configured!")); return Ok(actix_web::HttpResponse::BadRequest().body("RTC Relay not configured!"));
} }
@ -284,13 +290,19 @@ pub async fn open_ws(req: actix_web::HttpRequest,
// Check remote IP address // Check remote IP address
if !match_ip(&conf.ip, ip.ip().to_string().as_str()) { if !match_ip(&conf.ip, ip.ip().to_string().as_str()) {
eprintln!("A relay from {} tried to connect to the server but the IP address is not authorized!", ip); eprintln!(
"A relay from {} tried to connect to the server but the IP address is not authorized!",
ip
);
return Ok(actix_web::HttpResponse::Unauthorized().body("Access denied!")); return Ok(actix_web::HttpResponse::Unauthorized().body("Access denied!"));
} }
// Check the token // Check the token
if !req.query_string().eq(&format!("token={}", &conf.token)) { if !req.query_string().eq(&format!("token={}", &conf.token)) {
eprintln!("A relay from {} tried to connect with an invalid access token!", ip); eprintln!(
"A relay from {} tried to connect with an invalid access token!",
ip
);
return Ok(actix_web::HttpResponse::Unauthorized().body("Invalid token!")); return Ok(actix_web::HttpResponse::Unauthorized().body("Invalid token!"));
} }
@ -304,9 +316,12 @@ pub async fn open_ws(req: actix_web::HttpRequest,
} }
/// Send a message to the relay /// Send a message to the relay
fn send_message_to_relay<T>(e: T) -> Res where T: Serialize { fn send_message_to_relay<T>(e: T) -> Res
let conn = get_active_connection() where
.ok_or(ExecError::boxed_new("Connection to RTC relay missing!"))?; T: Serialize,
{
let conn =
get_active_connection().ok_or(ExecError::boxed_new("Connection to RTC relay missing!"))?;
conn.do_send(RTCMessages::SendMessage(serde_json::to_value(e)?)); conn.do_send(RTCMessages::SendMessage(serde_json::to_value(e)?));
@ -320,12 +335,18 @@ pub fn handle_event(e: &events_helper::Event) -> Res {
send_message_to_relay(CallUserSignal { send_message_to_relay(CallUserSignal {
title: "signal".to_string(), title: "signal".to_string(),
callHash: signal.call_hash.to_string(), callHash: signal.call_hash.to_string(),
peerId: signal.user_id.as_ref().map(|i| i.id()).unwrap_or(0).to_string(), peerId: signal
.user_id
.as_ref()
.map(|i| i.id())
.unwrap_or(0)
.to_string(),
data: CallUserSignalData { data: CallUserSignalData {
r#type: match signal.signal { r#type: match signal.signal {
CallSignal::SDP(_, _, _) => "SDP", CallSignal::SDP(_, _, _) => "SDP",
CallSignal::Candidate(_, _) => "CANDIDATE", CallSignal::Candidate(_, _) => "CANDIDATE",
}.to_string(), }
.to_string(),
data: serde_json::from_str(&signal.raw_data)?, data: serde_json::from_str(&signal.raw_data)?,
}, },
})?; })?;
@ -345,7 +366,7 @@ pub fn handle_event(e: &events_helper::Event) -> Res {
callHash: request.call_hash.to_string(), callHash: request.call_hash.to_string(),
peerId: match &request.peer_id { peerId: match &request.peer_id {
None => "0".to_string(), None => "0".to_string(),
Some(id) => id.id().to_string() Some(id) => id.id().to_string(),
}, },
})?; })?;
} }

View File

@ -3,11 +3,11 @@
//! @author Pierre Hubert //! @author Pierre Hubert
use crate::api_data::global_search_result_api::GlobalSearchResultAPI; use crate::api_data::global_search_result_api::GlobalSearchResultAPI;
use crate::routes::RequestResult;
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::global_search_result::GlobalSearchResult; use crate::data::global_search_result::GlobalSearchResult;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
use crate::helpers::{groups_helper, user_helper}; use crate::helpers::{groups_helper, user_helper};
use crate::routes::RequestResult;
/// Search for user /// Search for user
pub async fn search_user(r: &mut HttpRequestHandler) -> RequestResult { pub async fn search_user(r: &mut HttpRequestHandler) -> RequestResult {
@ -32,11 +32,12 @@ pub async fn search_global(r: &mut HttpRequestHandler) -> RequestResult {
.map(|f| GlobalSearchResult::User(f.clone())) .map(|f| GlobalSearchResult::User(f.clone()))
.collect::<Vec<GlobalSearchResult>>(); .collect::<Vec<GlobalSearchResult>>();
list.append(&mut groups_helper::search_group(&query, limit)? list.append(
&mut groups_helper::search_group(&query, limit)?
.iter() .iter()
.map(|f| GlobalSearchResult::Group(f.clone())) .map(|f| GlobalSearchResult::Group(f.clone()))
.collect::<Vec<GlobalSearchResult>>()); .collect::<Vec<GlobalSearchResult>>(),
);
r.set_response(GlobalSearchResultAPI::for_list(&list)) r.set_response(GlobalSearchResultAPI::for_list(&list))
} }

View File

@ -9,8 +9,8 @@ use crate::api_data::language_settings_api::LanguageSettingsAPI;
use crate::api_data::notification_settings_api::NotificationSettingsAPI; use crate::api_data::notification_settings_api::NotificationSettingsAPI;
use crate::api_data::res_create_custom_emoji::ResCreateCustomEmoji; use crate::api_data::res_create_custom_emoji::ResCreateCustomEmoji;
use crate::api_data::security_settings_api::SecuritySettingsAPI; use crate::api_data::security_settings_api::SecuritySettingsAPI;
use crate::constants::{conservation_policy, SUPPORTED_LANGUAGES};
use crate::constants::accounts_info_policy::{MIN_FIRST_NAME_LENGTH, MIN_LAST_NAME_LENGTH}; use crate::constants::accounts_info_policy::{MIN_FIRST_NAME_LENGTH, MIN_LAST_NAME_LENGTH};
use crate::constants::{conservation_policy, SUPPORTED_LANGUAGES};
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::general_settings::GeneralSettings; use crate::data::general_settings::GeneralSettings;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
@ -20,8 +20,8 @@ use crate::data::new_data_conservation_policy::NewDataConservationPolicy;
use crate::data::new_notifications_settings::NewNotificationsSettings; use crate::data::new_notifications_settings::NewNotificationsSettings;
use crate::data::security_settings::{SecurityQuestion, SecuritySettings}; use crate::data::security_settings::{SecurityQuestion, SecuritySettings};
use crate::data::user::{AccountImageVisibility, UserPageVisibility}; use crate::data::user::{AccountImageVisibility, UserPageVisibility};
use crate::helpers::{account_helper, custom_emojies_helper, user_helper};
use crate::helpers::virtual_directory_helper::VirtualDirType; use crate::helpers::virtual_directory_helper::VirtualDirType;
use crate::helpers::{account_helper, custom_emojies_helper, user_helper};
use crate::routes::RequestResult; use crate::routes::RequestResult;
use crate::utils::string_utils::remove_html_nodes; use crate::utils::string_utils::remove_html_nodes;
@ -43,7 +43,10 @@ pub async fn set_general(r: &mut HttpRequestHandler) -> RequestResult {
let personal_website = r.post_url_opt("personnalWebsite", false)?; let personal_website = r.post_url_opt("personnalWebsite", false)?;
let virtual_directory = r.post_checked_virtual_directory_opt( let virtual_directory = r.post_checked_virtual_directory_opt(
"virtualDirectory", r.user_id_ref()?.id(), VirtualDirType::USER)?; "virtualDirectory",
r.user_id_ref()?.id(),
VirtualDirType::USER,
)?;
let new_settings = GeneralSettings { let new_settings = GeneralSettings {
id: r.user_id()?, id: r.user_id()?,
@ -58,7 +61,9 @@ pub async fn set_general(r: &mut HttpRequestHandler) -> RequestResult {
virtual_directory, virtual_directory,
allow_mails: r.post_bool("allow_comunic_mails")?, allow_mails: r.post_bool("allow_comunic_mails")?,
public_note: Some(r.post_content("publicNote", 0, false)?), public_note: Some(r.post_content("publicNote", 0, false)?),
location: r.post_string_optional("location").map(|s| remove_html_nodes(&s)), location: r
.post_string_optional("location")
.map(|s| remove_html_nodes(&s)),
}; };
account_helper::set_general(&new_settings)?; account_helper::set_general(&new_settings)?;
@ -229,33 +234,57 @@ pub async fn set_data_conservation_policy(r: &mut HttpRequestHandler) -> Request
delete_notifications_after: r.post_positive_u64_opt("notification_lifetime")?, delete_notifications_after: r.post_positive_u64_opt("notification_lifetime")?,
delete_comments_after: r.post_positive_u64_opt("comments_lifetime")?, delete_comments_after: r.post_positive_u64_opt("comments_lifetime")?,
delete_posts_after: r.post_positive_u64_opt("posts_lifetime")?, delete_posts_after: r.post_positive_u64_opt("posts_lifetime")?,
delete_conversation_messages_after: r.post_positive_u64_opt("conversation_messages_lifetime")?, delete_conversation_messages_after: r
.post_positive_u64_opt("conversation_messages_lifetime")?,
delete_likes_after: r.post_positive_u64_opt("likes_lifetime")?, delete_likes_after: r.post_positive_u64_opt("likes_lifetime")?,
}; };
// Check policy is respected // Check policy is respected
if policy.delete_account_after.map(|f| f < conservation_policy::MIN_INACTIVE_ACCOUNT_LIFETIME.as_secs()) == Some(true) { if policy
.delete_account_after
.map(|f| f < conservation_policy::MIN_INACTIVE_ACCOUNT_LIFETIME.as_secs())
== Some(true)
{
r.bad_request("Invalid lifetime for inactive account !".to_string())?; r.bad_request("Invalid lifetime for inactive account !".to_string())?;
} }
if policy.delete_notifications_after.map(|f| f < conservation_policy::MIN_NOTIFICATIONS_LIFETIME.as_secs()) == Some(true) { if policy
.delete_notifications_after
.map(|f| f < conservation_policy::MIN_NOTIFICATIONS_LIFETIME.as_secs())
== Some(true)
{
r.bad_request("Invalid lifetime for notifications !".to_string())?; r.bad_request("Invalid lifetime for notifications !".to_string())?;
} }
if policy.delete_comments_after.map(|f| f < conservation_policy::MIN_COMMENTS_LIFETIME.as_secs()) == Some(true) { if policy
.delete_comments_after
.map(|f| f < conservation_policy::MIN_COMMENTS_LIFETIME.as_secs())
== Some(true)
{
r.bad_request("Invalid lifetime for comments !".to_string())?; r.bad_request("Invalid lifetime for comments !".to_string())?;
} }
if policy.delete_posts_after.map(|f| f < conservation_policy::MIN_POSTS_LIFETIME.as_secs()) == Some(true) { if policy
.delete_posts_after
.map(|f| f < conservation_policy::MIN_POSTS_LIFETIME.as_secs())
== Some(true)
{
r.bad_request("Invalid lifetime for posts !".to_string())?; r.bad_request("Invalid lifetime for posts !".to_string())?;
} }
if policy.delete_conversation_messages_after.map(|f| f < conservation_policy::MIN_CONVERSATION_MESSAGES_LIFETIME.as_secs()) == Some(true) { if policy
.delete_conversation_messages_after
.map(|f| f < conservation_policy::MIN_CONVERSATION_MESSAGES_LIFETIME.as_secs())
== Some(true)
{
r.bad_request("Invalid lifetime for conversation messages !".to_string())?; r.bad_request("Invalid lifetime for conversation messages !".to_string())?;
} }
if policy.delete_likes_after.map(|f| f < conservation_policy::MIN_LIKES_LIFETIME.as_secs()) == Some(true) { if policy
.delete_likes_after
.map(|f| f < conservation_policy::MIN_LIKES_LIFETIME.as_secs())
== Some(true)
{
r.bad_request("Invalid lifetime for likes !".to_string())?; r.bad_request("Invalid lifetime for likes !".to_string())?;
} }

View File

@ -4,16 +4,20 @@
use crate::api_data::survey_api::SurveyAPI; use crate::api_data::survey_api::SurveyAPI;
use crate::constants::MAXIMUM_NUMBER_SURVEY_CHOICES; use crate::constants::MAXIMUM_NUMBER_SURVEY_CHOICES;
use crate::routes::RequestResult;
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::error::ResultBoxError; use crate::data::error::ResultBoxError;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::post::PostAccessLevel; use crate::data::post::PostAccessLevel;
use crate::helpers::survey_helper; use crate::helpers::survey_helper;
use crate::routes::RequestResult;
impl HttpRequestHandler { impl HttpRequestHandler {
/// Get the ID of a survey associated to a post whose ID was specified in the request /// Get the ID of a survey associated to a post whose ID was specified in the request
fn post_survey_id_from_post_id(&mut self, name: &str, min_level: PostAccessLevel) -> ResultBoxError<u64> { fn post_survey_id_from_post_id(
&mut self,
name: &str,
min_level: PostAccessLevel,
) -> ResultBoxError<u64> {
let post = self.post_post_with_access(name, min_level)?; let post = self.post_post_with_access(name, min_level)?;
survey_helper::get_id(post.id) survey_helper::get_id(post.id)
} }
@ -66,8 +70,12 @@ pub async fn create_new_choice(r: &mut HttpRequestHandler) -> RequestResult {
} }
// Check for similar choices // Check for similar choices
if survey.choices.iter().find( if survey
|c| c.name.to_lowercase().eq(&new_choice.to_lowercase())).is_some() { .choices
.iter()
.find(|c| c.name.to_lowercase().eq(&new_choice.to_lowercase()))
.is_some()
{
r.forbidden("This choice already exists!".to_string())?; r.forbidden("This choice already exists!".to_string())?;
} }

View File

@ -7,12 +7,12 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::api_data::user_info::APIUserInfo; use crate::api_data::user_info::APIUserInfo;
use crate::routes::RequestResult;
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::user::UserID; use crate::data::user::UserID;
use crate::helpers::user_helper; use crate::helpers::user_helper;
use crate::helpers::user_helper::find_user_by_id; use crate::helpers::user_helper::find_user_by_id;
use crate::routes::RequestResult;
/// Get information about a single user /// Get information about a single user
pub async fn get_single(request: &mut HttpRequestHandler) -> RequestResult { pub async fn get_single(request: &mut HttpRequestHandler) -> RequestResult {
@ -42,7 +42,10 @@ pub async fn get_multiple(request: &mut HttpRequestHandler) -> RequestResult {
"At least one user was not found!", "At least one user was not found!",
)?; )?;
map.insert(user_id.id(), APIUserInfo::new(&request.user_id_opt(), &user)?); map.insert(
user_id.id(),
APIUserInfo::new(&request.user_id_opt(), &user)?,
);
} }
request.set_response(map) request.set_response(map)
@ -52,10 +55,14 @@ pub async fn get_multiple(request: &mut HttpRequestHandler) -> RequestResult {
pub async fn get_advanced_info(request: &mut HttpRequestHandler) -> RequestResult { pub async fn get_advanced_info(request: &mut HttpRequestHandler) -> RequestResult {
let user_id = request.post_user_id("userID")?; let user_id = request.post_user_id("userID")?;
if !user_helper::can_see_user_page(&request.user_id_opt().unwrap_or(UserID::new(0)), &user_id)? { if !user_helper::can_see_user_page(&request.user_id_opt().unwrap_or(UserID::new(0)), &user_id)?
{
request.forbidden("You are not allowed to see this user page!".to_string())?; request.forbidden("You are not allowed to see this user page!".to_string())?;
} }
let user = user_helper::find_user_by_id(&user_id)?; let user = user_helper::find_user_by_id(&user_id)?;
request.set_response(APIUserInfo::new_advanced_info(&request.user_id_opt(), &user)?) request.set_response(APIUserInfo::new_advanced_info(
&request.user_id_opt(),
&user,
)?)
} }

View File

@ -3,10 +3,10 @@
//! This module contains all the base action that can be executed by the WebSocket //! This module contains all the base action that can be executed by the WebSocket
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::conversation::ConvID;
use crate::data::error::Res; use crate::data::error::Res;
use crate::data::post::PostAccessLevel; use crate::data::post::PostAccessLevel;
use crate::data::user_ws_request_handler::UserWsRequestHandler; use crate::data::user_ws_request_handler::UserWsRequestHandler;
use crate::data::conversation::ConvID;
/// Update incognito status of the connection /// Update incognito status of the connection
pub fn set_incognito(r: &mut UserWsRequestHandler) -> Res { pub fn set_incognito(r: &mut UserWsRequestHandler) -> Res {
@ -19,27 +19,35 @@ pub fn set_incognito(r: &mut UserWsRequestHandler) -> Res {
/// Register a conversation /// Register a conversation
pub fn register_conv(r: &mut UserWsRequestHandler) -> Res { pub fn register_conv(r: &mut UserWsRequestHandler) -> Res {
let conv_id = r.post_conv("convID")?.conv_id; let conv_id = r.post_conv("convID")?.conv_id;
r.update_conn(|c| { c.conversations.insert(conv_id); })?; r.update_conn(|c| {
c.conversations.insert(conv_id);
})?;
r.success("ok") r.success("ok")
} }
/// Un-register a conversation /// Un-register a conversation
pub fn unregister_conv(r: &mut UserWsRequestHandler) -> Res { pub fn unregister_conv(r: &mut UserWsRequestHandler) -> Res {
let conv_id = ConvID::new(r.post_u64("convID")?); let conv_id = ConvID::new(r.post_u64("convID")?);
r.update_conn(|c| { c.conversations.remove(&conv_id); })?; r.update_conn(|c| {
c.conversations.remove(&conv_id);
})?;
r.success("ok") r.success("ok")
} }
/// Register a post /// Register a post
pub fn register_post(r: &mut UserWsRequestHandler) -> Res { pub fn register_post(r: &mut UserWsRequestHandler) -> Res {
let post = r.post_post_with_access("postID", PostAccessLevel::BASIC_ACCESS)?; let post = r.post_post_with_access("postID", PostAccessLevel::BASIC_ACCESS)?;
r.update_conn(|c| { c.posts.insert(post.id); })?; r.update_conn(|c| {
c.posts.insert(post.id);
})?;
r.success("ok") r.success("ok")
} }
/// Un-register a post /// Un-register a post
pub fn unregister_post(r: &mut UserWsRequestHandler) -> Res { pub fn unregister_post(r: &mut UserWsRequestHandler) -> Res {
let post_id = r.post_u64("postID")?; let post_id = r.post_u64("postID")?;
r.update_conn(|c| { c.posts.remove(&post_id); })?; r.update_conn(|c| {
c.posts.remove(&post_id);
})?;
r.success("ok") r.success("ok")
} }

View File

@ -5,15 +5,17 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use actix::{Actor, ActorContext, Addr, AsyncContext, Handler, Running, StreamHandler};
use actix::prelude::*; use actix::prelude::*;
use actix::{Actor, ActorContext, Addr, AsyncContext, Handler, Running, StreamHandler};
use actix_web_actors::ws; use actix_web_actors::ws;
use actix_web_actors::ws::ProtocolError; use actix_web_actors::ws::ProtocolError;
use serde_json::Value; use serde_json::Value;
use crate::api_data::res_get_ws_token::ResGetWsToken; use crate::api_data::res_get_ws_token::ResGetWsToken;
use crate::constants::{USER_LAST_ACTIVITY_REFRESH, WS_ACCESS_TOKEN_LENGTH}; use crate::constants::{USER_LAST_ACTIVITY_REFRESH, WS_ACCESS_TOKEN_LENGTH};
use crate::controllers::user_ws_controller::ws_connections_list::{add_connection, find_connection, get_ws_connections_list, remove_connection}; use crate::controllers::user_ws_controller::ws_connections_list::{
add_connection, find_connection, get_ws_connections_list, remove_connection,
};
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::config::conf; use crate::data::config::conf;
use crate::data::error::{ExecError, Res, ResultBoxError}; use crate::data::error::{ExecError, Res, ResultBoxError};
@ -23,8 +25,8 @@ use crate::data::user_token::UserAccessToken;
use crate::data::user_ws_connection::UserWsConnection; use crate::data::user_ws_connection::UserWsConnection;
use crate::data::user_ws_message::UserWsMessage; use crate::data::user_ws_message::UserWsMessage;
use crate::data::user_ws_request_handler::{UserWsRequestHandler, UserWsResponseType}; use crate::data::user_ws_request_handler::{UserWsRequestHandler, UserWsResponseType};
use crate::helpers::{account_helper, events_helper};
use crate::helpers::events_helper::Event; use crate::helpers::events_helper::Event;
use crate::helpers::{account_helper, events_helper};
use crate::user_ws_routes::exec_user_ws_route; use crate::user_ws_routes::exec_user_ws_route;
use crate::utils::crypt_utils::rand_str; use crate::utils::crypt_utils::rand_str;
use crate::utils::date_utils::time; use crate::utils::date_utils::time;
@ -54,9 +56,7 @@ mod ws_tokens_list {
} }
lazy_static! { lazy_static! {
static ref WS_TOKENS: Arc<Mutex<Vec<WsToken>>> = { static ref WS_TOKENS: Arc<Mutex<Vec<WsToken>>> = Arc::new(Mutex::new(Vec::new()));
Arc::new(Mutex::new(Vec::new()))
};
} }
/// Get the list of WebSocket tokens /// Get the list of WebSocket tokens
@ -109,7 +109,10 @@ mod ws_connections_list {
impl UserWsConnection { impl UserWsConnection {
/// Change some of the properties of the connection /// Change some of the properties of the connection
pub fn replace<H>(mut self, do_update: H) -> Self where H: FnOnce(&mut Self) { pub fn replace<H>(mut self, do_update: H) -> Self
where
H: FnOnce(&mut Self),
{
let list = get_ws_connections_list(); let list = get_ws_connections_list();
let mut list = list.lock().unwrap(); let mut list = list.lock().unwrap();
@ -131,9 +134,8 @@ mod ws_connections_list {
} }
lazy_static! { lazy_static! {
static ref WS_CONNECTIONS: Arc<Mutex<Vec<UserWsConnection >>> = { static ref WS_CONNECTIONS: Arc<Mutex<Vec<UserWsConnection>>> =
Arc::new(Mutex::new(Vec::new())) Arc::new(Mutex::new(Vec::new()));
};
} }
/// Get the list of WebSocket connections /// Get the list of WebSocket connections
@ -230,14 +232,23 @@ impl WsSession {
/// Helper method that update user last activity at every specified amount of time /// Helper method that update user last activity at every specified amount of time
fn user_activity(&self, ctx: &mut actix_web_actors::ws::WebsocketContext<Self>) { fn user_activity(&self, ctx: &mut actix_web_actors::ws::WebsocketContext<Self>) {
if !self.incognito && account_helper::update_last_activity(&self.user_token.user_id).is_err() { if !self.incognito
eprintln!("Failed to do initial refresh of last activity for user {} !", self.user_token.user_id.id()); && account_helper::update_last_activity(&self.user_token.user_id).is_err()
{
eprintln!(
"Failed to do initial refresh of last activity for user {} !",
self.user_token.user_id.id()
);
} }
ctx.run_interval(USER_LAST_ACTIVITY_REFRESH, |_, ctx| { ctx.run_interval(USER_LAST_ACTIVITY_REFRESH, |_, ctx| {
if let Some(conn) = find_connection(ctx.address()) { if let Some(conn) = find_connection(ctx.address()) {
if !conn.incognito && account_helper::update_last_activity(conn.user_id()).is_err() { if !conn.incognito && account_helper::update_last_activity(conn.user_id()).is_err()
eprintln!("Failed to refresh last activity for user {} !", conn.user_id().id()); {
eprintln!(
"Failed to refresh last activity for user {} !",
conn.user_id().id()
);
} }
} }
}); });
@ -247,18 +258,23 @@ impl WsSession {
async fn handle_message(addr: Addr<WsSession>, msg: &str) -> Res<UserWsMessage> { async fn handle_message(addr: Addr<WsSession>, msg: &str) -> Res<UserWsMessage> {
let incoming_msg: UserWsMessage = serde_json::from_str(&msg)?; let incoming_msg: UserWsMessage = serde_json::from_str(&msg)?;
let data = incoming_msg.data.as_object() let data = incoming_msg
.data
.as_object()
.ok_or(ExecError::boxed_new("Could not parse values!"))?; .ok_or(ExecError::boxed_new("Could not parse values!"))?;
let mut args = HashMap::new(); let mut args = HashMap::new();
for (k, v) in data { for (k, v) in data {
args.insert(k.to_string(), match v { args.insert(
k.to_string(),
match v {
Value::Null => "null".to_string(), Value::Null => "null".to_string(),
Value::Bool(b) => b.to_string(), Value::Bool(b) => b.to_string(),
Value::Number(n) => n.to_string(), Value::Number(n) => n.to_string(),
Value::String(s) => s.to_string(), Value::String(s) => s.to_string(),
_ => "invalid".to_string() _ => "invalid".to_string(),
}); },
);
} }
let mut handler = UserWsRequestHandler::new( let mut handler = UserWsRequestHandler::new(
@ -268,7 +284,7 @@ impl WsSession {
let result = match exec_user_ws_route(&incoming_msg.title, &mut handler).await { let result = match exec_user_ws_route(&incoming_msg.title, &mut handler).await {
None => handler.not_found("Route not found!".to_string()), None => handler.not_found("Route not found!".to_string()),
Some(r) => r Some(r) => r,
}; };
if !handler.has_response() { if !handler.has_response() {
@ -343,8 +359,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsSession {
Ok(msg) => msg, Ok(msg) => msg,
}; };
if conf().verbose_mode if conf().verbose_mode {
{
println!("USER WEBSOCKET MESSAGE: {:?}", msg); println!("USER WEBSOCKET MESSAGE: {:?}", msg);
} }
@ -382,9 +397,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsSession {
future.into_actor(self).spawn(ctx); future.into_actor(self).spawn(ctx);
} }
ws::Message::Binary(_) => { ws::Message::Binary(_) => ctx.text("WS is text only!"),
ctx.text("WS is text only!")
}
ws::Message::Close(_) => { ws::Message::Close(_) => {
ctx.stop(); ctx.stop();
@ -394,7 +407,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsSession {
ctx.stop(); ctx.stop();
} }
ws::Message::Nop => () ws::Message::Nop => (),
} }
} }
} }
@ -407,7 +420,6 @@ pub struct WsQueuedMessage(String);
#[rtype(result = "()")] #[rtype(result = "()")]
pub struct WsCloseConnection(); pub struct WsCloseConnection();
impl Handler<WsQueuedMessage> for WsSession { impl Handler<WsQueuedMessage> for WsSession {
type Result = (); type Result = ();
@ -424,7 +436,6 @@ impl Handler<WsCloseConnection> for WsSession {
} }
} }
/// Main WebSocket route /// Main WebSocket route
pub async fn ws_route( pub async fn ws_route(
req: actix_web::HttpRequest, req: actix_web::HttpRequest,
@ -501,9 +512,13 @@ pub fn send_to_client(conn: &UserWsConnection, msg: &UserWsMessage) -> Res {
} }
/// Send a message to specific users /// Send a message to specific users
pub fn send_message_to_specific_connections<F, M>(filter: F, msg_generator: M) -> Res<Vec<UserWsConnection>> pub fn send_message_to_specific_connections<F, M>(
where F: Fn(&UserWsConnection) -> bool, filter: F,
M: Fn(&UserWsConnection) -> Res<UserWsMessage> msg_generator: M,
) -> Res<Vec<UserWsConnection>>
where
F: Fn(&UserWsConnection) -> bool,
M: Fn(&UserWsConnection) -> Res<UserWsMessage>,
{ {
let connections = get_ws_connections_list() let connections = get_ws_connections_list()
.lock() .lock()
@ -522,15 +537,22 @@ pub fn send_message_to_specific_connections<F, M>(filter: F, msg_generator: M) -
/// Check out whether user is connected or not /// Check out whether user is connected or not
pub fn is_user_connected(user_id: &UserID) -> bool { pub fn is_user_connected(user_id: &UserID) -> bool {
get_ws_connections_list().lock().unwrap().iter().any(|c| c.user_id() == user_id) get_ws_connections_list()
.lock()
.unwrap()
.iter()
.any(|c| c.user_id() == user_id)
} }
/// Check out whether user is connected or not and has at list one not incognito connection /// Check out whether user is connected or not and has at list one not incognito connection
pub fn is_user_connected_not_incognito(user_id: &UserID) -> bool { pub fn is_user_connected_not_incognito(user_id: &UserID) -> bool {
get_ws_connections_list().lock().unwrap().iter().any(|c| c.user_id() == user_id && !c.incognito) get_ws_connections_list()
.lock()
.unwrap()
.iter()
.any(|c| c.user_id() == user_id && !c.incognito)
} }
/// Disconnect a user from all the WebSockets of a given access token /// Disconnect a user from all the WebSockets of a given access token
pub fn disconnect_from_user_token(token: &UserAccessToken) -> Res { pub fn disconnect_from_user_token(token: &UserAccessToken) -> Res {
let connections = get_ws_connections_list() let connections = get_ws_connections_list()
@ -567,7 +589,9 @@ pub fn disconnect_user_from_all_sockets(user_id: &UserID) -> Res {
/// Do something with all active connections /// Do something with all active connections
pub fn foreach_connection<F>(mut f: F) -> Res pub fn foreach_connection<F>(mut f: F) -> Res
where F: FnMut(&UserWsConnection) -> Res { where
F: FnMut(&UserWsConnection) -> Res,
{
let list = get_ws_connections_list().lock().unwrap().clone(); let list = get_ws_connections_list().lock().unwrap().clone();
for conn in list.iter() { for conn in list.iter() {

View File

@ -4,10 +4,10 @@
use crate::api_data::res_find_user_by_virtual_directory::FindUserByVirtualDirectoryAPIResult; use crate::api_data::res_find_user_by_virtual_directory::FindUserByVirtualDirectoryAPIResult;
use crate::api_data::res_find_virtual_directory::ResultFindVirtualDirectory; use crate::api_data::res_find_virtual_directory::ResultFindVirtualDirectory;
use crate::routes::RequestResult;
use crate::data::base_request_handler::BaseRequestHandler; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::http_request_handler::HttpRequestHandler; use crate::data::http_request_handler::HttpRequestHandler;
use crate::helpers::{groups_helper, user_helper}; use crate::helpers::{groups_helper, user_helper};
use crate::routes::RequestResult;
/// Find a user by its virtual directory /// Find a user by its virtual directory
pub async fn find_user(r: &mut HttpRequestHandler) -> RequestResult { pub async fn find_user(r: &mut HttpRequestHandler) -> RequestResult {
@ -29,8 +29,11 @@ pub async fn find(r: &mut HttpRequestHandler) -> RequestResult {
let group = groups_helper::find_by_virtual_directory(&directory); let group = groups_helper::find_by_virtual_directory(&directory);
if user.is_err() && group.is_err() { if user.is_err() && group.is_err() {
println!("Find virtual directory errors:\n* User: {}\n* Group: {}", println!(
user.unwrap_err(), group.unwrap_err()); "Find virtual directory errors:\n* User: {}\n* Group: {}",
user.unwrap_err(),
group.unwrap_err()
);
r.not_found("Specified user / group virtual directory not found !".to_string()) r.not_found("Specified user / group virtual directory not found !".to_string())
} else { } else {
r.set_response(ResultFindVirtualDirectory::new(user, group)) r.set_response(ResultFindVirtualDirectory::new(user, group))

View File

@ -5,7 +5,7 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use crate::data::comment::Comment; use crate::data::comment::Comment;
use crate::data::conversation::{Conversation, ConvID}; use crate::data::conversation::{ConvID, Conversation};
use crate::data::conversation_message::ConversationMessage; use crate::data::conversation_message::ConversationMessage;
use crate::data::error::ResultBoxError; use crate::data::error::ResultBoxError;
use crate::data::friend::Friend; use crate::data::friend::Friend;
@ -38,7 +38,9 @@ impl AccountExport {
set.insert(self.user.id.clone()); set.insert(self.user.id.clone());
// Friends // Friends
self.friends_list.iter().for_each(|f| { set.insert(f.friend_id.clone()); }); self.friends_list.iter().for_each(|f| {
set.insert(f.friend_id.clone());
});
// Posts // Posts
for post in &self.posts { for post in &self.posts {
@ -48,20 +50,28 @@ impl AccountExport {
set.insert(id.clone()); set.insert(id.clone());
} }
comments_helper::get(post.id)?.iter().for_each(|f| { set.insert(f.user_id.clone()); }) comments_helper::get(post.id)?.iter().for_each(|f| {
set.insert(f.user_id.clone());
})
} }
// Comments // Comments
self.comments.iter().for_each(|f| { set.insert(f.user_id.clone()); }); self.comments.iter().for_each(|f| {
set.insert(f.user_id.clone());
});
// Conversation members // Conversation members
for conv in &self.conversations { for conv in &self.conversations {
conv.members_ids().into_iter().for_each(|f| { set.insert(f); }); conv.members_ids().into_iter().for_each(|f| {
set.insert(f);
});
} }
// Conversation messages // Conversation messages
for (_, conv_messages) in &self.conversation_messages { for (_, conv_messages) in &self.conversation_messages {
conv_messages.iter().for_each(|f| { set.extend(f.referenced_users_id()); }) conv_messages.iter().for_each(|f| {
set.extend(f.referenced_users_id());
})
} }
Ok(set) Ok(set)

View File

@ -6,7 +6,7 @@ use mysql::serde::{Deserializer, Serializer};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use webauthn_rs::prelude::Passkey; use webauthn_rs::prelude::Passkey;
use crate::constants::admin::{ADMIN_ROLES_LIST, AdminRole}; use crate::constants::admin::{AdminRole, ADMIN_ROLES_LIST};
use crate::data::u64_visitor::U64Visitor; use crate::data::u64_visitor::U64Visitor;
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
@ -73,14 +73,16 @@ pub struct NewAdminGeneralSettings {
impl AdminRole { impl AdminRole {
pub fn from_id(id: &str) -> Option<Self> { pub fn from_id(id: &str) -> Option<Self> {
ADMIN_ROLES_LIST.iter() ADMIN_ROLES_LIST
.iter()
.filter(|r| r.id.eq(id)) .filter(|r| r.id.eq(id))
.map(|r| r.role) .map(|r| r.role)
.next() .next()
} }
pub fn to_id(&self) -> &'static str { pub fn to_id(&self) -> &'static str {
ADMIN_ROLES_LIST.iter() ADMIN_ROLES_LIST
.iter()
.filter(|r| r.role.eq(self)) .filter(|r| r.role.eq(self))
.map(|r| r.id) .map(|r| r.id)
.next() .next()
@ -89,15 +91,21 @@ impl AdminRole {
} }
impl Serialize for AdminID { impl Serialize for AdminID {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
S: Serializer { where
S: Serializer,
{
serializer.serialize_u64(self.0) serializer.serialize_u64(self.0)
} }
} }
impl<'de> Deserialize<'de> for AdminID { impl<'de> Deserialize<'de> for AdminID {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error> where fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
D: Deserializer<'de> { where
deserializer.deserialize_u64(U64Visitor {}).map(AdminID::new) D: Deserializer<'de>,
{
deserializer
.deserialize_u64(U64Visitor {})
.map(AdminID::new)
} }
} }

View File

@ -6,18 +6,58 @@ use crate::data::user::UserID;
#[derive(serde::Serialize, serde::Deserialize, Clone)] #[derive(serde::Serialize, serde::Deserialize, Clone)]
pub enum AdminAction { pub enum AdminAction {
AuthWithResetToken, AuthWithResetToken,
AuthWithAccessKey { key: String, key_id: u64 }, AuthWithAccessKey {
RegisteredAdminKey { key_id: u64, key_name: String, target: AdminID }, key: String,
DeletedAdminKey { key_id: u64, key_name: String, target: AdminID }, key_id: u64,
GeneratedAdminResetToken { target: AdminID }, },
CreatedAdmin { id: AdminID, name: String, email: String }, RegisteredAdminKey {
UpdatedAdminGeneralSettings { target: AdminID, new_email: String, new_name: String }, key_id: u64,
AddAdminRole { target: AdminID, role: String }, key_name: String,
RemoveAdminRole { target: AdminID, role: String }, target: AdminID,
AccessUserPage { user_id: UserID, user_name: String }, },
ChangedEmailAddress { user_id: UserID, user_name: String, old_mail: String, new_mail: String }, DeletedAdminKey {
CreatePasswordRecoveryLink { user_id: UserID, user_name: String }, key_id: u64,
UnsupportedAction { raw_data: String }, key_name: String,
target: AdminID,
},
GeneratedAdminResetToken {
target: AdminID,
},
CreatedAdmin {
id: AdminID,
name: String,
email: String,
},
UpdatedAdminGeneralSettings {
target: AdminID,
new_email: String,
new_name: String,
},
AddAdminRole {
target: AdminID,
role: String,
},
RemoveAdminRole {
target: AdminID,
role: String,
},
AccessUserPage {
user_id: UserID,
user_name: String,
},
ChangedEmailAddress {
user_id: UserID,
user_name: String,
old_mail: String,
new_mail: String,
},
CreatePasswordRecoveryLink {
user_id: UserID,
user_name: String,
},
UnsupportedAction {
raw_data: String,
},
} }
impl AdminAction { impl AdminAction {

View File

@ -14,7 +14,6 @@ pub struct APIClient {
impl APIClient { impl APIClient {
pub fn is_firebase_available(&self) -> bool { pub fn is_firebase_available(&self) -> bool {
self.firebase_project_name.is_some() && self.firebase_project_name.is_some() && self.firebase_service_account_file.is_some()
self.firebase_service_account_file.is_some()
} }
} }

View File

@ -2,7 +2,6 @@
//! //!
//! Base handling code for all user requests //! Base handling code for all user requests
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error; use std::error::Error;
use std::str::from_utf8; use std::str::from_utf8;
@ -17,7 +16,7 @@ use crate::constants::PASSWORD_MIN_LENGTH;
use crate::data::admin::AdminID; use crate::data::admin::AdminID;
use crate::data::comment::Comment; use crate::data::comment::Comment;
use crate::data::config::conf; use crate::data::config::conf;
use crate::data::conversation::{ConversationMember, ConvID}; use crate::data::conversation::{ConvID, ConversationMember};
use crate::data::conversation_message::ConversationMessage; use crate::data::conversation_message::ConversationMessage;
use crate::data::custom_emoji::CustomEmoji; use crate::data::custom_emoji::CustomEmoji;
use crate::data::error::{ExecError, Res, ResultBoxError}; use crate::data::error::{ExecError, Res, ResultBoxError};
@ -26,14 +25,22 @@ use crate::data::group_id::GroupID;
use crate::data::post::{Post, PostAccessLevel}; use crate::data::post::{Post, PostAccessLevel};
use crate::data::user::UserID; use crate::data::user::UserID;
use crate::data::user_token::UserAccessToken; use crate::data::user_token::UserAccessToken;
use crate::helpers::{account_helper, admin_account_helper, admin_roles_helper, comments_helper, conversations_helper, custom_emojies_helper, friends_helper, groups_helper, posts_helper, user_helper, virtual_directory_helper};
use crate::helpers::virtual_directory_helper::VirtualDirType; use crate::helpers::virtual_directory_helper::VirtualDirType;
use crate::helpers::{
account_helper, admin_account_helper, admin_roles_helper, comments_helper,
conversations_helper, custom_emojies_helper, friends_helper, groups_helper, posts_helper,
user_helper, virtual_directory_helper,
};
use crate::routes::RequestResult; use crate::routes::RequestResult;
use crate::utils::mp3_utils::is_valid_mp3; use crate::utils::mp3_utils::is_valid_mp3;
use crate::utils::mp4_utils::is_valid_mp4; use crate::utils::mp4_utils::is_valid_mp4;
use crate::utils::pdf_utils::is_valid_pdf; use crate::utils::pdf_utils::is_valid_pdf;
use crate::utils::string_utils::{check_emoji_code, check_html_color, check_url, remove_html_nodes}; use crate::utils::string_utils::{
use crate::utils::user_data_utils::{generate_new_user_data_file_name, prepare_file_creation, user_data_path}; check_emoji_code, check_html_color, 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; use crate::utils::virtual_directories_utils;
use crate::utils::zip_utils::is_valid_zip; use crate::utils::zip_utils::is_valid_zip;
@ -42,7 +49,6 @@ struct SuccessMessage {
success: String, success: String,
} }
pub struct PostFile { pub struct PostFile {
pub name: String, pub name: String,
pub buff: actix_web::web::Bytes, pub buff: actix_web::web::Bytes,
@ -54,7 +60,6 @@ pub enum RequestValue {
File(PostFile), File(PostFile),
} }
pub trait BaseRequestHandler { pub trait BaseRequestHandler {
/// Get a parameter of the request /// Get a parameter of the request
fn post_parameter_opt(&self, name: &str) -> Option<&RequestValue>; fn post_parameter_opt(&self, name: &str) -> Option<&RequestValue>;
@ -82,14 +87,14 @@ pub trait BaseRequestHandler {
/// Success message /// Success message
fn success(&mut self, message: &str) -> RequestResult { fn success(&mut self, message: &str) -> RequestResult {
self.set_response(SuccessMessage { self.set_response(SuccessMessage {
success: message.to_string() success: message.to_string(),
}) })
} }
/// Success without message /// Success without message
fn ok(&mut self) -> RequestResult { fn ok(&mut self) -> RequestResult {
self.set_response(SuccessMessage { self.set_response(SuccessMessage {
success: "OK.".to_string() success: "OK.".to_string(),
}) })
} }
@ -175,7 +180,7 @@ pub trait BaseRequestHandler {
self.internal_error(ExecError::boxed_new(msg))?; self.internal_error(ExecError::boxed_new(msg))?;
unreachable!() unreachable!()
} }
Some(e) => Ok(e) Some(e) => Ok(e),
} }
} }
@ -186,11 +191,10 @@ pub trait BaseRequestHandler {
self.bad_request(msg.to_string())?; self.bad_request(msg.to_string())?;
unreachable!() unreachable!()
} }
Some(e) => Ok(e) Some(e) => Ok(e),
} }
} }
/// Get a user ID, if available /// Get a user ID, if available
fn user_id_opt(&self) -> Option<UserID> { fn user_id_opt(&self) -> Option<UserID> {
self.user_id_opt_ref().map(|u| u.clone()) self.user_id_opt_ref().map(|u| u.clone())
@ -212,16 +216,16 @@ pub trait BaseRequestHandler {
/// Get user ID as a reference /// Get user ID as a reference
fn user_id_ref(&self) -> ResultBoxError<&UserID> { fn user_id_ref(&self) -> ResultBoxError<&UserID> {
self.user_id_opt_ref().ok_or(ExecError::boxed_new("Could not get required user ID!")) self.user_id_opt_ref()
.ok_or(ExecError::boxed_new("Could not get required user ID!"))
} }
/// Get current admin ID, returning an error in case of error /// Get current admin ID, returning an error in case of error
fn admin_id(&self) -> Res<AdminID> { fn admin_id(&self) -> Res<AdminID> {
self.admin_id_opt().ok_or(ExecError::boxed_new("Could not get required admin ID!")) self.admin_id_opt()
.ok_or(ExecError::boxed_new("Could not get required admin ID!"))
} }
/// Check if a POST parameter was present in the request or not /// Check if a POST parameter was present in the request or not
fn has_post_parameter(&self, name: &str) -> bool { fn has_post_parameter(&self, name: &str) -> bool {
self.post_parameter_opt(name).is_some() self.post_parameter_opt(name).is_some()
@ -254,8 +258,12 @@ pub trait BaseRequestHandler {
} }
/// Get a post string, specifying minimum length /// Get a post string, specifying minimum length
fn post_string_opt(&mut self, name: &str, min_length: usize, required: bool) fn post_string_opt(
-> ResultBoxError<String> { &mut self,
name: &str,
min_length: usize,
required: bool,
) -> ResultBoxError<String> {
let param = self.post_parameter(name)?; let param = self.post_parameter(name)?;
match (&param, required) { match (&param, required) {
@ -263,14 +271,17 @@ pub trait BaseRequestHandler {
if s.len() >= min_length { if s.len() >= min_length {
Ok(s.to_string()) Ok(s.to_string())
} else { } else {
Err(self.bad_request(format!("'{}' is too short!", name)).unwrap_err()) Err(self
.bad_request(format!("'{}' is too short!", name))
.unwrap_err())
} }
} }
(_, false) => Ok(String::new()), (_, false) => Ok(String::new()),
(_, true) => (_, true) => Err(self
Err(self.bad_request(format!("'{}' is not a string!", name)).unwrap_err()), .bad_request(format!("'{}' is not a string!", name))
.unwrap_err()),
} }
} }
@ -280,9 +291,11 @@ pub trait BaseRequestHandler {
Some(RequestValue::String(s)) => { Some(RequestValue::String(s)) => {
if s.is_empty() { if s.is_empty() {
None None
} else { Some(s.to_string()) } } else {
Some(s.to_string())
} }
_ => None }
_ => None,
} }
} }
@ -324,7 +337,8 @@ pub trait BaseRequestHandler {
/// Get the extension of a file included in the request /// Get the extension of a file included in the request
fn post_file_ext(&mut self, name: &str, default: &str) -> Res<String> { fn post_file_ext(&mut self, name: &str, default: &str) -> Res<String> {
let suffix = self.post_file_type(name)? let suffix = self
.post_file_type(name)?
.parse::<mime_guess::mime::Mime>()? .parse::<mime_guess::mime::Mime>()?
.suffix() .suffix()
.map(|s| s.as_str()) .map(|s| s.as_str())
@ -335,8 +349,13 @@ pub trait BaseRequestHandler {
} }
/// Save an image in user data directory /// Save an image in user data directory
fn save_post_image(&mut self, name: &str, folder: &str, max_w: u32, max_h: u32) -> ResultBoxError<String> { fn save_post_image(
&mut self,
name: &str,
folder: &str,
max_w: u32,
max_h: u32,
) -> ResultBoxError<String> {
// Load image // Load image
let file = self.post_file(name)?; let file = self.post_file(name)?;
let mut image = image::load_from_memory(file.buff.as_ref())?; let mut image = image::load_from_memory(file.buff.as_ref())?;
@ -378,13 +397,12 @@ pub trait BaseRequestHandler {
} }
} }
// Determine image destination // Determine image destination
let target_user_data_folder = prepare_file_creation(&self.user_id()?, folder)?; let target_user_data_folder = prepare_file_creation(&self.user_id()?, folder)?;
let target_file_path = generate_new_user_data_file_name(target_user_data_folder.as_path(), "png")?; let target_file_path =
generate_new_user_data_file_name(target_user_data_folder.as_path(), "png")?;
let target_sys_path = user_data_path(target_file_path.as_path()); let target_sys_path = user_data_path(target_file_path.as_path());
// Save image // Save image
image.save_with_format(target_sys_path, ImageFormat::Png)?; image.save_with_format(target_sys_path, ImageFormat::Png)?;
@ -400,7 +418,8 @@ pub trait BaseRequestHandler {
// Determine pdf file destination // Determine pdf file destination
let target_user_data_folder = prepare_file_creation(self.user_id_ref()?, folder)?; let target_user_data_folder = prepare_file_creation(self.user_id_ref()?, folder)?;
let target_file_path = generate_new_user_data_file_name(target_user_data_folder.as_path(), ext)?; let target_file_path =
generate_new_user_data_file_name(target_user_data_folder.as_path(), ext)?;
let target_sys_path = user_data_path(target_file_path.as_path()); let target_sys_path = user_data_path(target_file_path.as_path());
std::fs::write(target_sys_path, &copied_buff.as_ref())?; std::fs::write(target_sys_path, &copied_buff.as_ref())?;
@ -518,7 +537,7 @@ pub trait BaseRequestHandler {
fn post_positive_u64_opt(&mut self, name: &str) -> Res<Option<u64>> { fn post_positive_u64_opt(&mut self, name: &str) -> Res<Option<u64>> {
match self.post_u64_opt(name, 0)? { match self.post_u64_opt(name, 0)? {
0 => Ok(None), 0 => Ok(None),
val => Ok(Some(val)) val => Ok(Some(val)),
} }
} }
@ -532,7 +551,6 @@ pub trait BaseRequestHandler {
self.post_bool(name).unwrap_or(default) self.post_bool(name).unwrap_or(default)
} }
/// Get an email included in the request /// Get an email included in the request
fn post_email(&mut self, name: &str) -> ResultBoxError<String> { fn post_email(&mut self, name: &str) -> ResultBoxError<String> {
let mail = self.post_string(name)?; let mail = self.post_string(name)?;
@ -565,14 +583,20 @@ pub trait BaseRequestHandler {
} }
/// Get the response to a key register credential included in the request /// Get the response to a key register credential included in the request
fn post_register_public_key_credential(&mut self, name: &str) -> Res<webauthn_rs::prelude::RegisterPublicKeyCredential> { fn post_register_public_key_credential(
&mut self,
name: &str,
) -> Res<webauthn_rs::prelude::RegisterPublicKeyCredential> {
let str = self.post_string(name)?; let str = self.post_string(name)?;
Ok(serde_json::from_str(&str)?) Ok(serde_json::from_str(&str)?)
} }
/// Get the response to a key authentication included in the request /// Get the response to a key authentication included in the request
fn post_auth_public_key_credential(&mut self, name: &str) -> Res<webauthn_rs::prelude::PublicKeyCredential> { fn post_auth_public_key_credential(
&mut self,
name: &str,
) -> Res<webauthn_rs::prelude::PublicKeyCredential> {
let str = self.post_string(name)?; let str = self.post_string(name)?;
Ok(serde_json::from_str(&str)?) Ok(serde_json::from_str(&str)?)
@ -619,7 +643,8 @@ pub trait BaseRequestHandler {
/// Get a list of users ID included in the request /// Get a list of users ID included in the request
fn post_users_id(&mut self, name: &str) -> ResultBoxError<HashSet<UserID>> { fn post_users_id(&mut self, name: &str) -> ResultBoxError<HashSet<UserID>> {
let users = self.post_numbers_list(name, 1)? let users = self
.post_numbers_list(name, 1)?
.iter() .iter()
.map(|u| UserID::new(u.clone())) .map(|u| UserID::new(u.clone()))
.collect::<HashSet<UserID>>(); .collect::<HashSet<UserID>>();
@ -653,23 +678,39 @@ pub trait BaseRequestHandler {
let dir = self.post_string(name)?; let dir = self.post_string(name)?;
if !virtual_directories_utils::check_virtual_directory(&dir) { if !virtual_directories_utils::check_virtual_directory(&dir) {
self.bad_request(format!("Invalid virtual directory specified in '{}' !", name))?; self.bad_request(format!(
"Invalid virtual directory specified in '{}' !",
name
))?;
} }
Ok(dir) Ok(dir)
} }
/// Get a string included in the request, with HTML codes removed /// Get a string included in the request, with HTML codes removed
fn post_string_without_html(&mut self, name: &str, min_length: usize, required: bool) -> ResultBoxError<String> { fn post_string_without_html(
Ok(remove_html_nodes(self.post_string_opt(name, min_length, required)?.as_str())) &mut self,
name: &str,
min_length: usize,
required: bool,
) -> ResultBoxError<String> {
Ok(remove_html_nodes(
self.post_string_opt(name, min_length, required)?.as_str(),
))
} }
/// Get an optional string included in the request, with HTML codes removed /// Get an optional string included in the request, with HTML codes removed
fn post_string_without_html_opt(&mut self, name: &str, min_length: usize) -> ResultBoxError<Option<String>> { fn post_string_without_html_opt(
&mut self,
name: &str,
min_length: usize,
) -> ResultBoxError<Option<String>> {
if !self.has_post_parameter(name) { if !self.has_post_parameter(name) {
Ok(None) Ok(None)
} else { } else {
Ok(Some(remove_html_nodes(self.post_string_opt(name, min_length, true)?.as_str()))) Ok(Some(remove_html_nodes(
self.post_string_opt(name, min_length, true)?.as_str(),
)))
} }
} }
@ -706,8 +747,12 @@ pub trait BaseRequestHandler {
"Requested conversation message was not found!", "Requested conversation message was not found!",
)?; )?;
if conversations_helper::get_user_membership(&self.user_id()?, conv_message.conv_id).is_err() { if conversations_helper::get_user_membership(&self.user_id()?, conv_message.conv_id)
self.forbidden("You do not belong to the conversation attached to this message!".to_string())?; .is_err()
{
self.forbidden(
"You do not belong to the conversation attached to this message!".to_string(),
)?;
} }
Ok(conv_message) Ok(conv_message)
@ -725,7 +770,11 @@ pub trait BaseRequestHandler {
} }
/// Get the ID of a group included in a request with a check for access level of current user /// Get the ID of a group included in a request with a check for access level of current user
fn post_group_id_with_access(&mut self, name: &str, min_level: GroupAccessLevel) -> ResultBoxError<GroupID> { fn post_group_id_with_access(
&mut self,
name: &str,
min_level: GroupAccessLevel,
) -> ResultBoxError<GroupID> {
let group_id = self.post_group_id(name)?; let group_id = self.post_group_id(name)?;
let access_level = groups_helper::get_access_level(&group_id, self.user_id_opt())?; let access_level = groups_helper::get_access_level(&group_id, self.user_id_opt())?;
@ -734,7 +783,10 @@ pub trait BaseRequestHandler {
} }
if access_level < min_level { if access_level < min_level {
self.forbidden("You do not have enough rights to perform what you intend to do on this group!".to_string())?; self.forbidden(
"You do not have enough rights to perform what you intend to do on this group!"
.to_string(),
)?;
} }
Ok(group_id) Ok(group_id)
@ -756,7 +808,12 @@ pub trait BaseRequestHandler {
} }
/// Get an optional virtual directory included in the request /// Get an optional virtual directory included in the request
fn post_checked_virtual_directory_opt(&mut self, name: &str, target_id: u64, target_type: VirtualDirType) -> ResultBoxError<Option<String>> { fn post_checked_virtual_directory_opt(
&mut self,
name: &str,
target_id: u64,
target_type: VirtualDirType,
) -> ResultBoxError<Option<String>> {
let dir = self.post_string_opt(name, 0, false)?; let dir = self.post_string_opt(name, 0, false)?;
if dir.is_empty() { if dir.is_empty() {
@ -771,7 +828,11 @@ pub trait BaseRequestHandler {
} }
/// Get information about a post whose ID was specified in the request /// Get information about a post whose ID was specified in the request
fn post_post_with_access(&mut self, name: &str, min_level: PostAccessLevel) -> ResultBoxError<Post> { fn post_post_with_access(
&mut self,
name: &str,
min_level: PostAccessLevel,
) -> ResultBoxError<Post> {
let post_id = self.post_u64(name)?; let post_id = self.post_u64(name)?;
let post = self.ok_or_not_found( let post = self.ok_or_not_found(
posts_helper::get_single(post_id), posts_helper::get_single(post_id),
@ -795,8 +856,12 @@ pub trait BaseRequestHandler {
if comment.user_id != self.user_id_or_invalid() { if comment.user_id != self.user_id_or_invalid() {
let post = posts_helper::get_single(comment.post_id)?; let post = posts_helper::get_single(comment.post_id)?;
if posts_helper::get_access_level(&post, &self.user_id_opt())? == PostAccessLevel::NO_ACCESS { if posts_helper::get_access_level(&post, &self.user_id_opt())?
self.forbidden("You are not allowed to access this post information !".to_string())?; == PostAccessLevel::NO_ACCESS
{
self.forbidden(
"You are not allowed to access this post information !".to_string(),
)?;
} }
} }
@ -815,7 +880,12 @@ pub trait BaseRequestHandler {
} }
/// Get a content of a post and sanitize it /// Get a content of a post and sanitize it
fn post_content(&mut self, name: &str, min_len: usize, required: bool) -> ResultBoxError<String> { fn post_content(
&mut self,
name: &str,
min_len: usize,
required: bool,
) -> ResultBoxError<String> {
let content = self.post_string_opt(name, min_len, required)?; let content = self.post_string_opt(name, min_len, required)?;
if content.contains("data:image") { if content.contains("data:image") {

View File

@ -59,7 +59,10 @@ impl SdpType {
match val { match val {
"offer" => Ok(SdpType::Offer), "offer" => Ok(SdpType::Offer),
"answer" => Ok(SdpType::Answer), "answer" => Ok(SdpType::Answer),
_ => Err(ExecError::boxed_new(&format!("SDP type {} is unknown!", val))) _ => Err(ExecError::boxed_new(&format!(
"SDP type {} is unknown!",
val
))),
} }
} }
} }

View File

@ -37,7 +37,6 @@ pub struct IndependentPushService {
pub public_url: String, pub public_url: String,
} }
#[derive(Debug, serde::Serialize, serde::Deserialize)] #[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum BannerNature { pub enum BannerNature {
@ -57,8 +56,7 @@ pub struct Banner {
impl Banner { impl Banner {
pub fn is_visible(&self) -> bool { pub fn is_visible(&self) -> bool {
self.enabled && self.enabled && self.expire.map(|v| v < 1 || v > time()).unwrap_or(true)
self.expire.map(|v| v < 1 || v > time()).unwrap_or(true)
} }
} }
@ -110,7 +108,8 @@ impl Config {
Yaml::BadValue => { Yaml::BadValue => {
panic!("{} is missing", name); panic!("{} is missing", name);
} }
}.to_string(); }
.to_string();
// Check if we have to get an environment variable // Check if we have to get an environment variable
Self::parse_string_val(val) Self::parse_string_val(val)
@ -126,7 +125,6 @@ impl Config {
/// Load the configuration from a given path /// Load the configuration from a given path
pub fn load(path: &str) -> Result<(), Box<dyn Error>> { pub fn load(path: &str) -> Result<(), Box<dyn Error>> {
// Read & parse configuration // Read & parse configuration
let conf_str = std::fs::read_to_string(path)?; let conf_str = std::fs::read_to_string(path)?;
let parsed = YamlLoader::load_from_str(&conf_str)?; let parsed = YamlLoader::load_from_str(&conf_str)?;
@ -153,12 +151,19 @@ impl Config {
enabled: Config::yaml_bool(parsed_rtc, "enabled"), enabled: Config::yaml_bool(parsed_rtc, "enabled"),
ip: Config::yaml_str(parsed_rtc, "ip"), ip: Config::yaml_str(parsed_rtc, "ip"),
token: Config::yaml_str(parsed_rtc, "token"), token: Config::yaml_str(parsed_rtc, "token"),
ice_servers: parsed_rtc["ice-servers"].as_vec().unwrap().iter() ice_servers: parsed_rtc["ice-servers"]
.map(|f| Self::parse_string_val(f.as_str().unwrap().to_string())).collect(), .as_vec()
.unwrap()
.iter()
.map(|f| Self::parse_string_val(f.as_str().unwrap().to_string()))
.collect(),
max_users_per_calls: Config::yaml_u64(parsed_rtc, "max-users-per-calls"), max_users_per_calls: Config::yaml_u64(parsed_rtc, "max-users-per-calls"),
allow_video: Config::yaml_bool(parsed_rtc, "allow-video"), allow_video: Config::yaml_bool(parsed_rtc, "allow-video"),
max_users_per_video_calls: Config::yaml_u64(parsed_rtc, "max-users-per-video-calls"), max_users_per_video_calls: Config::yaml_u64(
}) parsed_rtc,
"max-users-per-video-calls",
),
}),
}; };
let proxy = Config::yaml_str(parsed, "proxy"); let proxy = Config::yaml_str(parsed, "proxy");
@ -171,7 +176,7 @@ impl Config {
control_url: Config::yaml_str(parsed_independent_push_service, "control-url"), control_url: Config::yaml_str(parsed_independent_push_service, "control-url"),
control_token: Config::yaml_str(parsed_independent_push_service, "control-token"), control_token: Config::yaml_str(parsed_independent_push_service, "control-token"),
public_url: Config::yaml_str(parsed_independent_push_service, "public-url"), public_url: Config::yaml_str(parsed_independent_push_service, "public-url"),
}) }),
}; };
let parsed_banner = &parsed["banner"]; let parsed_banner = &parsed["banner"];
@ -181,38 +186,42 @@ impl Config {
enabled: Self::yaml_bool(parsed_banner, "enabled"), enabled: Self::yaml_bool(parsed_banner, "enabled"),
expire: match Self::yaml_u64(parsed_banner, "expire") { expire: match Self::yaml_u64(parsed_banner, "expire") {
0 => None, 0 => None,
v => Some(v) v => Some(v),
}, },
nature: match Self::yaml_str(parsed_banner, "nature").as_str() { nature: match Self::yaml_str(parsed_banner, "nature").as_str() {
"information" => BannerNature::Information, "information" => BannerNature::Information,
"warning" => BannerNature::Warning, "warning" => BannerNature::Warning,
"success" => BannerNature::Success, "success" => BannerNature::Success,
v => panic!("Invalid banner nature: {} !", v) v => panic!("Invalid banner nature: {} !", v),
}, },
message: parsed_banner["message"].as_hash().unwrap().iter() message: parsed_banner["message"]
.map(|(k, v)| ( .as_hash()
.unwrap()
.iter()
.map(|(k, v)| {
(
k.as_str().unwrap().to_string(), k.as_str().unwrap().to_string(),
Self::parse_string_val(v.as_str().unwrap().to_string())) Self::parse_string_val(v.as_str().unwrap().to_string()),
) )
})
.collect(), .collect(),
link: parsed_banner["link"].as_str() link: parsed_banner["link"]
.as_str()
.map(str::to_string) .map(str::to_string)
.map(Self::parse_string_val) .map(Self::parse_string_val)
.and_then(|s| match s.is_empty() { .and_then(|s| match s.is_empty() {
true => None, true => None,
false => Some(s) false => Some(s),
}),
}), }),
})
}; };
let forez_groups = match &parsed["forez_groups"] { let forez_groups = match &parsed["forez_groups"] {
Yaml::BadValue => vec![], Yaml::BadValue => vec![],
_ => { _ => Self::yaml_str(parsed, "forez_groups")
Self::yaml_str(parsed, "forez_groups")
.split(",") .split(",")
.map(|f| GroupID::new(f.trim().parse::<u64>().unwrap())) .map(|f| GroupID::new(f.trim().parse::<u64>().unwrap()))
.collect() .collect(),
}
}; };
let conf = Config { let conf = Config {
@ -235,7 +244,7 @@ impl Config {
proxy: match proxy.as_ref() { proxy: match proxy.as_ref() {
"none" => None, "none" => None,
s => Some(s.to_string()) s => Some(s.to_string()),
}, },
verbose_mode: Config::yaml_bool(parsed, "verbose-mode"), verbose_mode: Config::yaml_bool(parsed, "verbose-mode"),
@ -270,8 +279,7 @@ impl Config {
/// Check if rtc relay is enabled /// Check if rtc relay is enabled
pub fn is_rtc_relay_enabled(&self) -> bool { pub fn is_rtc_relay_enabled(&self) -> bool {
self.rtc_relay.is_some() self.rtc_relay.is_some() && self.rtc_relay.as_ref().unwrap().enabled
&& self.rtc_relay.as_ref().unwrap().enabled
} }
/// Check if independent push notifications service is enabled /// Check if independent push notifications service is enabled

View File

@ -3,8 +3,8 @@
//! @author Pierre Hubert //! @author Pierre Hubert
use crate::data::group_id::GroupID; use crate::data::group_id::GroupID;
use crate::data::user::UserID;
use crate::data::group_member::GroupMembershipLevel; use crate::data::group_member::GroupMembershipLevel;
use crate::data::user::UserID;
#[derive(Copy, Debug, PartialEq, Eq, Clone, Hash)] #[derive(Copy, Debug, PartialEq, Eq, Clone, Hash)]
pub struct ConvID(u64); pub struct ConvID(u64);
@ -71,8 +71,7 @@ impl Conversation {
/// Check out whether a given user is an admin of a conversation or not /// Check out whether a given user is an admin of a conversation or not
pub fn is_admin(&self, user_id: &UserID) -> bool { pub fn is_admin(&self, user_id: &UserID) -> bool {
self self.members
.members
.iter() .iter()
.any(|m| m.user_id == user_id && m.is_admin) .any(|m| m.user_id == user_id && m.is_admin)
} }
@ -94,10 +93,7 @@ impl Conversation {
/// Check out whether a user is the last administrator of a conversation or not /// Check out whether a user is the last administrator of a conversation or not
pub fn is_last_admin(&self, user_id: &UserID) -> bool { pub fn is_last_admin(&self, user_id: &UserID) -> bool {
let admins: Vec<&ConversationMember> = self.members let admins: Vec<&ConversationMember> = self.members.iter().filter(|m| m.is_admin).collect();
.iter()
.filter(|m| m.is_admin)
.collect();
admins.len() == 1 && admins[0].user_id == user_id admins.len() == 1 && admins[0].user_id == user_id
} }

View File

@ -42,10 +42,22 @@ pub enum ConversationServerMessageType {
impl ConversationServerMessageType { impl ConversationServerMessageType {
pub fn to_db(&self) -> String { pub fn to_db(&self) -> String {
let info = match self { let info = match self {
ConversationServerMessageType::UserCreatedConversation(u) => ("user_created_conv", Some(u.clone()), None), ConversationServerMessageType::UserCreatedConversation(u) => {
ConversationServerMessageType::UserAddedAnotherUserToConversation(msg) => ("user_added_another_user", Some(msg.user_who_added.clone()), Some(msg.user_added.clone())), ("user_created_conv", Some(u.clone()), None)
ConversationServerMessageType::UserLeftConversation(u) => ("user_left", Some(u.clone()), None), }
ConversationServerMessageType::UserRemovedFromConversation(msg) => ("user_removed", Some(msg.user_who_removed.clone()), Some(msg.user_removed.clone())), ConversationServerMessageType::UserAddedAnotherUserToConversation(msg) => (
"user_added_another_user",
Some(msg.user_who_added.clone()),
Some(msg.user_added.clone()),
),
ConversationServerMessageType::UserLeftConversation(u) => {
("user_left", Some(u.clone()), None)
}
ConversationServerMessageType::UserRemovedFromConversation(msg) => (
"user_removed",
Some(msg.user_who_removed.clone()),
Some(msg.user_removed.clone()),
),
}; };
format!( format!(
@ -69,30 +81,34 @@ impl ConversationServerMessageType {
split[0], split[0],
match id_1 { match id_1 {
0 => None, 0 => None,
id => Some(UserID::new(id)) id => Some(UserID::new(id)),
}, },
match id_2 { match id_2 {
0 => None, 0 => None,
id => Some(UserID::new(id)) id => Some(UserID::new(id)),
}, },
); );
match info { match info {
("user_created_conv", Some(user_id), _) => Ok(Self::UserCreatedConversation(user_id)), ("user_created_conv", Some(user_id), _) => Ok(Self::UserCreatedConversation(user_id)),
("user_added_another_user", Some(user_id), Some(user_2)) => Ok(Self::UserAddedAnotherUserToConversation(UserAddedAnotherUserToConversation { ("user_added_another_user", Some(user_id), Some(user_2)) => Ok(
Self::UserAddedAnotherUserToConversation(UserAddedAnotherUserToConversation {
user_who_added: user_id, user_who_added: user_id,
user_added: user_2, user_added: user_2,
})), }),
),
("user_left", Some(user_id), _) => Ok(Self::UserLeftConversation(user_id)), ("user_left", Some(user_id), _) => Ok(Self::UserLeftConversation(user_id)),
("user_removed", Some(user_id), Some(user_2)) => Ok(Self::UserRemovedFromConversation(UserRemovedAnotherUserToConversation { ("user_removed", Some(user_id), Some(user_2)) => Ok(Self::UserRemovedFromConversation(
UserRemovedAnotherUserToConversation {
user_who_removed: user_id, user_who_removed: user_id,
user_removed: user_2, user_removed: user_2,
})), },
)),
_ => Err(ExecError::boxed_new("Unknown server message type!")) _ => Err(ExecError::boxed_new("Unknown server message type!")),
} }
} }
} }

View File

@ -7,7 +7,6 @@ use std::fmt::{Display, Formatter};
/// ///
/// @author Pierre Hubert /// @author Pierre Hubert
/// Simple result type /// Simple result type
pub type ResultExecError<E> = Result<E, ExecError>; pub type ResultExecError<E> = Result<E, ExecError>;
pub type ResultBoxError<E = ()> = Result<E, Box<dyn Error>>; pub type ResultBoxError<E = ()> = Result<E, Box<dyn Error>>;
@ -36,5 +35,4 @@ impl fmt::Display for ExecError {
} }
} }
impl error::Error for ExecError {} impl error::Error for ExecError {}

View File

@ -22,7 +22,8 @@ impl GroupVisibilityLevel {
GroupVisibilityLevel::OPEN_GROUP => "open", GroupVisibilityLevel::OPEN_GROUP => "open",
GroupVisibilityLevel::PRIVATE_GROUP => "private", GroupVisibilityLevel::PRIVATE_GROUP => "private",
GroupVisibilityLevel::SECRETE_GROUP => "secrete", GroupVisibilityLevel::SECRETE_GROUP => "secrete",
}.to_string() }
.to_string()
} }
pub fn from_api(level: &str) -> GroupVisibilityLevel { pub fn from_api(level: &str) -> GroupVisibilityLevel {
@ -30,7 +31,7 @@ impl GroupVisibilityLevel {
"open" => GroupVisibilityLevel::OPEN_GROUP, "open" => GroupVisibilityLevel::OPEN_GROUP,
"private" => GroupVisibilityLevel::PRIVATE_GROUP, "private" => GroupVisibilityLevel::PRIVATE_GROUP,
"secrete" => GroupVisibilityLevel::SECRETE_GROUP, "secrete" => GroupVisibilityLevel::SECRETE_GROUP,
_ => GroupVisibilityLevel::SECRETE_GROUP _ => GroupVisibilityLevel::SECRETE_GROUP,
} }
} }
} }
@ -49,7 +50,8 @@ impl GroupRegistrationLevel {
GroupRegistrationLevel::OPEN_REGISTRATION => "open", GroupRegistrationLevel::OPEN_REGISTRATION => "open",
GroupRegistrationLevel::MODERATED_REGISTRATION => "moderated", GroupRegistrationLevel::MODERATED_REGISTRATION => "moderated",
GroupRegistrationLevel::CLOSED_REGISTRATION => "closed", GroupRegistrationLevel::CLOSED_REGISTRATION => "closed",
}.to_string() }
.to_string()
} }
pub fn from_api(level: &str) -> GroupRegistrationLevel { pub fn from_api(level: &str) -> GroupRegistrationLevel {
@ -77,14 +79,15 @@ impl GroupPostsCreationLevel {
match self { match self {
GroupPostsCreationLevel::POSTS_LEVEL_MODERATORS => "moderators", GroupPostsCreationLevel::POSTS_LEVEL_MODERATORS => "moderators",
GroupPostsCreationLevel::POSTS_LEVEL_ALL_MEMBERS => "members", GroupPostsCreationLevel::POSTS_LEVEL_ALL_MEMBERS => "members",
}.to_string() }
.to_string()
} }
pub fn from_api(level: &str) -> GroupPostsCreationLevel { pub fn from_api(level: &str) -> GroupPostsCreationLevel {
match level { match level {
"members" => GroupPostsCreationLevel::POSTS_LEVEL_ALL_MEMBERS, "members" => GroupPostsCreationLevel::POSTS_LEVEL_ALL_MEMBERS,
"moderators" => GroupPostsCreationLevel::POSTS_LEVEL_MODERATORS, "moderators" => GroupPostsCreationLevel::POSTS_LEVEL_MODERATORS,
_ => GroupPostsCreationLevel::POSTS_LEVEL_MODERATORS _ => GroupPostsCreationLevel::POSTS_LEVEL_MODERATORS,
} }
} }
} }

View File

@ -23,7 +23,9 @@ impl GroupMembershipLevel {
pub fn is_at_least_member(&self) -> bool { pub fn is_at_least_member(&self) -> bool {
matches!( matches!(
&self, &self,
GroupMembershipLevel::ADMINISTRATOR | GroupMembershipLevel::MODERATOR | GroupMembershipLevel::MEMBER GroupMembershipLevel::ADMINISTRATOR
| GroupMembershipLevel::MODERATOR
| GroupMembershipLevel::MEMBER
) )
} }
@ -35,7 +37,8 @@ impl GroupMembershipLevel {
GroupMembershipLevel::INVITED => "invited", GroupMembershipLevel::INVITED => "invited",
GroupMembershipLevel::PENDING => "pending", GroupMembershipLevel::PENDING => "pending",
GroupMembershipLevel::VISITOR => "visitor", GroupMembershipLevel::VISITOR => "visitor",
}.to_string() }
.to_string()
} }
pub fn from_api(level: &str) -> GroupMembershipLevel { pub fn from_api(level: &str) -> GroupMembershipLevel {

View File

@ -1,9 +1,9 @@
use actix_http::header::{HeaderName, HeaderValue};
use std::collections::HashMap; use std::collections::HashMap;
use std::str::FromStr; use std::str::FromStr;
use actix_http::header::{HeaderName, HeaderValue};
use actix_web::{HttpRequest, HttpResponse, HttpResponseBuilder};
use actix_web::http::StatusCode; use actix_web::http::StatusCode;
use actix_web::{HttpRequest, HttpResponse, HttpResponseBuilder};
use serde::Serialize; use serde::Serialize;
use crate::api_data::http_error::HttpError; use crate::api_data::http_error::HttpError;
@ -61,15 +61,14 @@ impl HttpRequestHandler {
// Put additional headers if required // Put additional headers if required
for (k, v) in &self.headers { for (k, v) in &self.headers {
response.headers_mut().insert(HeaderName::from_str(k)?, response
HeaderValue::from_str(v)?, .headers_mut()
); .insert(HeaderName::from_str(k)?, HeaderValue::from_str(v)?);
} }
Ok(response) Ok(response)
} }
/// Get the path of the request /// Get the path of the request
pub fn request_path(&self) -> String { pub fn request_path(&self) -> String {
self.request.path().to_string() self.request.path().to_string()
@ -89,18 +88,22 @@ impl HttpRequestHandler {
"Client not recognized!", "Client not recognized!",
)?; )?;
if let Some(allowed_origin) = &client.domain { if let Some(allowed_origin) = &client.domain {
match self.request.headers().get("Referer") { match self.request.headers().get("Referer") {
None => self.bad_request("Unknown origin!".to_string())?, None => self.bad_request("Unknown origin!".to_string())?,
Some(s) => { Some(s) => {
if !s.to_str()?.starts_with(allowed_origin) { if !s.to_str()?.starts_with(allowed_origin) {
self.bad_request("Use of this client is prohibited from this domain!".to_string())?; self.bad_request(
"Use of this client is prohibited from this domain!".to_string(),
)?;
} }
} }
} }
self.headers.insert("Access-Control-Allow-Origin".to_string(), allowed_origin.to_string()); self.headers.insert(
"Access-Control-Allow-Origin".to_string(),
allowed_origin.to_string(),
);
} }
self.client = Some(client); self.client = Some(client);
@ -126,9 +129,9 @@ impl HttpRequestHandler {
Err(e) => { Err(e) => {
println!("Error marking login tokens as invalid: {}", e); println!("Error marking login tokens as invalid: {}", e);
self.response = Some( self.response = Some(
actix_web::HttpResponse:: actix_web::HttpResponse::build(actix_web::http::StatusCode::from_u16(412)?)
build(actix_web::http::StatusCode::from_u16(412)?) .json(HttpError::new(412, "Please check your login tokens!")),
.json(HttpError::new(412, "Please check your login tokens!"))); );
Err(e) Err(e)
} }
} }
@ -174,8 +177,9 @@ impl BaseRequestHandler for HttpRequestHandler {
/// Set request error /// Set request error
fn set_error(&mut self, error: HttpError) { fn set_error(&mut self, error: HttpError) {
self.response = Some(HttpResponseBuilder::new(StatusCode::from_u16(error.error.code).unwrap()) self.response = Some(
.json(error)); HttpResponseBuilder::new(StatusCode::from_u16(error.error.code).unwrap()).json(error),
);
} }
/// Get the remote IP address /// Get the remote IP address

View File

@ -1,48 +1,48 @@
pub mod error;
pub mod config; pub mod config;
pub mod error;
pub mod api_client;
pub mod base_request_handler; pub mod base_request_handler;
pub mod http_request_handler; pub mod http_request_handler;
pub mod user_ws_request_handler; pub mod user_ws_request_handler;
pub mod api_client;
pub mod user; pub mod account_export;
pub mod user_token; pub mod admin;
pub mod custom_emoji; pub mod admin_action_log;
pub mod new_conversation; pub mod call_signal;
pub mod comment;
pub mod conversation; pub mod conversation;
pub mod conversation_message; pub mod conversation_message;
pub mod new_conversation_message; pub mod custom_emoji;
pub mod group_id;
pub mod group;
pub mod new_group;
pub mod group_member;
pub mod global_search_result;
pub mod friend; pub mod friend;
pub mod friendship_status; pub mod friendship_status;
pub mod post; pub mod general_settings;
pub mod survey; pub mod global_search_result;
pub mod comment; pub mod group;
pub mod group_id;
pub mod group_member;
pub mod lang_settings;
pub mod new_account;
pub mod new_conversation;
pub mod new_conversation_message;
pub mod new_custom_emoji;
pub mod new_data_conservation_policy;
pub mod new_group;
pub mod new_notifications_settings;
pub mod new_survey; pub mod new_survey;
pub mod notification; pub mod notification;
pub mod user_membership; pub mod post;
pub mod new_account;
pub mod account_export;
pub mod user_like;
pub mod survey_response;
pub mod general_settings;
pub mod lang_settings;
pub mod security_settings;
pub mod new_data_conservation_policy;
pub mod new_custom_emoji;
pub mod user_ws_message;
pub mod user_ws_connection;
pub mod call_signal;
pub mod new_notifications_settings;
pub mod push_notification;
pub mod presence; pub mod presence;
pub mod admin; pub mod push_notification;
pub mod webauthn_config;
pub mod admin_action_log;
pub mod u64_visitor;
pub mod report; pub mod report;
pub mod security_settings;
pub mod survey;
pub mod survey_response;
pub mod u64_visitor;
pub mod user;
pub mod user_like;
pub mod user_membership;
pub mod user_token;
pub mod user_ws_connection;
pub mod user_ws_message;
pub mod webauthn_config;

View File

@ -2,9 +2,9 @@
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use crate::data::user::UserID;
use crate::data::conversation::ConvID; use crate::data::conversation::ConvID;
use crate::data::conversation_message::{ConversationMessageFile, ConversationServerMessageType}; use crate::data::conversation_message::{ConversationMessageFile, ConversationServerMessageType};
use crate::data::user::UserID;
/// Information about a new conversation message /// Information about a new conversation message
pub struct NewConversationMessage { pub struct NewConversationMessage {

View File

@ -43,7 +43,8 @@ impl NotifElemType {
NotifElemType::FRIENDSHIP_REQUEST => "friend_request", NotifElemType::FRIENDSHIP_REQUEST => "friend_request",
NotifElemType::GROUP_MEMBERSHIP => "group_membership", NotifElemType::GROUP_MEMBERSHIP => "group_membership",
NotifElemType::UNKNOWN => "", NotifElemType::UNKNOWN => "",
}.to_string() }
.to_string()
} }
pub fn to_api(&self) -> String { pub fn to_api(&self) -> String {
@ -51,7 +52,6 @@ impl NotifElemType {
} }
} }
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub enum NotifEventType { pub enum NotifEventType {
UNKNOWN, UNKNOWN,
@ -79,11 +79,19 @@ impl NotifEventType {
"elem_created" => NotifEventType::ELEM_CREATED, "elem_created" => NotifEventType::ELEM_CREATED,
"elem_updated" => NotifEventType::ELEM_UPDATED, "elem_updated" => NotifEventType::ELEM_UPDATED,
"sent_group_membership_invitation" => NotifEventType::SENT_GROUP_MEMBERSHIP_INVITATION, "sent_group_membership_invitation" => NotifEventType::SENT_GROUP_MEMBERSHIP_INVITATION,
"accepted_group_membership_invitation" => NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_INVITATION, "accepted_group_membership_invitation" => {
"rejected_group_membership_invitation" => NotifEventType::REJECTED_GROUP_MEMBERSHIP_INVITATION, NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_INVITATION
}
"rejected_group_membership_invitation" => {
NotifEventType::REJECTED_GROUP_MEMBERSHIP_INVITATION
}
"sent_group_membership_request" => NotifEventType::SENT_GROUP_MEMBERSHIP_REQUEST, "sent_group_membership_request" => NotifEventType::SENT_GROUP_MEMBERSHIP_REQUEST,
"accepted_group_membership_request" => NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_REQUEST, "accepted_group_membership_request" => {
"rejected_group_membership_request" => NotifEventType::REJECTED_GROUP_MEMBERSHIP_REQUEST, NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_REQUEST
}
"rejected_group_membership_request" => {
NotifEventType::REJECTED_GROUP_MEMBERSHIP_REQUEST
}
_ => NotifEventType::UNKNOWN, _ => NotifEventType::UNKNOWN,
} }
} }
@ -97,13 +105,22 @@ impl NotifEventType {
NotifEventType::ELEM_CREATED => "elem_created", NotifEventType::ELEM_CREATED => "elem_created",
NotifEventType::ELEM_UPDATED => "elem_updated", NotifEventType::ELEM_UPDATED => "elem_updated",
NotifEventType::SENT_GROUP_MEMBERSHIP_INVITATION => "sent_group_membership_invitation", NotifEventType::SENT_GROUP_MEMBERSHIP_INVITATION => "sent_group_membership_invitation",
NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_INVITATION => "accepted_group_membership_invitation", NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_INVITATION => {
NotifEventType::REJECTED_GROUP_MEMBERSHIP_INVITATION => "rejected_group_membership_invitation", "accepted_group_membership_invitation"
}
NotifEventType::REJECTED_GROUP_MEMBERSHIP_INVITATION => {
"rejected_group_membership_invitation"
}
NotifEventType::SENT_GROUP_MEMBERSHIP_REQUEST => "sent_group_membership_request", NotifEventType::SENT_GROUP_MEMBERSHIP_REQUEST => "sent_group_membership_request",
NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_REQUEST => "accepted_group_membership_request", NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_REQUEST => {
NotifEventType::REJECTED_GROUP_MEMBERSHIP_REQUEST => "rejected_group_membership_request", "accepted_group_membership_request"
NotifEventType::UNKNOWN => "" }
}.to_string() NotifEventType::REJECTED_GROUP_MEMBERSHIP_REQUEST => {
"rejected_group_membership_request"
}
NotifEventType::UNKNOWN => "",
}
.to_string()
} }
pub fn to_api(&self) -> String { pub fn to_api(&self) -> String {
@ -111,7 +128,6 @@ impl NotifEventType {
} }
} }
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub enum NotifEventVisibility { pub enum NotifEventVisibility {
EVENT_PRIVATE, EVENT_PRIVATE,
@ -131,7 +147,8 @@ impl NotifEventVisibility {
match self { match self {
NotifEventVisibility::EVENT_PUBLIC => "event_public", NotifEventVisibility::EVENT_PUBLIC => "event_public",
NotifEventVisibility::EVENT_PRIVATE => "event_private", NotifEventVisibility::EVENT_PRIVATE => "event_private",
}.to_string() }
.to_string()
} }
pub fn to_api(&self) -> String { pub fn to_api(&self) -> String {

View File

@ -28,7 +28,8 @@ impl PostVisibilityLevel {
PostVisibilityLevel::VISIBILITY_FRIENDS => "friends", PostVisibilityLevel::VISIBILITY_FRIENDS => "friends",
PostVisibilityLevel::VISIBILITY_USER => "private", PostVisibilityLevel::VISIBILITY_USER => "private",
PostVisibilityLevel::VISIBILITY_GROUP_MEMBERS => "members", PostVisibilityLevel::VISIBILITY_GROUP_MEMBERS => "members",
}.to_string() }
.to_string()
} }
pub fn from_api(level: &str) -> PostVisibilityLevel { pub fn from_api(level: &str) -> PostVisibilityLevel {
@ -66,7 +67,8 @@ impl PostAccessLevel {
PostAccessLevel::BASIC_ACCESS => "basic", PostAccessLevel::BASIC_ACCESS => "basic",
PostAccessLevel::INTERMEDIATE_ACCESS => "intermediate", PostAccessLevel::INTERMEDIATE_ACCESS => "intermediate",
PostAccessLevel::FULL_ACCESS => "full", PostAccessLevel::FULL_ACCESS => "full",
}.to_string() }
.to_string()
} }
} }
@ -114,7 +116,8 @@ impl PostKind {
PostKind::POST_KIND_COUNTDOWN(_) => "countdown", PostKind::POST_KIND_COUNTDOWN(_) => "countdown",
PostKind::POST_KIND_SURVEY => "survey", PostKind::POST_KIND_SURVEY => "survey",
PostKind::POST_KIND_YOUTUBE(_) => "youtube", PostKind::POST_KIND_YOUTUBE(_) => "youtube",
}.to_string() }
.to_string()
} }
} }

View File

@ -14,9 +14,9 @@ pub struct Presence {
impl PartialEq for Presence { impl PartialEq for Presence {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.user_id == other.user_id && self.user_id == other.user_id
self.year == other.year && && self.year == other.year
self.month == other.month && && self.month == other.month
self.day == other.day && self.day == other.day
} }
} }

View File

@ -67,7 +67,7 @@ pub const REPORT_CAUSES: [ReportCause; 5] = [
_id: "other", _id: "other",
label_fr: "Autre", label_fr: "Autre",
label_en: "Other", label_en: "Other",
} },
]; ];
impl ReportCause { impl ReportCause {

View File

@ -6,26 +6,21 @@ use crate::data::user::UserID;
pub struct SecurityQuestion(Option<String>, Option<String>); pub struct SecurityQuestion(Option<String>, Option<String>);
impl SecurityQuestion impl SecurityQuestion {
{ pub fn new(question: &Option<String>, answer: &Option<String>) -> SecurityQuestion {
pub fn new(question: &Option<String>, answer: &Option<String>) -> SecurityQuestion
{
SecurityQuestion(question.clone(), answer.clone()) SecurityQuestion(question.clone(), answer.clone())
} }
pub fn question(&self) -> Option<String> pub fn question(&self) -> Option<String> {
{
self.0.clone() self.0.clone()
} }
pub fn answer(&self) -> Option<String> pub fn answer(&self) -> Option<String> {
{
self.1.clone() self.1.clone()
} }
} }
pub struct SecuritySettings pub struct SecuritySettings {
{
pub id: UserID, pub id: UserID,
pub question1: SecurityQuestion, pub question1: SecurityQuestion,
pub question2: SecurityQuestion, pub question2: SecurityQuestion,

View File

@ -11,5 +11,5 @@ pub struct SurveyResponse {
pub time_sent: u64, pub time_sent: u64,
pub user_id: UserID, pub user_id: UserID,
pub survey_id: u64, pub survey_id: u64,
pub choice_id: u64 pub choice_id: u64,
} }

View File

@ -13,8 +13,10 @@ impl<'de> Visitor<'de> for U64Visitor {
formatter.write_str("An unsigned integer value") formatter.write_str("An unsigned integer value")
} }
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
E: Error, { where
E: Error,
{
Ok(v) Ok(v)
} }
} }

View File

@ -65,16 +65,22 @@ impl PartialEq<UserID> for &UserID {
} }
impl Serialize for UserID { impl Serialize for UserID {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
S: Serializer { where
S: Serializer,
{
serializer.serialize_u64(self.0) serializer.serialize_u64(self.0)
} }
} }
impl<'de> Deserialize<'de> for UserID { impl<'de> Deserialize<'de> for UserID {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error> where fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
D: Deserializer<'de> { where
deserializer.deserialize_u64(U64Visitor {}).map(|id| UserID::new(id)) D: Deserializer<'de>,
{
deserializer
.deserialize_u64(U64Visitor {})
.map(|id| UserID::new(id))
} }
} }
@ -90,7 +96,7 @@ impl UserPageVisibility {
match self { match self {
UserPageVisibility::OPEN => "open", UserPageVisibility::OPEN => "open",
UserPageVisibility::PUBLIC => "public", UserPageVisibility::PUBLIC => "public",
UserPageVisibility::PRIVATE => "private" UserPageVisibility::PRIVATE => "private",
} }
} }
} }
@ -214,9 +220,9 @@ impl User {
/// Check out whether security questions have been defined for this user or not /// Check out whether security questions have been defined for this user or not
pub fn has_security_questions(&self) -> bool { pub fn has_security_questions(&self) -> bool {
self.security_question_1.is_some() && self.security_question_1.is_some()
self.security_answer_1.is_some() && && self.security_answer_1.is_some()
self.security_question_2.is_some() && && self.security_question_2.is_some()
self.security_answer_2.is_some() && self.security_answer_2.is_some()
} }
} }

View File

@ -1,4 +1,6 @@
use crate::constants::push_notifications_db_prefix::{FIREBASE_PREFIX, INDEPENDENT_PREFIX, NONE_PREFIX}; use crate::constants::push_notifications_db_prefix::{
FIREBASE_PREFIX, INDEPENDENT_PREFIX, NONE_PREFIX,
};
use crate::constants::USER_ACCESS_TOKEN_ACTIVITY_REFRESH; use crate::constants::USER_ACCESS_TOKEN_ACTIVITY_REFRESH;
use crate::data::user::UserID; use crate::data::user::UserID;
use crate::utils::date_utils::time; use crate::utils::date_utils::time;

View File

@ -29,7 +29,6 @@ impl UserWsConnection {
&self.user_token.user_id &self.user_token.user_id
} }
/// Check out whether a connection is being used to make a call in a specific conversation or not /// Check out whether a connection is being used to make a call in a specific conversation or not
pub fn is_having_call_with_conversation(&self, conv_id: &ConvID) -> bool { pub fn is_having_call_with_conversation(&self, conv_id: &ConvID) -> bool {
if let Some(call_info) = &self.active_call { if let Some(call_info) = &self.active_call {

View File

@ -5,13 +5,13 @@ use std::collections::HashMap;
use serde::Serialize; use serde::Serialize;
use crate::api_data::http_error::HttpError; use crate::api_data::http_error::HttpError;
use crate::data::admin::AdminID;
use crate::data::base_request_handler::{BaseRequestHandler, RequestValue}; use crate::data::base_request_handler::{BaseRequestHandler, RequestValue};
use crate::data::conversation::ConvID; use crate::data::conversation::ConvID;
use crate::data::error::{Res, ResultBoxError}; use crate::data::error::{Res, ResultBoxError};
use crate::data::user_token::UserAccessToken; use crate::data::user_token::UserAccessToken;
use crate::data::user_ws_connection::UserWsConnection; use crate::data::user_ws_connection::UserWsConnection;
use crate::routes::RequestResult; use crate::routes::RequestResult;
use crate::data::admin::AdminID;
pub enum UserWsResponseType { pub enum UserWsResponseType {
SUCCESS, SUCCESS,
@ -30,10 +30,16 @@ pub struct UserWsRequestHandler {
} }
impl UserWsRequestHandler { impl UserWsRequestHandler {
pub fn new(connection: &UserWsConnection, args: HashMap<String, String>) -> UserWsRequestHandler { pub fn new(
connection: &UserWsConnection,
args: HashMap<String, String>,
) -> UserWsRequestHandler {
UserWsRequestHandler { UserWsRequestHandler {
connection: connection.clone(), connection: connection.clone(),
args: args.into_iter().map(|f| (f.0, RequestValue::String(f.1))).collect(), args: args
.into_iter()
.map(|f| (f.0, RequestValue::String(f.1)))
.collect(),
response: None, response: None,
} }
} }
@ -58,7 +64,10 @@ impl UserWsRequestHandler {
} }
/// Update information about the WebSocket connection /// Update information about the WebSocket connection
pub fn update_conn<H>(&mut self, do_updates: H) -> ResultBoxError where H: FnOnce(&mut UserWsConnection) { pub fn update_conn<H>(&mut self, do_updates: H) -> ResultBoxError
where
H: FnOnce(&mut UserWsConnection),
{
self.connection = self.connection.clone().replace(do_updates); self.connection = self.connection.clone().replace(do_updates);
Ok(()) Ok(())

View File

@ -8,7 +8,8 @@ use crate::data::config::conf;
pub fn get_wan() -> Webauthn { pub fn get_wan() -> Webauthn {
WebauthnBuilder::new( WebauthnBuilder::new(
conf().admin_url conf()
.admin_url
.replace("https://", "") .replace("https://", "")
.replace("http://", "") .replace("http://", "")
.split(':') .split(':')

View File

@ -1,7 +1,7 @@
use bcrypt::{DEFAULT_COST, hash_with_result, verify}; use bcrypt::{hash_with_result, verify, DEFAULT_COST};
use crate::constants::database_tables_names::{USERS_TABLE, USER_ACCESS_TOKENS_TABLE};
use crate::constants::{PASSWORD_RESET_TOKEN_LENGTH, PASSWORD_RESET_TOKEN_LIFETIME}; use crate::constants::{PASSWORD_RESET_TOKEN_LENGTH, PASSWORD_RESET_TOKEN_LIFETIME};
use crate::constants::database_tables_names::{USER_ACCESS_TOKENS_TABLE, USERS_TABLE};
use crate::controllers::user_ws_controller; use crate::controllers::user_ws_controller;
use crate::data::account_export::AccountExport; use crate::data::account_export::AccountExport;
use crate::data::api_client::APIClient; use crate::data::api_client::APIClient;
@ -15,10 +15,14 @@ use crate::data::report::ReportTarget;
use crate::data::security_settings::SecuritySettings; use crate::data::security_settings::SecuritySettings;
use crate::data::user::{AccountImageVisibility, User, UserID, UserPageVisibility}; use crate::data::user::{AccountImageVisibility, User, UserID, UserPageVisibility};
use crate::data::user_token::{PushNotificationToken, UserAccessToken}; use crate::data::user_token::{PushNotificationToken, UserAccessToken};
use crate::helpers::{comments_helper, conversations_helper, custom_emojies_helper, database, events_helper, forez_presence_helper, friends_helper, groups_helper, likes_helper, notifications_helper, posts_helper, push_notifications_helper, reports_helper, survey_helper, user_helper};
use crate::helpers::database::{DeleteQuery, InsertQuery, QueryInfo, RowResult, UpdateInfo}; use crate::helpers::database::{DeleteQuery, InsertQuery, QueryInfo, RowResult, UpdateInfo};
use crate::helpers::events_helper::Event; use crate::helpers::events_helper::Event;
use crate::helpers::likes_helper::LikeType; use crate::helpers::likes_helper::LikeType;
use crate::helpers::{
comments_helper, conversations_helper, custom_emojies_helper, database, events_helper,
forez_presence_helper, friends_helper, groups_helper, likes_helper, notifications_helper,
posts_helper, push_notifications_helper, reports_helper, survey_helper, user_helper,
};
use crate::utils::crypt_utils::{legacy_crypt_pass, rand_str}; use crate::utils::crypt_utils::{legacy_crypt_pass, rand_str};
use crate::utils::date_utils::{mysql_date, time}; use crate::utils::date_utils::{mysql_date, time};
use crate::utils::user_data_utils::user_data_path; use crate::utils::user_data_utils::user_data_path;
@ -68,14 +72,20 @@ pub fn login_user(email: &str, password: &str, client: &APIClient) -> ResultBoxE
.add_str("token", &new_token.token) .add_str("token", &new_token.token)
.add_u64("last_refresh", new_token.last_refresh) .add_u64("last_refresh", new_token.last_refresh)
.add_u64("timeout", new_token.timeout) .add_u64("timeout", new_token.timeout)
.add_opt_str("push_notifications_token", new_token.push_notifications_token.to_db().as_ref()) .add_opt_str(
"push_notifications_token",
new_token.push_notifications_token.to_db().as_ref(),
)
.insert_drop_result()?; .insert_drop_result()?;
Ok(new_token.token) Ok(new_token.token)
} }
/// Find a user ID based on login token /// Find a user ID based on login token
pub fn find_user_by_login_token(token: &str, client: &APIClient) -> ResultBoxError<UserAccessToken> { pub fn find_user_by_login_token(
token: &str,
client: &APIClient,
) -> ResultBoxError<UserAccessToken> {
QueryInfo::new(USER_ACCESS_TOKENS_TABLE) QueryInfo::new(USER_ACCESS_TOKENS_TABLE)
.cond_u64("client_id", client.id) .cond_u64("client_id", client.id)
.cond("token", token) .cond("token", token)
@ -195,11 +205,13 @@ pub fn change_password(user_id: &UserID, new_password: &str) -> ResultBoxError {
} }
/// Check out whether a virtual directory is taken by a user or not /// Check out whether a virtual directory is taken by a user or not
pub fn check_user_directory_availability(dir: &str, user_id: Option<UserID>) -> ResultBoxError<bool> { pub fn check_user_directory_availability(
dir: &str,
user_id: Option<UserID>,
) -> ResultBoxError<bool> {
let found_user = user_helper::find_user_by_virtual_directory(dir); let found_user = user_helper::find_user_by_virtual_directory(dir);
match (found_user, user_id) { match (found_user, user_id) {
// A user was found, but we did not specify a user // A user was found, but we did not specify a user
(Ok(_), None) => Ok(false), (Ok(_), None) => Ok(false),
@ -207,7 +219,7 @@ pub fn check_user_directory_availability(dir: &str, user_id: Option<UserID>) ->
(Ok(user), Some(id)) => Ok(user.id == id), (Ok(user), Some(id)) => Ok(user.id == id),
// No user was found, virtual directory is considered as available // No user was found, virtual directory is considered as available
(Err(_), _) => Ok(true) (Err(_), _) => Ok(true),
} }
} }
@ -219,15 +231,20 @@ pub fn update_last_activity(user_id: &UserID) -> ResultBoxError {
.exec() .exec()
} }
/// Set new general settings of an account /// Set new general settings of an account
pub fn set_general(settings: &GeneralSettings) -> ResultBoxError { pub fn set_general(settings: &GeneralSettings) -> ResultBoxError {
database::UpdateInfo::new(USERS_TABLE) database::UpdateInfo::new(USERS_TABLE)
.cond_user_id("ID", &settings.id) .cond_user_id("ID", &settings.id)
.set_str("prenom", &settings.first_name) .set_str("prenom", &settings.first_name)
.set_str("nom", &settings.last_name) .set_str("nom", &settings.last_name)
.set_legacy_bool("public", settings.page_status != UserPageVisibility::PRIVATE) .set_legacy_bool(
.set_legacy_bool("pageouverte", settings.page_status == UserPageVisibility::OPEN) "public",
settings.page_status != UserPageVisibility::PRIVATE,
)
.set_legacy_bool(
"pageouverte",
settings.page_status == UserPageVisibility::OPEN,
)
.set_legacy_bool("bloquecommentaire", settings.block_comments) .set_legacy_bool("bloquecommentaire", settings.block_comments)
.set_legacy_bool("autoriser_post_amis", settings.allow_posts_from_friends) .set_legacy_bool("autoriser_post_amis", settings.allow_posts_from_friends)
.set_legacy_bool("autorise_mail", settings.allow_mails) .set_legacy_bool("autorise_mail", settings.allow_mails)
@ -277,8 +294,7 @@ pub fn delete_account_image(user_id: &UserID) -> ResultBoxError {
let path = user_data_path(user.account_image_path.unwrap().as_ref()); let path = user_data_path(user.account_image_path.unwrap().as_ref());
if path.exists() if path.exists() {
{
std::fs::remove_file(path)?; std::fs::remove_file(path)?;
} }
@ -301,7 +317,10 @@ pub fn set_account_image(user_id: &UserID, uri: &String) -> ResultBoxError {
} }
/// Set account image visibility level /// Set account image visibility level
pub fn set_account_image_visibility(user_id: &UserID, level: AccountImageVisibility) -> ResultBoxError { pub fn set_account_image_visibility(
user_id: &UserID,
level: AccountImageVisibility,
) -> ResultBoxError {
database::UpdateInfo::new(USERS_TABLE) database::UpdateInfo::new(USERS_TABLE)
.cond_user_id("ID", user_id) .cond_user_id("ID", user_id)
.set_str("account_image_visibility", &level.to_db()) .set_str("account_image_visibility", &level.to_db())
@ -313,10 +332,16 @@ pub fn set_data_conservation_policy(new_policy: NewDataConservationPolicy) -> Re
database::UpdateInfo::new(USERS_TABLE) database::UpdateInfo::new(USERS_TABLE)
.cond_user_id("ID", &new_policy.user_id) .cond_user_id("ID", &new_policy.user_id)
.set_opt_u64_or_zero("delete_account_after", new_policy.delete_account_after) .set_opt_u64_or_zero("delete_account_after", new_policy.delete_account_after)
.set_opt_u64_or_zero("delete_notifications_after", new_policy.delete_notifications_after) .set_opt_u64_or_zero(
"delete_notifications_after",
new_policy.delete_notifications_after,
)
.set_opt_u64_or_zero("delete_comments_after", new_policy.delete_comments_after) .set_opt_u64_or_zero("delete_comments_after", new_policy.delete_comments_after)
.set_opt_u64_or_zero("delete_posts_after", new_policy.delete_posts_after) .set_opt_u64_or_zero("delete_posts_after", new_policy.delete_posts_after)
.set_opt_u64_or_zero("delete_conversation_messages_after", new_policy.delete_conversation_messages_after) .set_opt_u64_or_zero(
"delete_conversation_messages_after",
new_policy.delete_conversation_messages_after,
)
.set_opt_u64_or_zero("delete_likes_after", new_policy.delete_likes_after) .set_opt_u64_or_zero("delete_likes_after", new_policy.delete_likes_after)
.exec() .exec()
} }
@ -331,7 +356,10 @@ pub fn set_notifications_settings(new_settings: NewNotificationsSettings) -> Res
} }
/// Set new push notification token /// Set new push notification token
pub async fn set_push_notification_token(client: &UserAccessToken, new_token: PushNotificationToken) -> Res { pub async fn set_push_notification_token(
client: &UserAccessToken,
new_token: PushNotificationToken,
) -> Res {
// In case of independent push service, remove previous client // In case of independent push service, remove previous client
push_notifications_helper::un_register_from_previous_service(client).await?; push_notifications_helper::un_register_from_previous_service(client).await?;
@ -451,7 +479,6 @@ fn hash_password(pass: &str) -> Res<String> {
/// If the password is encoded using the legacy method, it is automatically upgraded in case of /// If the password is encoded using the legacy method, it is automatically upgraded in case of
/// success /// success
fn validate_password(user: &User, password: &str) -> Res<bool> { fn validate_password(user: &User, password: &str) -> Res<bool> {
// We check if the password use the new storage mechanism // We check if the password use the new storage mechanism
if user.password.starts_with("$") { if user.password.starts_with("$") {
return Ok(verify(password, &user.password)?); return Ok(verify(password, &user.password)?);
@ -472,7 +499,8 @@ fn validate_password(user: &User, password: &str) -> Res<bool> {
} }
fn db_to_user_access_token(res: &RowResult) -> Res<UserAccessToken> { fn db_to_user_access_token(res: &RowResult) -> Res<UserAccessToken> {
let push_notifications_token = PushNotificationToken::from_db(res.get_optional_str("push_notifications_token")?); let push_notifications_token =
PushNotificationToken::from_db(res.get_optional_str("push_notifications_token")?);
Ok(UserAccessToken { Ok(UserAccessToken {
id: res.get_u64("id")?, id: res.get_u64("id")?,

Some files were not shown because too many files have changed in this diff Show More