use crate::structs::ConnectionState::Disconnected;
use crate::{ConversationID, CwtchLib, ProfileIdentity};
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DefaultOnError};
use serde_repr::*;
use std::collections::HashMap;
use crate::event::{ContactIdentity, FileKey};
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub enum ConnectionState {
Disconnected,
Connecting,
Connected,
Authenticated,
Synced,
Failed,
Killed,
}
impl Default for ConnectionState {
fn default() -> ConnectionState {
Disconnected
}
}
impl From<&str> for ConnectionState {
fn from(name: &str) -> Self {
match name {
"Disconnected" => ConnectionState::Disconnected,
"Connecting" => ConnectionState::Connecting,
"Connected" => ConnectionState::Connected,
"Authenticated" => ConnectionState::Authenticated,
"Synced" => ConnectionState::Synced,
"Failed" => ConnectionState::Failed,
"Killed" => ConnectionState::Killed,
_ => ConnectionState::Disconnected,
}
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum ContactAuthorization {
Unknown,
Approved,
Blocked,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(non_snake_case)]
pub struct Attribute {
pub exists: bool,
pub value: String,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(non_snake_case)]
pub struct CwtchEvent {
pub event_type: String,
pub event_ID: String,
pub data: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "PascalCase")]
#[allow(non_snake_case)]
pub struct AccessControl {
pub blocked: bool,
pub read: bool,
pub append: bool,
pub auto_connect: bool,
pub exchange_attributes: bool,
pub share_files: bool,
pub render_images: bool
}
pub type ACL = HashMap<String, AccessControl>;
#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Conversation {
#[serde(alias = "onion")]
pub contact_id: ContactIdentity,
pub identifier: ConversationID, pub name: String,
#[serde_as(deserialize_as = "DefaultOnError")]
pub status: ConnectionState,
pub accepted: bool,
pub access_control_list: ACL,
pub blocked: bool,
pub is_group: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Server {
pub onion: String,
pub status: ConnectionState,
}
#[derive(Debug, Clone)]
pub struct Profile {
pub profile_id: ProfileIdentity,
pub nick: String,
pub image_path: String,
pub attr: HashMap<String, String>,
pub conversations: HashMap<ConversationID, Conversation>,
pub servers: HashMap<String, Server>,
}
#[derive(Debug, Serialize_repr, Deserialize_repr, PartialEq, Clone)]
#[repr(i32)]
pub enum MessageType {
TextMessage = 1,
QuotedMessage = 10,
SuggestContact = 100,
InviteGroup = 101,
FileShare = 200,
MalformedMessage,
}
impl From<i32> for MessageType {
fn from(x: i32) -> Self {
match x {
1 => MessageType::TextMessage,
10 => MessageType::QuotedMessage,
100 => MessageType::SuggestContact,
101 => MessageType::InviteGroup,
200 => MessageType::FileShare,
_ => MessageType::MalformedMessage,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct MessageWrapper {
pub o: MessageType,
pub d: String,
}
impl MessageWrapper {
pub fn from_json(json: &str) -> Self {
match serde_json::from_str(json) {
Ok(m) => m,
Err(e) => MessageWrapper {o: MessageType::MalformedMessage, d: e.to_string()}
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[allow(non_snake_case)]
pub struct QuotedMessageStructure {
pub quotedHash: String,
pub body: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[allow(non_snake_case)]
pub struct LocallyIndexedMessage {
pub Message: LocalMessage,
pub LocalIndex: i32,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[allow(non_snake_case)]
pub struct LocalMessage {
pub Timestamp: String, pub Received: String, pub PeerID: String,
pub Message: MessageWrapper,
pub Signature: String,
pub PreviousMessageSig: String,
pub ReceivedByServer: bool,
pub Acknowledged: bool, pub Error: String,
pub Flags: u64
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[allow(non_snake_case)]
pub struct SharedFile {
pub FileKey: FileKey,
pub Path: String,
pub DateShared: String,
pub Active: bool,
pub Expired: bool
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[allow(non_snake_case)]
pub struct Settings {
pub Locale: String,
pub Theme: String,
pub ThemeMode: String,
pub PreviousPid: i64,
pub ExperimentsEnabled: bool,
pub Experiments: HashMap<String, bool>,
pub BlockUnknownConnections: bool,
pub StreamerMode: bool,
pub StateRootPane: i32,
pub FirstTime: bool,
pub UIColumnModePortrait: String,
pub UIColumnModeLandscape: String,
pub DownloadPath: String,
}
pub enum Experiments {
ServersExperiment,
GroupExperiment,
FileSharingExperiment,
ImagePreviewsExperiment,
}
impl Experiments {
pub fn to_key_string(self) -> String {
match self {
Experiments::ServersExperiment => "servers-experiment".to_string(),
Experiments::GroupExperiment => "tapir-groups-experiment".to_string(),
Experiments::FileSharingExperiment => "filesharing".to_string(),
Experiments::ImagePreviewsExperiment => "filesharing-images".to_string(),
}
}
}
impl Settings {
pub fn save(&self, cwtch: &dyn CwtchLib) -> Result<(), String> {
cwtch.update_settings(&self);
return Ok(());
}
}
impl Profile {
pub fn new(
identity: ProfileIdentity,
name: &str,
picture: &str,
conversations_json: &str,
server_list: &str,
) -> Result<Profile, String> {
let conversations = match Profile::process_conversations(conversations_json) {
Ok(c) => c,
Err(e) => return Err(e),
};
let servers = match Profile::process_servers(server_list) {
Ok(s) => s,
Err(e) => return Err(e),
};
Ok(Profile {
profile_id: identity,
nick: name.to_string(),
image_path: picture.to_string(),
attr: Default::default(),
conversations,
servers: servers,
})
}
fn process_conversations(conversations_json: &str) -> Result<HashMap<ConversationID, Conversation>, String> {
let mut conversations: HashMap<ConversationID, Conversation> = HashMap::new();
if conversations_json == "null" {
return Ok(conversations);
}
let conversations_map: Vec<Conversation> = match serde_json::from_str(conversations_json) {
Ok(cm) => cm,
Err(e) => return Err(format!("invalid json: {:?}", e)),
};
for conversation in conversations_map {
conversations.insert(conversation.identifier, conversation);
}
Ok(conversations)
}
fn process_servers(servers_json: &str) -> Result<HashMap<String, Server>, String> {
let mut servers: HashMap<String, Server> = HashMap::new();
if servers_json == "null" {
return Ok(servers);
}
let servers_map: Vec<Server> = match serde_json::from_str(servers_json) {
Ok(sm) => sm,
Err(e) => return Err(format!("invalid json: {:?}", e)),
};
for server in servers_map {
servers.insert(server.onion.clone(), server);
}
Ok(servers)
}
pub fn find_conversation_id_by_handle(&self, contact_id: ContactIdentity) -> Option<ConversationID> {
match self.conversations.values().filter(|c| c.contact_id == contact_id).next() {
Some(conversation) => Some(conversation.identifier),
None => None
}
}
}