libcwtch-rs/src/structs.rs

291 lines
11 KiB
Rust

use crate::structs::ConnectionState::Disconnected;
use crate::CwtchLib;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DefaultOnError};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug)]
/// Defines the states a Cwtch connection can be in
pub enum ConnectionState {
/// The Cwtch connection is not conected at all
Disconnected,
/// Cwtch is attempting to connect to the address, it may or may not be online
Connecting,
/// Cwtch has made a basic connection to the address, but not done authorization. The address is online but unverified as the desired target
Connected,
/// Cwtch has authenticated the desired connection
Authenticated,
/// In the case of a server connection, Cwtch has finished syncing messages from the server and is caught up
Synced,
/// The connection attempt failed
Failed,
/// The connection has been killed
Killed,
}
impl Default for ConnectionState {
fn default() -> ConnectionState {
Disconnected
}
}
impl ConnectionState {
/// Creates a ConnectionState from a string sent from libcwtch-go
pub fn new(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")]
/// Defines the various authorization modes a contact can be in
pub enum ContactAuthorization {
/// This is an unknown (new?) contact. The user has not approved them
Unknown,
/// The contact is approved by the user (manual action)
Approved,
/// The contact is blocked by the user, should be ignored
Blocked,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(non_snake_case)]
/// Struct to serialize/deserialize events coming off the Cwtch appbus
pub struct CwtchEvent {
/// the type of event, as defined in https://git.openprivacy.ca/cwtch.im/cwtch/src/branch/master/event/common.go
pub event_type: String,
/// event ID that can be used to respond to some events and keep them differentiated (event_ID because golang naming conventions in libCwtch-go)
pub event_ID: String,
/// a map of keys and values of arguments for the event as defined in https://git.openprivacy.ca/cwtch.im/cwtch/src/branch/master/event/common.go
pub data: HashMap<String, String>,
}
#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
/// Struct to serialize/deserialize conversations coming from libcwtch-go
pub struct Conversation {
/// onion address / id of the conversation
#[serde(alias = "onion")]
pub handle: String,
/// unique identifier of the contact/conversation to be used in API access
pub identifier: i32,
/// display name of the conversation, as determined in libcwtch-go from name specified by contact
pub name: String,
#[serde_as(deserialize_as = "DefaultOnError")]
// cwtch loads profile/conversation from storage and leaves status blank, it's filled in "soon" by events...
/// contact connection status
pub status: ConnectionState,
/// has the conversation been manually accpted
pub accepted: bool,
/// has the conversation been manually blocked
pub blocked: bool,
/// is this conversation a group? if so "onion" will be a group ID
/// FIXME: deprecate
pub is_group: bool,
//attr: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug)]
/// Struct to serialize/deserialize servers coming from libcwtch-go
pub struct Server {
/// onion address of the server
pub onion: String,
/// server connection status
pub status: ConnectionState,
}
#[derive(Debug)]
/// Struct to serialize/deserialize profiles coming from libcwtch-go
pub struct Profile {
/// onion address / ID of the profile
pub onion: String,
/// nick name of the onion as supplied by libcwtch-go based on "name" attribute
pub nick: String,
/// path to a profile image, controled by "picture" attribute
pub image_path: String,
/// all profile attributes
pub attr: HashMap<String, String>,
/// map of conversation [ onion => conversation ]
pub conversations: HashMap<String, Conversation>,
/// map of servers [ onion => server ]
pub servers: HashMap<String, Server>,
}
#[derive(Debug, Serialize, Deserialize)]
/// Struct to serialize/deserialize messages sent over Cwtch between profiles / conversation
pub struct Message {
/// overlay id that the message is targeting as defined in cwtch/model/overlay.go
/// [ OverlayChat = 1, OverlayInviteContact = 100, OverlayInviteGroup = 101, OverlayFileSharing = 200 ]
pub o: i64,
/// data of the message
pub d: String,
}
#[derive(Debug, Serialize, Deserialize)]
/// Settings as defined in libcwtch-go/utils/settings.go and should be populated by handeling the UpdateGlobalSettings event emited during cwtch.start()
#[allow(non_snake_case)]
pub struct Settings {
/// locale for the UI to use
pub Locale: String,
/// theme of the UI
pub Theme: String,
/// Theme mode of the ui, light or dark
pub ThemeMode: String,
/// previous pid of the run, managed by libcwtch-go
pub PreviousPid: i64,
/// controls if subsequent experiments are enabled at all
pub ExperimentsEnabled: bool,
/// map of experiment names and their enabled status
pub Experiments: HashMap<String, bool>,
/// Should the app block unknown conversations
pub BlockUnknownConnections: bool,
// Notification policy of a UI app
//pub NotificationPolicy: String, //Todo: NotificationPolicy struct
// Notification content to show for a UI app
//pub NotificationContent: String,
/// Should the UI hide conversation IDs
pub StreamerMode: bool,
/// Unused?
pub StateRootPane: i32,
/// is this the first run
pub FirstTime: bool,
/// UI column mode
pub UIColumnModePortrait: String,
/// UI column mode
pub UIColumnModeLandscape: String,
/// Path to download files to
pub DownloadPath: String,
// Turn on advanced tor config in the UI
//pub AllowAdvancedTorConfig: bool,
// Custom torrc value
//pub CustomTorrc: String,
// Use the value of CustomTorrc with tor
//pub UseCustomTorrc: bool,
// Unused? delete
//pub UseExternalTor: bool,
// Tor socks port, if not default
//pub CustomSocksPort: i32,
// Tor control port if not default
//pub CustomControlPort: i32,
// Use tor cache for faster start
//pub UseTorCache: bool,
// Tor config dir
//pub TorCacheDir: String,
}
/// Enum of experiment types that can be managed in Settings
pub enum Experiments {
/// experiment enabling in app management and running of Cwtch servers
ServersExperiment,
/// experiment enabling use of Cwtch groups
GroupExperiment,
/// experiment enabling filesharing
FileSharingExperiment,
/// experiment enabling auto downloading of image files to Settings::DownloadPath
ImagePreviewsExperiment,
}
impl Experiments {
/// returns the experiment settings key
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 {
/// Given a CwtchLib, handles sending an event to it with updated settings represented by this struct for saving
pub fn save(&self, cwtch: &dyn CwtchLib) -> Result<(), String> {
let settings_json = match serde_json::to_string(&self) {
Ok(s) => s,
Err(e) => return Err(e.to_string()),
};
let save_settings_event: CwtchEvent = CwtchEvent {
event_type: "UpdateGlobalSettings".to_string(),
event_ID: "0".to_string(),
data: HashMap::from([("Data".to_string(), settings_json)]),
};
let event_json = match serde_json::to_string(&save_settings_event) {
Ok(s) => s,
Err(e) => return Err(e.to_string()),
};
cwtch.send_app_event(&event_json);
return Ok(());
}
}
impl Profile {
/// Create a new profile populated from supplied data
/// contacts_json as supplied by libcwtch-go, a map of conversations
/// server_list as supplied by libcwtch-go, a map of servers
pub fn new(
identity: &str,
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 {
onion: identity.to_string(),
nick: name.to_string(),
image_path: picture.to_string(),
attr: Default::default(),
conversations,
servers: servers,
})
}
fn process_conversations(conversations_json: &str) -> Result<HashMap<String, Conversation>, String> {
let mut conversations: HashMap<String, 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.handle.clone(), 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)
}
}