2021-09-16 01:57:36 +00:00
use crate ::structs ::ConnectionState ::Disconnected ;
2022-07-21 08:04:21 +00:00
use crate ::{ ConversationID , CwtchLib , ProfileIdentity } ;
2021-09-04 03:36:35 +00:00
use serde ::{ Deserialize , Serialize } ;
2021-09-15 19:17:45 +00:00
use serde_with ::{ serde_as , DefaultOnError } ;
2022-07-21 08:04:21 +00:00
use serde_repr ::* ;
2021-09-04 03:36:35 +00:00
use std ::collections ::HashMap ;
2023-05-07 18:21:14 +00:00
use crate ::event ::{ ContactIdentity , FileKey } ;
2021-09-15 19:17:45 +00:00
2022-07-21 08:04:21 +00:00
#[ derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq) ]
2021-09-15 19:17:45 +00:00
/// 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
2021-09-16 01:57:36 +00:00
Killed ,
2021-09-15 19:17:45 +00:00
}
impl Default for ConnectionState {
fn default ( ) -> ConnectionState {
Disconnected
}
}
2022-07-07 06:52:30 +00:00
impl From < & str > for ConnectionState {
2022-04-05 01:10:02 +00:00
/// Creates a ConnectionState from a string sent from libcwtch-go
2022-07-07 06:52:30 +00:00
fn from ( name : & str ) -> Self {
2022-04-05 01:10:02 +00:00
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 ,
}
}
}
2021-09-15 19:17:45 +00:00
#[ 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
2021-09-16 01:57:36 +00:00
Blocked ,
2021-09-15 19:17:45 +00:00
}
2021-09-04 03:36:35 +00:00
2022-04-09 17:46:09 +00:00
#[ derive(Serialize, Deserialize, Debug) ]
#[ serde(rename_all = " PascalCase " ) ]
#[ allow(non_snake_case) ]
/// Struct to deserialize the results of Get*Attribute requests into
pub struct Attribute {
/// Was the attribute value found in storage
pub exists : bool ,
/// The value of the requested attribute
pub value : String ,
}
2021-09-04 03:36:35 +00:00
#[ derive(Serialize, Deserialize, Debug) ]
#[ serde(rename_all = " PascalCase " ) ]
2021-09-11 04:57:57 +00:00
#[ allow(non_snake_case) ]
2021-09-08 07:32:56 +00:00
/// Struct to serialize/deserialize events coming off the Cwtch appbus
2021-09-04 03:36:35 +00:00
pub struct CwtchEvent {
2021-09-11 04:57:57 +00:00
/// the type of event, as defined in https://git.openprivacy.ca/cwtch.im/cwtch/src/branch/master/event/common.go
2021-09-04 03:36:35 +00:00
pub event_type : String ,
2021-09-11 04:57:57 +00:00
/// 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
2021-09-04 03:36:35 +00:00
pub data : HashMap < String , String > ,
}
2022-07-21 08:04:21 +00:00
#[ derive(Serialize, Deserialize, Debug, Clone) ]
2022-04-09 17:46:09 +00:00
#[ serde(rename_all = " PascalCase " ) ]
#[ allow(non_snake_case) ]
/// AccessControl is a type determining client assigned authorization to a peer
pub struct AccessControl {
/// Any attempts from this handle to connect are blocked
pub blocked : bool ,
/// Allows a handle to access the conversation
pub read : bool ,
/// Allows a handle to append new messages to the conversation
pub append : bool ,
2024-02-24 18:49:57 +00:00
/// Profile should automatically try to connect with peer
pub auto_connect : bool ,
/// Profile should automatically exchange attributes like Name, Profile Image, etc.
pub exchange_attributes : bool ,
/// Allows a handle to share files to a conversation
pub share_files : bool ,
/// Indicates that certain filetypes should be autodownloaded and rendered when shared by this contact
pub render_images : bool
2022-04-09 17:46:09 +00:00
}
/// represents an access control list for a conversation. Mapping handles to conversation functions
pub type ACL = HashMap < String , AccessControl > ;
2021-09-15 19:17:45 +00:00
#[ serde_as ]
2022-07-21 08:04:21 +00:00
#[ derive(Serialize, Deserialize, Debug, Clone) ]
2021-09-08 07:32:56 +00:00
#[ serde(rename_all = " camelCase " ) ]
2022-04-06 04:11:37 +00:00
/// Struct to serialize/deserialize conversations coming from libcwtch-go
pub struct Conversation {
/// onion address / id of the conversation
#[ serde(alias = " onion " ) ]
2022-07-21 08:04:21 +00:00
pub contact_id : ContactIdentity ,
2022-04-05 01:10:02 +00:00
/// unique identifier of the contact/conversation to be used in API access
2022-07-21 08:04:21 +00:00
pub identifier : ConversationID , // TODO TEST does this work here for serde deserializtion
2022-04-06 04:11:37 +00:00
/// display name of the conversation, as determined in libcwtch-go from name specified by contact
2021-09-04 03:36:35 +00:00
pub name : String ,
2021-09-16 01:57:36 +00:00
#[ serde_as(deserialize_as = " DefaultOnError " ) ]
2022-04-06 04:11:37 +00:00
// cwtch loads profile/conversation from storage and leaves status blank, it's filled in "soon" by events...
2021-09-15 19:17:45 +00:00
/// contact connection status
pub status : ConnectionState ,
2022-04-06 04:11:37 +00:00
/// has the conversation been manually accpted
2022-04-05 01:10:02 +00:00
pub accepted : bool ,
2022-04-09 17:46:09 +00:00
///represents an access control list for a conversation. Mapping handles to conversation functions
pub access_control_list : ACL ,
2022-04-06 04:11:37 +00:00
/// has the conversation been manually blocked
2022-04-05 01:10:02 +00:00
pub blocked : bool ,
2022-04-06 04:11:37 +00:00
/// is this conversation a group? if so "onion" will be a group ID
/// FIXME: deprecate
2021-09-08 07:32:56 +00:00
pub is_group : bool ,
2021-09-04 03:36:35 +00:00
//attr: HashMap<String, String>,
}
2022-07-21 08:04:21 +00:00
#[ derive(Serialize, Deserialize, Debug, Clone) ]
2021-09-08 07:32:56 +00:00
/// Struct to serialize/deserialize servers coming from libcwtch-go
2021-09-04 03:36:35 +00:00
pub struct Server {
2021-09-11 04:57:57 +00:00
/// onion address of the server
2021-09-04 03:36:35 +00:00
pub onion : String ,
2021-09-15 19:17:45 +00:00
/// server connection status
pub status : ConnectionState ,
2021-09-04 03:36:35 +00:00
}
2022-07-21 08:04:21 +00:00
#[ derive(Debug, Clone) ]
2021-09-08 07:32:56 +00:00
/// Struct to serialize/deserialize profiles coming from libcwtch-go
2021-09-04 03:36:35 +00:00
pub struct Profile {
2021-09-11 04:57:57 +00:00
/// onion address / ID of the profile
2022-07-21 08:04:21 +00:00
pub profile_id : ProfileIdentity ,
2021-09-11 04:57:57 +00:00
/// nick name of the onion as supplied by libcwtch-go based on "name" attribute
2021-09-04 03:36:35 +00:00
pub nick : String ,
2021-09-11 04:57:57 +00:00
/// path to a profile image, controled by "picture" attribute
2021-09-08 07:32:56 +00:00
pub image_path : String ,
2021-09-11 04:57:57 +00:00
/// all profile attributes
2021-09-16 01:57:36 +00:00
pub attr : HashMap < String , String > ,
2022-04-06 04:11:37 +00:00
/// map of conversation [ onion => conversation ]
2022-07-21 08:04:21 +00:00
pub conversations : HashMap < ConversationID , Conversation > ,
2021-09-11 04:57:57 +00:00
/// map of servers [ onion => server ]
2021-09-04 03:36:35 +00:00
pub servers : HashMap < String , Server > ,
}
2022-07-21 08:04:21 +00:00
#[ derive(Debug, Serialize_repr, Deserialize_repr, PartialEq, Clone) ]
#[ repr(i32) ]
/// Enum matching message types and their overlays as defined in Cwtch
/// - https://git.openprivacy.ca/cwtch.im/cwtch/src/commit/907a7ca638fbcbaac5d652608d455d4217597fa9/model/overlay.go
/// - https://git.openprivacy.ca/cwtch.im/cwtch-ui/src/commit/3a752b73972cbfc53b6da26116fe018ee2ac2343/lib/models/message.dart
pub enum MessageType {
/// A standard Cwtch message of text
TextMessage = 1 ,
/// This message is a text message but it also contains a reference to the message it is quoting
QuotedMessage = 10 ,
/// A shared contact message
SuggestContact = 100 ,
/// A group invite message
InviteGroup = 101 ,
/// a share file message
FileShare = 200 ,
/// This message is of an unsupported type so malformed
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) ]
2022-04-06 04:11:37 +00:00
/// Struct to serialize/deserialize messages sent over Cwtch between profiles / conversation
2023-05-07 18:21:14 +00:00
/// Overlay => data
/// 1 TextMessage: d = plain text message
/// 10 QuotedMessage: d = json of QuotedMessageStructure, body = plain text of message
pub struct MessageWrapper {
2021-09-11 04:57:57 +00:00
/// overlay id that the message is targeting as defined in cwtch/model/overlay.go
/// [ OverlayChat = 1, OverlayInviteContact = 100, OverlayInviteGroup = 101, OverlayFileSharing = 200 ]
2022-07-21 08:04:21 +00:00
pub o : MessageType ,
2023-05-07 18:21:14 +00:00
/// data of the message, for OverlayChat, a
2021-09-16 01:57:36 +00:00
pub d : String ,
2021-09-04 03:36:35 +00:00
}
2023-05-07 18:21:14 +00:00
impl MessageWrapper {
2022-07-21 08:04:21 +00:00
/// parse json into a Message
pub fn from_json ( json : & str ) -> Self {
match serde_json ::from_str ( json ) {
Ok ( m ) = > m ,
2023-05-07 18:21:14 +00:00
Err ( e ) = > MessageWrapper { o : MessageType ::MalformedMessage , d : e . to_string ( ) }
2022-07-21 08:04:21 +00:00
}
}
}
2023-05-07 18:21:14 +00:00
#[ derive(Debug, Serialize, Deserialize, Clone) ]
/// defined: cwtch-ui/lib/model/messages/quotedmessage.dart ~ln 15
#[ allow(non_snake_case) ]
pub struct QuotedMessageStructure {
/// hash message id of message being quoted
pub quotedHash : String ,
/// plain text of the reply message
pub body : String ,
}
#[ derive(Debug, Serialize, Deserialize, Clone) ]
/// LocallyIndexedMessage is a type wrapper around a Message and a TimeLine Index that is local to this
/// instance of the timeline.
/// defined in: cwtch/model/message.go ~ln 31
#[ allow(non_snake_case) ]
pub struct LocallyIndexedMessage {
/// The message
pub Message : LocalMessage ,
/// the local index in this timeline instance
pub LocalIndex : i32 ,
}
#[ derive(Debug, Serialize, Deserialize, Clone) ]
/// Message is a local representation of a given message sent over a group chat channel.
/// defined in: cwtch/model/message.go ~ln 38
#[ allow(non_snake_case) ]
pub struct LocalMessage {
/// Timestamp of the message from sender
pub Timestamp : String , //time.Time
/// Timestamp message was received at
pub Received : String , //time.Time
/// ContactId of the sender
pub PeerID : String ,
/// Message contents
pub Message : MessageWrapper ,
/// author's signature verifying the message
pub Signature : String ,
/// the signature of hte previous message in the sender's timeline
pub PreviousMessageSig : String ,
/// was message confirmed by server
pub ReceivedByServer : bool ,
/// was the message ack'ed by the recipient peer
pub Acknowledged : bool , // peer to peer
/// message if there was an error, empty if fine
pub Error : String ,
/// Application specific flags, useful for storing small amounts of metadata
pub Flags : u64
}
// Only for internal use?
/*
#[ derive(Debug, Serialize, Deserialize, Clone) ]
/// FilesharingOverlayMessage presents the canonical format of the File Sharing functionality Overlay Message
/// This is the format that the UI will parse to display the message
/// defined: cwtch/functionality/filesharing/filesharing_functionality.go ~ln 156
pub struct FilesharingOverlayMessage {
#[ serde(alias = " f " ) ]
pub name : String ,
#[ serde(alias = " h " ) ]
pub hash : String ,
#[ serde(alias = " n " ) ]
pub nonce : String ,
#[ serde(alias = " s " ) ]
pub size : u64 ,
}
* /
#[ derive(Debug, Serialize, Deserialize, Clone) ]
/// SharedFile struct from cwtch defined in cwtch/functionality/filesharing/filesharing_functionality.go ~ln 464
#[ allow(non_snake_case) ]
pub struct SharedFile {
/// The roothash.nonce identifier derived for this file share
pub FileKey : FileKey ,
/// Path is the OS specific location of the file
pub Path : String ,
/// DateShared is the original datetime the file was shared
/// go time.Time, => DateTime<FixedOffset>
/// DateTime::parse_from_rfc3339(cwtch_event.data["TimestampSent"].as_str()).unwrap_or( DateTime::from(Utc::now())),
pub DateShared : String ,
/// Active is true if the file is currently being shared, false otherwise
pub Active : bool ,
/// Expired is true if the file is not eligible to be shared (because e.g. it has been too long since the file was originally shared,
/// or the file no longer exists).
pub Expired : bool
}
2022-07-21 08:04:21 +00:00
#[ derive(Debug, Serialize, Deserialize, Clone) ]
2022-04-05 01:10:02 +00:00
/// 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 > ,
2022-04-06 04:11:37 +00:00
/// Should the app block unknown conversations
2022-04-05 01:10:02 +00:00
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,
2022-04-06 04:11:37 +00:00
/// Should the UI hide conversation IDs
2022-04-05 01:10:02 +00:00
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 {
2022-04-06 04:11:37 +00:00
/// experiment enabling in app management and running of Cwtch servers
2022-04-05 01:10:02 +00:00
ServersExperiment ,
2022-04-06 04:11:37 +00:00
/// experiment enabling use of Cwtch groups
2022-04-05 01:10:02 +00:00
GroupExperiment ,
2022-04-06 04:11:37 +00:00
/// experiment enabling filesharing
2022-04-05 01:10:02 +00:00
FileSharingExperiment ,
2022-04-06 04:11:37 +00:00
/// experiment enabling auto downloading of image files to Settings::DownloadPath
2022-04-05 01:10:02 +00:00
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 > {
2023-05-07 18:21:14 +00:00
cwtch . update_settings ( & self ) ;
2022-04-05 01:10:02 +00:00
return Ok ( ( ) ) ;
}
}
2021-09-04 03:36:35 +00:00
impl Profile {
2021-09-11 04:57:57 +00:00
/// Create a new profile populated from supplied data
2022-04-06 04:11:37 +00:00
/// contacts_json as supplied by libcwtch-go, a map of conversations
2021-09-11 04:57:57 +00:00
/// server_list as supplied by libcwtch-go, a map of servers
2021-09-16 01:57:36 +00:00
pub fn new (
2022-07-21 08:04:21 +00:00
identity : ProfileIdentity ,
2021-09-16 01:57:36 +00:00
name : & str ,
picture : & str ,
2022-04-06 04:11:37 +00:00
conversations_json : & str ,
2021-09-16 01:57:36 +00:00
server_list : & str ,
2022-01-16 19:24:22 +00:00
) -> Result < Profile , String > {
2022-04-06 04:11:37 +00:00
let conversations = match Profile ::process_conversations ( conversations_json ) {
2022-01-16 19:24:22 +00:00
Ok ( c ) = > c ,
2022-01-17 23:24:58 +00:00
Err ( e ) = > return Err ( e ) ,
2022-01-16 19:24:22 +00:00
} ;
let servers = match Profile ::process_servers ( server_list ) {
Ok ( s ) = > s ,
2022-01-17 23:24:58 +00:00
Err ( e ) = > return Err ( e ) ,
2022-01-16 19:24:22 +00:00
} ;
Ok ( Profile {
2022-07-21 08:04:21 +00:00
profile_id : identity ,
2021-09-16 01:57:36 +00:00
nick : name . to_string ( ) ,
image_path : picture . to_string ( ) ,
attr : Default ::default ( ) ,
2022-04-06 04:11:37 +00:00
conversations ,
2021-09-16 01:57:36 +00:00
servers : servers ,
2022-01-16 19:24:22 +00:00
} )
2021-09-04 03:36:35 +00:00
}
2022-07-21 08:04:21 +00:00
fn process_conversations ( conversations_json : & str ) -> Result < HashMap < ConversationID , Conversation > , String > {
let mut conversations : HashMap < ConversationID , Conversation > = HashMap ::new ( ) ;
2022-04-06 04:11:37 +00:00
if conversations_json = = " null " {
return Ok ( conversations ) ;
2021-09-04 03:36:35 +00:00
}
2022-04-06 04:11:37 +00:00
let conversations_map : Vec < Conversation > = match serde_json ::from_str ( conversations_json ) {
2022-01-16 19:24:22 +00:00
Ok ( cm ) = > cm ,
2022-01-17 23:24:58 +00:00
Err ( e ) = > return Err ( format! ( " invalid json: {:?} " , e ) ) ,
2022-01-16 19:24:22 +00:00
} ;
2022-04-06 04:11:37 +00:00
for conversation in conversations_map {
2022-05-02 01:34:40 +00:00
conversations . insert ( conversation . identifier , conversation ) ;
2021-09-04 03:36:35 +00:00
}
2022-04-06 04:11:37 +00:00
Ok ( conversations )
2021-09-04 03:36:35 +00:00
}
2022-01-16 19:24:22 +00:00
fn process_servers ( servers_json : & str ) -> Result < HashMap < String , Server > , String > {
2021-09-04 03:36:35 +00:00
let mut servers : HashMap < String , Server > = HashMap ::new ( ) ;
if servers_json = = " null " {
2022-01-16 19:24:22 +00:00
return Ok ( servers ) ;
2021-09-04 03:36:35 +00:00
}
2022-01-16 19:24:22 +00:00
let servers_map : Vec < Server > = match serde_json ::from_str ( servers_json ) {
Ok ( sm ) = > sm ,
2022-01-17 23:24:58 +00:00
Err ( e ) = > return Err ( format! ( " invalid json: {:?} " , e ) ) ,
2022-01-16 19:24:22 +00:00
} ;
2021-09-04 03:36:35 +00:00
for server in servers_map {
servers . insert ( server . onion . clone ( ) , server ) ;
}
2022-01-16 19:24:22 +00:00
Ok ( servers )
2021-09-04 03:36:35 +00:00
}
2022-05-02 01:34:40 +00:00
/// Find a conversation_id associated with a remote handle (used for some events with no conversation id like PeerStateChange)
2022-07-21 08:04:21 +00:00
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 ( ) {
2022-05-02 01:34:40 +00:00
Some ( conversation ) = > Some ( conversation . identifier ) ,
None = > None
}
}
2021-09-16 01:57:36 +00:00
}