aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/quassel-client.rs97
-rw-r--r--src/client/mod.rs291
-rw-r--r--src/frame/mod.rs3
-rw-r--r--src/lib.rs15
-rw-r--r--src/message/handshake/clientinit.rs30
-rw-r--r--src/message/handshake/clientinitack.rs46
-rw-r--r--src/message/handshake/clientinitreject.rs28
-rw-r--r--src/message/handshake/clientlogin.rs26
-rw-r--r--src/message/handshake/clientloginack.rs2
-rw-r--r--src/message/handshake/clientloginreject.rs26
-rw-r--r--src/message/handshake/init.rs11
-rw-r--r--src/message/handshake/mod.rs46
-rw-r--r--src/message/handshake/sessioninit.rs84
-rw-r--r--src/message/signalproxy.rs309
-rw-r--r--src/message/signalproxy/heartbeat.rs65
-rw-r--r--src/message/signalproxy/initdata.rs41
-rw-r--r--src/message/signalproxy/initrequest.rs37
-rw-r--r--src/message/signalproxy/mod.rs124
-rw-r--r--src/message/signalproxy/objects/aliasmanager.rs46
-rw-r--r--src/message/signalproxy/objects/backlogmanager.rs161
-rw-r--r--src/message/signalproxy/objects/buffersyncer.rs74
-rw-r--r--src/message/signalproxy/objects/identity.rs119
-rw-r--r--src/message/signalproxy/objects/mod.rs12
-rw-r--r--src/message/signalproxy/rpccall.rs59
-rw-r--r--src/message/signalproxy/syncmessage.rs46
-rw-r--r--src/primitive/datetime.rs10
-rw-r--r--src/primitive/message.rs101
-rw-r--r--src/primitive/string.rs7
-rw-r--r--src/primitive/variant.rs25
-rw-r--r--src/primitive/variantlist.rs1
-rw-r--r--src/session/mod.rs2
31 files changed, 1056 insertions, 888 deletions
diff --git a/src/bin/quassel-client.rs b/src/bin/quassel-client.rs
deleted file mode 100644
index fa3fdf8..0000000
--- a/src/bin/quassel-client.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-use failure::Error;
-
-extern crate libquassel;
-
-extern crate tokio;
-extern crate pretty_env_logger;
-
-use libquassel::primitive::*;
-use libquassel::message::*;
-use libquassel::client::*;
-
-use tokio::io::{AsyncRead, AsyncWrite};
-use core::marker::Unpin;
-use futures::SinkExt;
-use std::future::Future;
-
-use log::*;
-
-#[tokio::main]
-async fn main() -> Result<(), Error> {
- pretty_env_logger::init();
-
-// let mut client = client::Client::<tokio::net::TcpStream>::connect(
-// "cocaine.farm",
-// 4242,
-// true,
-// ).await.unwrap();
-
- let username = std::env::args().nth(1).expect("no username given");
- let password = std::env::args().nth(2).expect("no password given");
-
- let funcs = Funcs {
- init: InitFuncs {
- client_init_ack,
- client_init_reject,
- client_login_ack,
- client_login_reject,
- session_init
- },
- message: MessageFuncs {
- sync_message,
- rpc_call,
- init_request,
- init_data,
- heart_beat,
- heart_beat_reply
- }
- };
-
- let mut client = Client::<tokio_tls::TlsStream<tokio::net::TcpStream>>::connect_tls(
- "cocaine.farm",
- 4242,
- true,
- User {
- name: username,
- password,
- },
- //funcs,
- ).await.unwrap();
-
- client.run().await;
-
- Ok(())
-}
-
-async fn client_init_ack<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: &mut Client<T, F>, item: VariantMap) {
- use libquassel::{HandshakeSerialize, HandshakeDeserialize};
-
- info!(target: "init", "Initialization successfull");
- info!(target: "login", "Starting Login");
- let login = ClientLogin {user: client.user.name.clone(), password: client.user.password.clone()};
- client.stream.send(login.serialize().unwrap()).await.unwrap();
-}
-
-async fn client_init_reject<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: Client<T, F>, item: VariantMap) {
- error!(target: "init", "Initialization failed: {:?}", item);
-}
-
-async fn client_login_ack<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: Client<T, F>, item: VariantMap) {
- info!(target: "login", "Login successfull");
-}
-
-async fn client_login_reject<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: Client<T, F>, item: VariantMap) {
- error!(target: "login", "Login failed: {:?}", item);
-}
-
-async fn session_init<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: &mut Client<T, F>, item: VariantMap) {
- info!(target: "login", "Session Initialization finished. Switching to Connected state");
- client.state = ClientState::Connected;
-}
-
-async fn sync_message<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: Client<T, F>, item: SyncMessage) { unimplemented!() }
-async fn rpc_call<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: Client<T, F>, item: RpcCall) { unimplemented!() }
-async fn init_request<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: Client<T, F>, item: InitRequest) { unimplemented!() }
-async fn init_data<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: Client<T, F>, item: InitData) { unimplemented!() }
-async fn heart_beat<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: Client<T, F>, item: HeartBeat) { unimplemented!() }
-async fn heart_beat_reply<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: Client<T, F>, item: HeartBeatReply) { unimplemented!() }
diff --git a/src/client/mod.rs b/src/client/mod.rs
deleted file mode 100644
index cded8c9..0000000
--- a/src/client/mod.rs
+++ /dev/null
@@ -1,291 +0,0 @@
-//use std::io::BufWriter;
-use std::result::Result;
-use std::vec::Vec;
-
-use tokio::io::{AsyncRead, AsyncWrite};
-use core::marker::Unpin;
-use tokio::net::TcpStream;
-use tokio::prelude::*;
-
-use native_tls::TlsConnector;
-
-use tokio_tls;
-use tokio_tls::TlsStream;
-use tokio_util::codec::Framed;
-use futures_util::stream::StreamExt;
-use futures::SinkExt;
-use std::future::Future;
-
-use crate::frame::QuasselCodec;
-
-use failure::Error;
-
-use log::{trace, debug, info, error};
-
-use crate::message::*;
-use crate::primitive::*;
-
-extern crate log;
-
-pub struct Client<T, F>
-where
- F: Future,
- T: 'static + AsyncRead + AsyncWrite + Unpin,
-{
- pub stream: Framed<T, QuasselCodec>,
- pub tls: bool,
- pub compression: bool,
- pub state: ClientState,
- pub user: User,
- pub funcs: Funcs<T, F>,
-}
-
-pub struct User {
- pub name: String,
- pub password: String,
-}
-
-pub enum ClientState {
- Handshake,
- Connected,
-}
-
-impl <T: AsyncRead + AsyncWrite + Unpin, F: Future> Client<T, F> {
- pub async fn run(&mut self) {
- use crate::primitive::StringList;
- use crate::message::ClientInit;
- use crate::HandshakeSerialize;
-
- info!(target: "init", "Setting Features");
-
- let mut features = StringList::new();
- features.push("SynchronizedMarkerLine".to_string());
- features.push("Authenticators".to_string());
- features.push("ExtendedFeatures".to_string());
- features.push("BufferActivitySync".to_string());
- let client_init = ClientInit {
- client_version:String::from("Rust 0.0.0"),
- client_date: String::from("1579009211"),
- feature_list: features,
- client_features: 0x00008000,
- };
-
- self.stream.send(client_init.serialize().unwrap()).await.unwrap();
-
- // Start event loop
- while let Some(msg) = self.stream.next().await {
- let msg = msg.unwrap();
- match self.state {
- ClientState::Handshake => handle_login_message(self, &msg).await.unwrap(),
- ClientState::Connected => handle_message(self, &msg).await.unwrap(),
- }
- };
- }
-
- pub async fn connect(
- address: &'static str,
- port: u64,
- compression: bool,
- user: User,
- funcs: Funcs<TcpStream, impl Future>
- ) -> Result<Client<TcpStream, impl Future>, Error> {
- let mut stream = TcpStream::connect(format!("{}:{}", address, port)).await?;
-
- info!(target: "init", "Establishing Connection");
- let connack = init(&mut stream, false, compression).await?;
-
- debug!(target: "init", "{:?}", connack);
-
- let codec = QuasselCodec::builder()
- .compression(compression)
- .new_codec();
-
- let framed_stream = Framed::new(stream, codec);
-
- info!(target: "init", "Established Connection");
-
- return Ok(Client {
- stream: framed_stream,
- tls: false,
- compression,
- state: ClientState::Handshake,
- user,
- funcs,
- });
- }
-
- pub async fn connect_tls(
- address: &'static str,
- port: u64,
- compression: bool,
- user: User,
- funcs: Funcs<TlsStream<TcpStream>, impl Future>
- ) -> Result<Client<TlsStream<TcpStream>, impl Future>, Error> {
- let mut stream: TcpStream = TcpStream::connect(format!("{}:{}", address, port)).await?;
-
- info!(target: "init", "Establishing Connection");
- let connack = init(&mut stream, true, compression).await?;
-
- debug!(target: "init", "{:?}", connack);
-
- let codec = QuasselCodec::builder()
- .compression(compression)
- .new_codec();
-
- let tls_connector = tokio_tls::TlsConnector::from(TlsConnector::builder().build().unwrap());
-
- let tls_stream = tls_connector.connect(address, stream).await?;
-
- let framed_stream = Framed::new(tls_stream, codec);
-
- info!(target: "init", "Established Connection");
-
- return Ok(Client {
- stream: framed_stream,
- tls: true,
- compression,
- state: ClientState::Handshake,
- user,
- funcs,
- });
- }
-
-}
-
-pub async fn handle_login_message<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: &mut Client<T, F>, buf: &[u8]) -> Result<(), Error> {
- use crate::{HandshakeSerialize, HandshakeDeserialize};
- use crate::message::ClientLogin;
- use crate::primitive::{VariantMap, Variant};
-
- trace!(target: "message", "Received bytes: {:x?}", buf);
- let (_, res) = VariantMap::parse(buf)?;
- debug!(target: "init", "Received Messsage: {:#?}", res);
-
- let msgtype = match_variant!(&res["MsgType"], Variant::String);
- match msgtype.as_str() {
- "ClientInitAck" => {
- info!(target: "init", "Initialization successfull");
- info!(target: "login", "Starting Login");
- let login = ClientLogin {user: client.user.name.clone(), password: client.user.password.clone()};
- client.stream.send(login.serialize()?).await?;
- },
- "ClientInitReject" => {
- error!(target: "init", "Initialization failed: {:?}", res);
- },
- "ClientLoginAck" => {
- info!(target: "login", "Login successfull");
- },
- "SessionInit" => {
- info!(target: "login", "Session Initialization finished. Switching to Connected state");
- client.state = ClientState::Connected;
- }
- "ClientLoginReject" => {
- error!(target: "login", "Login failed: {:?}", res);
- },
- _ => {
- error!(target: "client", "Error: WrongMsgType: {:#?}", res);
- }
- }
-
- return Ok(());
-}
-
-pub async fn handle_message<T: AsyncRead + AsyncWrite + Unpin, F: Future>(client: &mut Client<T, F>, buf: &[u8]) -> Result<(), Error> {
- use crate::message::Message;
- use crate::primitive::VariantList;
- use crate::Deserialize;
- use crate::Serialize;
-
- trace!(target: "message", "Received bytes: {:x?}", buf);
- let (_, res) = Message::parse(buf)?;
- debug!(target: "init", "Received Messsage: {:#?}", res);
-
- match res {
- Message::SyncMessage(_) => {}
- Message::RpcCall(_) => {}
- Message::InitRequest(_) => {}
- Message::InitData(_) => {}
- Message::HeartBeat(_) => {}
- Message::HeartBeatReply(_) => {}
- }
-
- return Ok(());
-}
-
-pub struct Funcs<T, F>
-where
- T: 'static + AsyncRead + AsyncWrite + Unpin,
- F: std::future::Future,
-{
- pub init: InitFuncs<T, F>,
- pub message: MessageFuncs<T, F>,
-}
-
-pub struct InitFuncs<T, F>
-where
- T: 'static + AsyncRead + AsyncWrite + Unpin,
- F: std::future::Future,
-{
- pub client_init_ack: fn(&mut Client<T, F>, VariantMap) -> F,
- pub client_init_reject: fn(Client<T, F>, VariantMap) -> F,
- pub client_login_ack: fn(Client<T, F>, VariantMap) -> F,
- pub client_login_reject: fn(Client<T, F>, VariantMap) -> F,
- pub session_init: fn(&mut Client<T, F>, VariantMap) -> F,
-}
-
-pub struct MessageFuncs<T, F>
-where
- T: 'static + AsyncRead + AsyncWrite + Unpin,
- F: std::future::Future,
-{
- pub sync_message: fn(Client<T, F>, SyncMessage) -> F,
- pub rpc_call: fn(Client<T, F>, RpcCall) -> F,
- pub init_request: fn(Client<T, F>, InitRequest) -> F,
- pub init_data: fn(Client<T, F>, InitData) -> F,
- pub heart_beat: fn(Client<T, F>, HeartBeat) -> F,
- pub heart_beat_reply: fn(Client<T, F>, HeartBeatReply) -> F,
-}
-
-// Send the initialization message to the stream
-pub async fn init(stream: &mut TcpStream, tls: bool, compression: bool) -> Result<ConnAck, Error> {
- use crate::Deserialize;
-
- // Buffer for our initialization
- let mut init: Vec<u8> = vec![];
-
- // The handshake message
- let mut handshake: u32 = 0x42b33f00;
-
- // If TLS is enabled set the TLS bit on the handshake
- if tls {
- info!(target: "init", "Enabled TLS");
- handshake |= 0x01;
- }
-
- // If COMPRESSION is enabled set the COMPRESSION bit on the handshake
- if compression {
- info!(target: "init", "Enabled Compression");
- handshake |= 0x02;
- }
-
- // Select Protocol 2: Datastream
- let mut proto: u32 = 0x00000002;
-
- // Flag proto as the last protocol
- let fin: u32 = 0x80000000;
- proto |= fin;
-
- // Add handshake and protocol to our buffer
- init.extend(handshake.to_be_bytes().iter());
- init.extend(proto.to_be_bytes().iter());
-
- // Send Buffer
- stream.write(&init).await?;
-
- // Read Response
- let mut buf = [0; 4];
- stream.read(&mut buf).await?;
-
- let (_, connack) = ConnAck::parse(&buf)?;
- Ok(connack)
-}
diff --git a/src/frame/mod.rs b/src/frame/mod.rs
index 709d3af..cbeddb0 100644
--- a/src/frame/mod.rs
+++ b/src/frame/mod.rs
@@ -195,8 +195,7 @@ impl Decoder for QuasselCodec {
}
}
-impl Encoder for QuasselCodec {
- type Item = Vec<u8>;
+impl Encoder<Vec<u8>> for QuasselCodec {
type Error = io::Error;
fn encode(&mut self, data: Vec<u8>, dst: &mut BytesMut) -> Result<(), io::Error> {
diff --git a/src/lib.rs b/src/lib.rs
index 215dcfc..223a2da 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,23 +1,32 @@
+#![feature(external_doc)]
+#![feature(doc_cfg)]
+#![doc(include = "../README.md")]
+#[cfg_attr(docsrs, feature(doc_cfg))]
#[macro_use]
mod util;
-#[cfg(feature = "client")]
-pub mod client;
-
#[cfg(test)]
pub mod tests;
#[macro_use]
extern crate failure;
+/// Quassel Structures for serialization and deserialization
pub mod message;
+
+/// Quassels QT based primitive types that make up the more complex messages
pub mod primitive;
+pub mod session;
+
#[allow(dead_code)]
+/// Error Types
pub mod error;
#[allow(unused_variables, dead_code)]
#[cfg(feature = "framing")]
+#[cfg_attr(docsrs, doc(cfg(feature = "framing")))]
+/// Framing impl to be used with [`tokio_util::codec::Framed`]
pub mod frame;
use failure::Error;
diff --git a/src/message/handshake/clientinit.rs b/src/message/handshake/clientinit.rs
index 17ec9a1..d21d6aa 100644
--- a/src/message/handshake/clientinit.rs
+++ b/src/message/handshake/clientinit.rs
@@ -1,6 +1,5 @@
-use crate::error::ProtocolError;
use crate::primitive::{StringList, Variant, VariantMap};
-use crate::{HandshakeDeserialize, HandshakeSerialize};
+use crate::HandshakeSerialize;
use failure::Error;
@@ -32,7 +31,7 @@ use failure::Error;
/// | -- | EcdsaCertfpKeys | ECDSA keys for CertFP in identities |
/// | -- | LongMessageId | 64-bit IDs for messages |
/// | -- | SyncedCoreInfo | CoreInfo dynamically updated using signals |
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct ClientInit {
/// Version of the client
pub client_version: String,
@@ -68,24 +67,13 @@ impl HandshakeSerialize for ClientInit {
}
}
-impl HandshakeDeserialize for ClientInit {
- fn parse(b: &[u8]) -> Result<(usize, Self), Error> {
- let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?;
-
- let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8);
-
- if msgtype == "ClientInit" {
- return Ok((
- len,
- Self {
- client_version: match_variant!(values["ClientVersion"], Variant::String),
- client_date: match_variant!(values["ClientDate"], Variant::String),
- feature_list: match_variant!(values["FeatureList"], Variant::StringList),
- client_features: match_variant!(values["Features"], Variant::u32),
- },
- ));
- } else {
- bail!(ProtocolError::WrongMsgType);
+impl From<VariantMap> for ClientInit {
+ fn from(input: VariantMap) -> Self {
+ ClientInit {
+ client_version: match_variant!(input.get("ClientVersion").unwrap(), Variant::String),
+ client_date: match_variant!(input.get("ClientDate").unwrap(), Variant::String),
+ client_features: match_variant!(input.get("Features").unwrap(), Variant::u32),
+ feature_list: match_variant!(input.get("FeatureList").unwrap(), Variant::StringList),
}
}
}
diff --git a/src/message/handshake/clientinitack.rs b/src/message/handshake/clientinitack.rs
index 637b989..f881113 100644
--- a/src/message/handshake/clientinitack.rs
+++ b/src/message/handshake/clientinitack.rs
@@ -1,11 +1,10 @@
-use crate::error::ProtocolError;
-use crate::primitive::{StringList, Variant, VariantList, VariantMap};
-use crate::{HandshakeDeserialize, HandshakeSerialize};
+use crate::primitive::{Variant, VariantList, VariantMap};
+use crate::HandshakeSerialize;
use failure::Error;
/// ClientInitAck is received when the initialization was successfull
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct ClientInitAck {
/// Flags of supported legacy features
pub core_features: u32,
@@ -16,7 +15,7 @@ pub struct ClientInitAck {
/// List of VariantMaps of info on available authenticators
pub authenticators: VariantList,
/// List of supported extended features
- pub feature_list: StringList,
+ pub feature_list: Vec<String>,
}
impl HandshakeSerialize for ClientInitAck {
@@ -47,28 +46,21 @@ impl HandshakeSerialize for ClientInitAck {
}
}
-impl HandshakeDeserialize for ClientInitAck {
- fn parse(b: &[u8]) -> Result<(usize, Self), Error> {
- let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?;
-
- let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8);
-
- if msgtype == "ClientInitAck" {
- return Ok((
- len,
- Self {
- core_features: 0x00008000,
- core_configured: match_variant!(values["Configured"], Variant::bool),
- storage_backends: match_variant!(
- values["StorageBackends"],
- Variant::VariantList
- ),
- authenticators: match_variant!(values["Authenticators"], Variant::VariantList),
- feature_list: match_variant!(values["FeatureList"], Variant::StringList),
- },
- ));
- } else {
- bail!(ProtocolError::WrongMsgType);
+impl From<VariantMap> for ClientInitAck {
+ fn from(input: VariantMap) -> Self {
+ ClientInitAck {
+ // TODO make this compatible with older clients
+ core_features: 0,
+ core_configured: match_variant!(input.get("Configured").unwrap(), Variant::bool),
+ storage_backends: match_variant!(
+ input.get("StorageBackends").unwrap(),
+ Variant::VariantList
+ ),
+ authenticators: match_variant!(
+ input.get("Authenticators").unwrap(),
+ Variant::VariantList
+ ),
+ feature_list: match_variant!(input.get("FeatureList").unwrap(), Variant::StringList),
}
}
}
diff --git a/src/message/handshake/clientinitreject.rs b/src/message/handshake/clientinitreject.rs
index 06960b7..d93413d 100644
--- a/src/message/handshake/clientinitreject.rs
+++ b/src/message/handshake/clientinitreject.rs
@@ -1,14 +1,13 @@
-use crate::error::ProtocolError;
use crate::primitive::{Variant, VariantMap};
-use crate::{HandshakeDeserialize, HandshakeSerialize};
+use crate::HandshakeSerialize;
use failure::Error;
/// ClientInitReject is received when the initialization fails
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct ClientInitReject {
/// String with an error message of what went wrong
- pub error_string: String,
+ pub error: String,
}
impl HandshakeSerialize for ClientInitReject {
@@ -20,27 +19,16 @@ impl HandshakeSerialize for ClientInitReject {
);
values.insert(
"ErrorString".to_string(),
- Variant::String(self.error_string.clone()),
+ Variant::String(self.error.clone()),
);
return HandshakeSerialize::serialize(&values);
}
}
-impl HandshakeDeserialize for ClientInitReject {
- fn parse(b: &[u8]) -> Result<(usize, Self), Error> {
- let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?;
-
- let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8);
-
- if msgtype == "ClientInitReject" {
- return Ok((
- len,
- Self {
- error_string: match_variant!(values["ErrorString"], Variant::String),
- },
- ));
- } else {
- bail!(ProtocolError::WrongMsgType);
+impl From<VariantMap> for ClientInitReject {
+ fn from(input: VariantMap) -> Self {
+ ClientInitReject {
+ error: match_variant!(input.get("ErrorString").unwrap(), Variant::String),
}
}
}
diff --git a/src/message/handshake/clientlogin.rs b/src/message/handshake/clientlogin.rs
index 769245b..dffd996 100644
--- a/src/message/handshake/clientlogin.rs
+++ b/src/message/handshake/clientlogin.rs
@@ -1,12 +1,11 @@
-use crate::error::ProtocolError;
use crate::primitive::{Variant, VariantMap};
-use crate::{HandshakeDeserialize, HandshakeSerialize};
+use crate::HandshakeSerialize;
use failure::Error;
/// Login to the core with user data
/// username and password are transmitted in plain text
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct ClientLogin {
pub user: String,
pub password: String,
@@ -28,22 +27,11 @@ impl HandshakeSerialize for ClientLogin {
}
}
-impl HandshakeDeserialize for ClientLogin {
- fn parse(b: &[u8]) -> Result<(usize, Self), Error> {
- let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?;
-
- let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8);
-
- if msgtype == "ClientLogin" {
- return Ok((
- len,
- Self {
- user: match_variant!(values["User"], Variant::String),
- password: match_variant!(values["Password"], Variant::String),
- },
- ));
- } else {
- bail!(ProtocolError::WrongMsgType);
+impl From<VariantMap> for ClientLogin {
+ fn from(input: VariantMap) -> Self {
+ ClientLogin {
+ user: match_variant!(input.get("User").unwrap(), Variant::String),
+ password: match_variant!(input.get("Password").unwrap(), Variant::String),
}
}
}
diff --git a/src/message/handshake/clientloginack.rs b/src/message/handshake/clientloginack.rs
index 674d307..e385a81 100644
--- a/src/message/handshake/clientloginack.rs
+++ b/src/message/handshake/clientloginack.rs
@@ -6,7 +6,7 @@ use failure::Error;
/// ClientLoginAck is received after the client has successfully logged in
/// it has no fields
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct ClientLoginAck;
impl HandshakeSerialize for ClientLoginAck {
diff --git a/src/message/handshake/clientloginreject.rs b/src/message/handshake/clientloginreject.rs
index e8380d6..0c0fc85 100644
--- a/src/message/handshake/clientloginreject.rs
+++ b/src/message/handshake/clientloginreject.rs
@@ -1,14 +1,13 @@
-use crate::error::ProtocolError;
use crate::primitive::{Variant, VariantMap};
-use crate::{HandshakeDeserialize, HandshakeSerialize};
+use crate::HandshakeSerialize;
use failure::Error;
/// ClientLoginReject is received after the client failed to login
/// It contains an error message as String
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct ClientLoginReject {
- error: String,
+ pub error: String,
}
impl HandshakeSerialize for ClientLoginReject {
@@ -26,21 +25,10 @@ impl HandshakeSerialize for ClientLoginReject {
}
}
-impl HandshakeDeserialize for ClientLoginReject {
- fn parse(b: &[u8]) -> Result<(usize, Self), Error> {
- let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?;
-
- let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8);
-
- if msgtype == "ClientLogin" {
- return Ok((
- len,
- Self {
- error: match_variant!(values["ErrorString"], Variant::String),
- },
- ));
- } else {
- bail!(ProtocolError::WrongMsgType);
+impl From<VariantMap> for ClientLoginReject {
+ fn from(input: VariantMap) -> Self {
+ ClientLoginReject {
+ error: match_variant!(input.get("ErrorString").unwrap(), Variant::String),
}
}
}
diff --git a/src/message/handshake/init.rs b/src/message/handshake/init.rs
index e4c0fa9..b4604b6 100644
--- a/src/message/handshake/init.rs
+++ b/src/message/handshake/init.rs
@@ -2,6 +2,7 @@ use crate::Deserialize;
use crate::Serialize;
/// The first few bytes sent to the core to initialize the connection and setup if we want to use tls and compression
+#[derive(Clone, Debug)]
pub struct Init {
pub tls: bool,
pub compression: bool,
@@ -37,7 +38,15 @@ impl Init {
handshake |= 0x02;
}
- return handshake.serialize().unwrap();
+ // Select Protocol 2: Datastream
+
+ let mut init: Vec<u8> = vec![];
+
+ // Add handshake and protocol to our buffer
+ init.extend(handshake.serialize().unwrap());
+ init.extend(crate::message::Protocol::Datastream.serialize());
+
+ return init;
}
pub fn parse(buf: &[u8]) -> Self {
diff --git a/src/message/handshake/mod.rs b/src/message/handshake/mod.rs
index 9b3bcee..9885012 100644
--- a/src/message/handshake/mod.rs
+++ b/src/message/handshake/mod.rs
@@ -21,3 +21,49 @@ pub use init::*;
pub use protocol::*;
pub use sessioninit::*;
pub use types::*;
+
+use crate::primitive::{Variant, VariantMap};
+use crate::{HandshakeDeserialize, HandshakeSerialize};
+
+#[derive(Debug, Clone)]
+pub enum HandshakeMessage {
+ ClientInit(ClientInit),
+ ClientInitAck(ClientInitAck),
+ ClientInitReject(ClientInitReject),
+ ClientLogin(ClientLogin),
+ ClientLoginAck,
+ ClientLoginReject(ClientLoginReject),
+ SessionInit(SessionInit),
+}
+
+impl HandshakeSerialize for HandshakeMessage {
+ fn serialize(&self) -> Result<Vec<u8>, failure::Error> {
+ match self {
+ HandshakeMessage::ClientInit(inner) => inner.serialize(),
+ HandshakeMessage::ClientInitAck(inner) => inner.serialize(),
+ HandshakeMessage::ClientInitReject(inner) => inner.serialize(),
+ HandshakeMessage::ClientLogin(inner) => inner.serialize(),
+ HandshakeMessage::ClientLoginAck => ClientLoginAck.serialize(),
+ HandshakeMessage::ClientLoginReject(inner) => inner.serialize(),
+ HandshakeMessage::SessionInit(inner) => inner.serialize(),
+ }
+ }
+}
+
+impl HandshakeDeserialize for HandshakeMessage {
+ fn parse(b: &[u8]) -> Result<(usize, Self), failure::Error> {
+ let (size, res) = VariantMap::parse(b)?;
+
+ let msgtype = match_variant!(&res["MsgType"], Variant::String);
+ match msgtype.as_str() {
+ "ClientInit" => Ok((size, HandshakeMessage::ClientInit(res.into()))),
+ "ClientInitAck" => Ok((size, HandshakeMessage::ClientInitAck(res.into()))),
+ "ClientInitReject" => Ok((size, HandshakeMessage::ClientInitReject(res.into()))),
+ "ClientLogin" => Ok((size, HandshakeMessage::ClientLogin(res.into()))),
+ "ClientLoginAck" => Ok((size, HandshakeMessage::ClientLoginAck)),
+ "ClientLoginReject" => Ok((size, HandshakeMessage::ClientLoginReject(res.into()))),
+ "SessionInit" => Ok((size, HandshakeMessage::SessionInit(res.into()))),
+ _ => unimplemented!(),
+ }
+ }
+}
diff --git a/src/message/handshake/sessioninit.rs b/src/message/handshake/sessioninit.rs
index eca4c10..d1b4b90 100644
--- a/src/message/handshake/sessioninit.rs
+++ b/src/message/handshake/sessioninit.rs
@@ -1,61 +1,81 @@
-use crate::error::ProtocolError;
-use crate::primitive::{Variant, VariantList, VariantMap};
-use crate::{HandshakeDeserialize, HandshakeSerialize};
+use crate::message::objects::Identity;
+use crate::primitive::{BufferInfo, Variant, VariantMap};
+use crate::HandshakeSerialize;
use failure::Error;
/// SessionInit is received along with ClientLoginAck to initialize that user Session
// TODO Replace with proper types
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct SessionInit {
/// List of all configured identities
- identities: VariantList,
+ pub identities: Vec<Identity>,
/// List of all existing buffers
- buffers: VariantList,
+ pub buffers: Vec<BufferInfo>,
/// Ids of all networks
- network_ids: VariantList,
+ pub network_ids: Vec<i32>,
+}
+
+impl From<VariantMap> for SessionInit {
+ fn from(input: VariantMap) -> Self {
+ let state = match_variant!(input.get("SessionState").unwrap(), Variant::VariantMap);
+ SessionInit {
+ identities: match_variant!(state.get("Identities").unwrap(), Variant::VariantList)
+ .iter()
+ .map(|ident| Identity::from(match_variant!(ident, Variant::VariantMap)))
+ .collect(),
+ buffers: match_variant!(state.get("BufferInfos").unwrap(), Variant::VariantList)
+ .iter()
+ .map(|buffer| match buffer {
+ Variant::BufferInfo(buffer) => buffer.clone(),
+ _ => unimplemented!(),
+ })
+ .collect(),
+ network_ids: match_variant!(state.get("NetworkIds").unwrap(), Variant::VariantList)
+ .iter()
+ .map(|network| match network {
+ Variant::i32(network) => network.clone(),
+ _ => unimplemented!(),
+ })
+ .collect(),
+ }
+ }
}
impl HandshakeSerialize for SessionInit {
fn serialize(&self) -> Result<Vec<u8>, Error> {
- let mut values: VariantMap = VariantMap::with_capacity(1);
+ let mut values: VariantMap = VariantMap::with_capacity(4);
values.insert(
"MsgType".to_string(),
Variant::String("SessionInit".to_string()),
);
values.insert(
"Identities".to_string(),
- Variant::VariantList(self.identities.clone()),
+ Variant::VariantList(
+ self.identities
+ .iter()
+ .map(|ident| Variant::VariantMap(ident.clone().into()))
+ .collect(),
+ ),
);
values.insert(
"BufferInfos".to_string(),
- Variant::VariantList(self.buffers.clone()),
+ Variant::VariantList(
+ self.buffers
+ .iter()
+ .map(|buffer| Variant::BufferInfo(buffer.clone()))
+ .collect(),
+ ),
);
values.insert(
"NetworkIds".to_string(),
- Variant::VariantList(self.network_ids.clone()),
+ Variant::VariantList(
+ self.network_ids
+ .iter()
+ .map(|id| Variant::i32(id.clone()))
+ .collect(),
+ ),
);
return HandshakeSerialize::serialize(&values);
}
}
-
-impl HandshakeDeserialize for SessionInit {
- fn parse(b: &[u8]) -> Result<(usize, Self), Error> {
- let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?;
-
- let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8);
-
- if msgtype == "ClientLogin" {
- return Ok((
- len,
- Self {
- identities: match_variant!(values["Identities"], Variant::VariantList),
- buffers: match_variant!(values["BufferInfos"], Variant::VariantList),
- network_ids: match_variant!(values["NetworkIds"], Variant::VariantList),
- },
- ));
- } else {
- bail!(ProtocolError::WrongMsgType);
- }
- }
-}
diff --git a/src/message/signalproxy.rs b/src/message/signalproxy.rs
deleted file mode 100644
index 22a498a..0000000
--- a/src/message/signalproxy.rs
+++ /dev/null
@@ -1,309 +0,0 @@
-use crate::primitive::{DateTime, Variant, VariantList};
-use crate::{Deserialize, Serialize};
-
-#[derive(Clone, Debug, std::cmp::PartialEq)]
-pub enum Message {
- /// Bidirectional
- SyncMessage(SyncMessage),
- /// Bidirectional
- RpcCall(RpcCall),
- InitRequest(InitRequest),
- InitData(InitData),
- /// Bidirectional
- HeartBeat(HeartBeat),
- /// Bidirectional
- HeartBeatReply(HeartBeatReply),
-}
-
-impl Serialize for Message {
- fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
- match &self {
- Message::SyncMessage(value) => value.serialize(),
- Message::RpcCall(value) => value.serialize(),
- Message::InitRequest(value) => value.serialize(),
- Message::InitData(value) => value.serialize(),
- Message::HeartBeat(value) => value.serialize(),
- Message::HeartBeatReply(value) => value.serialize(),
- }
- }
-}
-
-impl Deserialize for Message {
- fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
- let (_, message_type) = i32::parse(&b[9..13])?;
-
- match MessageType::from(message_type) {
- MessageType::SyncMessage => {
- let (size, res) = SyncMessage::parse(&b)?;
-
- Ok((size, Message::SyncMessage(res)))
- }
- MessageType::RpcCall => {
- let (size, res) = RpcCall::parse(&b)?;
-
- Ok((size, Message::RpcCall(res)))
- }
- MessageType::InitRequest => {
- let (size, res) = InitRequest::parse(&b)?;
-
- Ok((size, Message::InitRequest(res)))
- }
- MessageType::InitData => {
- let (size, res) = InitData::parse(&b)?;
-
- Ok((size, Message::InitData(res)))
- }
- MessageType::HeartBeat => {
- let (size, res) = HeartBeat::parse(&b)?;
-
- Ok((size, Message::HeartBeat(res)))
- }
- MessageType::HeartBeatReply => {
- let (size, res) = HeartBeatReply::parse(&b)?;
-
- Ok((size, Message::HeartBeatReply(res)))
- }
- }
- }
-}
-
-/// Type of an SignalProxy Message
-/// The first element in the VariantList that is received
-#[repr(i32)]
-#[derive(Copy, Clone, Debug, std::cmp::PartialEq)]
-pub enum MessageType {
- /// Bidirectional
- SyncMessage = 0x00000001,
- /// Bidirectional
- RpcCall = 0x00000002,
- InitRequest = 0x00000003,
- InitData = 0x00000004,
- /// Bidirectional
- HeartBeat = 0x00000005,
- /// Bidirectional
- HeartBeatReply = 0x00000006,
-}
-
-impl From<i32> for MessageType {
- fn from(val: i32) -> Self {
- match val {
- 0x00000001 => MessageType::SyncMessage,
- 0x00000002 => MessageType::RpcCall,
- 0x00000003 => MessageType::InitRequest,
- 0x00000004 => MessageType::InitData,
- 0x00000005 => MessageType::HeartBeat,
- 0x00000006 => MessageType::HeartBeatReply,
- _ => unimplemented!(),
- }
- }
-}
-
-#[derive(Clone, Debug, std::cmp::PartialEq)]
-pub struct SyncMessage {
- class_name: String,
- object_name: String,
- slot_name: String,
- params: VariantList,
-}
-
-impl Serialize for SyncMessage {
- fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
- let mut res = VariantList::new();
-
- res.push(Variant::i32(MessageType::SyncMessage as i32));
- res.push(Variant::StringUTF8(self.class_name.clone()));
- res.push(Variant::StringUTF8(self.object_name.clone()));
- res.push(Variant::StringUTF8(self.slot_name.clone()));
-
- res.append(&mut self.params.clone());
-
- res.serialize()
- }
-}
-
-impl Deserialize for SyncMessage {
- fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
- let (size, mut res) = VariantList::parse(&b)?;
-
- res.remove(0);
-
- Ok((
- size,
- Self {
- class_name: match_variant!(res.remove(0), Variant::StringUTF8),
- object_name: match_variant!(res.remove(0), Variant::StringUTF8),
- slot_name: match_variant!(res.remove(0), Variant::StringUTF8),
- params: res,
- },
- ))
- }
-}
-
-#[derive(Clone, Debug, std::cmp::PartialEq)]
-pub struct RpcCall {
- slot_name: String,
- params: VariantList,
-}
-
-impl Serialize for RpcCall {
- fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
- let mut res = VariantList::new();
-
- res.push(Variant::i32(MessageType::RpcCall as i32));
- res.push(Variant::StringUTF8(self.slot_name.clone()));
-
- res.append(&mut self.params.clone());
-
- res.serialize()
- }
-}
-
-impl Deserialize for RpcCall {
- fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
- let (size, mut res) = VariantList::parse(&b)?;
-
- res.remove(0);
-
- Ok((
- size,
- Self {
- slot_name: match_variant!(res.remove(0), Variant::StringUTF8),
- params: res,
- },
- ))
- }
-}
-
-#[derive(Clone, Debug, std::cmp::PartialEq)]
-pub struct InitRequest {
- class_name: String,
- object_name: String,
-}
-
-impl Serialize for InitRequest {
- fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
- let mut res = VariantList::new();
-
- res.push(Variant::i32(MessageType::InitRequest as i32));
- res.push(Variant::StringUTF8(self.class_name.clone()));
- res.push(Variant::StringUTF8(self.object_name.clone()));
-
- res.serialize()
- }
-}
-
-impl Deserialize for InitRequest {
- fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
- let (size, mut res) = VariantList::parse(&b)?;
-
- res.remove(0);
-
- Ok((
- size,
- Self {
- class_name: match_variant!(res.remove(0), Variant::StringUTF8),
- object_name: match_variant!(res.remove(0), Variant::StringUTF8),
- },
- ))
- }
-}
-
-#[derive(Clone, Debug, std::cmp::PartialEq)]
-pub struct InitData {
- class_name: String,
- object_name: String,
- init_data: VariantList,
-}
-
-impl Serialize for InitData {
- fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
- let mut res = VariantList::new();
-
- res.push(Variant::i32(MessageType::InitData as i32));
- res.push(Variant::StringUTF8(self.class_name.clone()));
- res.push(Variant::StringUTF8(self.object_name.clone()));
-
- res.append(&mut self.init_data.clone());
-
- res.serialize()
- }
-}
-
-impl Deserialize for InitData {
- fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
- let (size, mut res) = VariantList::parse(&b)?;
-
- res.remove(0);
-
- Ok((
- size,
- Self {
- class_name: match_variant!(res.remove(0), Variant::StringUTF8),
- object_name: match_variant!(res.remove(0), Variant::StringUTF8),
- init_data: res,
- },
- ))
- }
-}
-
-#[derive(Clone, Debug, std::cmp::PartialEq)]
-pub struct HeartBeat {
- timestamp: DateTime,
-}
-
-impl Serialize for HeartBeat {
- fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
- let mut res = VariantList::new();
-
- res.push(Variant::i32(MessageType::HeartBeat as i32));
- res.push(Variant::DateTime(self.timestamp.clone()));
-
- res.serialize()
- }
-}
-
-impl Deserialize for HeartBeat {
- fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
- let (size, mut res) = VariantList::parse(&b)?;
-
- res.remove(0);
-
- Ok((
- size,
- Self {
- timestamp: match_variant!(res.remove(0), Variant::DateTime),
- },
- ))
- }
-}
-
-#[derive(Clone, Debug, std::cmp::PartialEq)]
-pub struct HeartBeatReply {
- timestamp: DateTime,
-}
-
-impl Serialize for HeartBeatReply {
- fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
- let mut res = VariantList::new();
-
- res.push(Variant::i32(MessageType::HeartBeatReply as i32));
- res.push(Variant::DateTime(self.timestamp.clone()));
-
- res.serialize()
- }
-}
-
-impl Deserialize for HeartBeatReply {
- fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
- let (size, mut res) = VariantList::parse(&b)?;
-
- res.remove(0);
-
- Ok((
- size,
- Self {
- timestamp: match_variant!(res.remove(0), Variant::DateTime),
- },
- ))
- }
-}
diff --git a/src/message/signalproxy/heartbeat.rs b/src/message/signalproxy/heartbeat.rs
new file mode 100644
index 0000000..46bfd51
--- /dev/null
+++ b/src/message/signalproxy/heartbeat.rs
@@ -0,0 +1,65 @@
+use crate::message::MessageType;
+use crate::primitive::{DateTime, Variant, VariantList};
+use crate::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct HeartBeat {
+ timestamp: DateTime,
+}
+
+impl Serialize for HeartBeat {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::HeartBeat as i32));
+ res.push(Variant::DateTime(self.timestamp.clone()));
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for HeartBeat {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ timestamp: match_variant!(res.remove(0), Variant::DateTime),
+ },
+ ))
+ }
+}
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct HeartBeatReply {
+ timestamp: DateTime,
+}
+
+impl Serialize for HeartBeatReply {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::HeartBeatReply as i32));
+ res.push(Variant::DateTime(self.timestamp.clone()));
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for HeartBeatReply {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ timestamp: match_variant!(res.remove(0), Variant::DateTime),
+ },
+ ))
+ }
+}
diff --git a/src/message/signalproxy/initdata.rs b/src/message/signalproxy/initdata.rs
new file mode 100644
index 0000000..e4fb077
--- /dev/null
+++ b/src/message/signalproxy/initdata.rs
@@ -0,0 +1,41 @@
+use crate::message::MessageType;
+use crate::primitive::{Variant, VariantList};
+use crate::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct InitData {
+ class_name: String,
+ object_name: String,
+ init_data: VariantList,
+}
+
+impl Serialize for InitData {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::InitData as i32));
+ res.push(Variant::StringUTF8(self.class_name.clone()));
+ res.push(Variant::StringUTF8(self.object_name.clone()));
+
+ res.append(&mut self.init_data.clone());
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for InitData {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ class_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ object_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ init_data: res,
+ },
+ ))
+ }
+}
diff --git a/src/message/signalproxy/initrequest.rs b/src/message/signalproxy/initrequest.rs
new file mode 100644
index 0000000..59eee2d
--- /dev/null
+++ b/src/message/signalproxy/initrequest.rs
@@ -0,0 +1,37 @@
+use crate::message::MessageType;
+use crate::primitive::{Variant, VariantList};
+use crate::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct InitRequest {
+ pub class_name: String,
+ pub object_name: String,
+}
+
+impl Serialize for InitRequest {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::InitRequest as i32));
+ res.push(Variant::ByteArray(self.class_name.clone()));
+ res.push(Variant::ByteArray(self.object_name.clone()));
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for InitRequest {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ class_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ object_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ },
+ ))
+ }
+}
diff --git a/src/message/signalproxy/mod.rs b/src/message/signalproxy/mod.rs
new file mode 100644
index 0000000..4e99fb0
--- /dev/null
+++ b/src/message/signalproxy/mod.rs
@@ -0,0 +1,124 @@
+use crate::{Deserialize, Serialize};
+
+mod heartbeat;
+mod initdata;
+mod initrequest;
+pub mod objects;
+mod rpccall;
+mod syncmessage;
+
+pub use heartbeat::*;
+pub use initdata::*;
+pub use initrequest::*;
+pub use rpccall::*;
+pub use syncmessage::*;
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub enum Message {
+ /// Bidirectional
+ SyncMessage(SyncMessage),
+ /// Bidirectional
+ RpcCall(RpcCall),
+ InitRequest(InitRequest),
+ InitData(InitData),
+ /// Bidirectional
+ HeartBeat(HeartBeat),
+ /// Bidirectional
+ HeartBeatReply(HeartBeatReply),
+}
+
+// impl Message {
+// fn act(&self) {
+// match &self {
+// Message::SyncMessage(value) => value.serialize(),
+// Message::RpcCall(value) => value.serialize(),
+// Message::InitRequest(value) => value.serialize(),
+// Message::InitData(value) => value.serialize(),
+// Message::HeartBeat(value) => value.serialize(),
+// Message::HeartBeatReply(value) => value.serialize(),
+// }
+// }
+// }
+
+impl Serialize for Message {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ match &self {
+ Message::SyncMessage(value) => value.serialize(),
+ Message::RpcCall(value) => value.serialize(),
+ Message::InitRequest(value) => value.serialize(),
+ Message::InitData(value) => value.serialize(),
+ Message::HeartBeat(value) => value.serialize(),
+ Message::HeartBeatReply(value) => value.serialize(),
+ }
+ }
+}
+
+impl Deserialize for Message {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (_, message_type) = i32::parse(&b[9..13])?;
+
+ match MessageType::from(message_type) {
+ MessageType::SyncMessage => {
+ let (size, res) = SyncMessage::parse(&b)?;
+
+ Ok((size, Message::SyncMessage(res)))
+ }
+ MessageType::RpcCall => {
+ let (size, res) = RpcCall::parse(&b)?;
+
+ Ok((size, Message::RpcCall(res)))
+ }
+ MessageType::InitRequest => {
+ let (size, res) = InitRequest::parse(&b)?;
+
+ Ok((size, Message::InitRequest(res)))
+ }
+ MessageType::InitData => {
+ let (size, res) = InitData::parse(&b)?;
+
+ Ok((size, Message::InitData(res)))
+ }
+ MessageType::HeartBeat => {
+ let (size, res) = HeartBeat::parse(&b)?;
+
+ Ok((size, Message::HeartBeat(res)))
+ }
+ MessageType::HeartBeatReply => {
+ let (size, res) = HeartBeatReply::parse(&b)?;
+
+ Ok((size, Message::HeartBeatReply(res)))
+ }
+ }
+ }
+}
+
+/// Type of an SignalProxy Message
+/// The first element in the VariantList that is received
+#[repr(i32)]
+#[derive(Copy, Clone, Debug, std::cmp::PartialEq)]
+pub enum MessageType {
+ /// Bidirectional
+ SyncMessage = 0x00000001,
+ /// Bidirectional
+ RpcCall = 0x00000002,
+ InitRequest = 0x00000003,
+ InitData = 0x00000004,
+ /// Bidirectional
+ HeartBeat = 0x00000005,
+ /// Bidirectional
+ HeartBeatReply = 0x00000006,
+}
+
+impl From<i32> for MessageType {
+ fn from(val: i32) -> Self {
+ match val {
+ 0x00000001 => MessageType::SyncMessage,
+ 0x00000002 => MessageType::RpcCall,
+ 0x00000003 => MessageType::InitRequest,
+ 0x00000004 => MessageType::InitData,
+ 0x00000005 => MessageType::HeartBeat,
+ 0x00000006 => MessageType::HeartBeatReply,
+ _ => unimplemented!(),
+ }
+ }
+}
diff --git a/src/message/signalproxy/objects/aliasmanager.rs b/src/message/signalproxy/objects/aliasmanager.rs
new file mode 100644
index 0000000..828caaa
--- /dev/null
+++ b/src/message/signalproxy/objects/aliasmanager.rs
@@ -0,0 +1,46 @@
+use crate::primitive::{Variant, VariantMap};
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct AliasManager {
+ pub aliases: Vec<Alias>,
+}
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct Alias {
+ name: String,
+ expansion: String,
+}
+
+impl AliasManager {
+ /// Client to Server
+ ///
+ /// Replaces all properties of the object with the content of the
+ /// "properties" parameter. This parameter is in network representation.
+ ///
+ fn request_update(self: &mut Self, properties: VariantMap) {
+ self.update(properties);
+ }
+
+ /// Server to Client
+ fn add_alias(self: &mut Self, name: String, expansion: String) {
+ self.aliases.push(Alias { name, expansion });
+ }
+
+ /// Server to Client
+ ///
+ /// Replaces all properties of the object with the content of the
+ /// "properties" parameter. This parameter is in network representation.
+ ///
+ fn update(self: &mut Self, properties: VariantMap) {
+ let mut alias: Vec<Alias> = Vec::new();
+
+ // for (i, name) in match_variant!(properties[&"Aliases".to_string()], Variant::String) {
+ // alias.push(Alias {
+ // name,
+ // expansion: match_variant!(properties["Aliases"], Variant::String)["expansions"][i],
+ // })
+ // }
+
+ self.aliases = alias
+ }
+}
diff --git a/src/message/signalproxy/objects/backlogmanager.rs b/src/message/signalproxy/objects/backlogmanager.rs
new file mode 100644
index 0000000..86a7f61
--- /dev/null
+++ b/src/message/signalproxy/objects/backlogmanager.rs
@@ -0,0 +1,161 @@
+// interface BacklogManager {
+//
+//
+// // C->S calls
+//
+// /**
+// * Loads backlog for `bufferId`, starting at message `first`, up to `last`
+// * (plus `additional` more messages after `last`) but at most `limit`
+// * messages total.
+// */
+// requestBacklog(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int, additional: Int)
+// /**
+// * Same as `requestBacklog`, but only messages of a certain message `type`
+// * with certain `flags` set.
+// */
+// requestBacklogFiltered(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int, additional: Int, type: Int, flags: Int)
+// /**
+// * Same as `requestBacklog`, but applied to all buffers.
+// */
+// requestBacklogAll(first: MsgId, last: MsgId, limit: Int, additional: Int)
+// /**
+// * Same as `requestBacklogFiltered`, but applied to all buffers.
+// */
+// requestBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int, type: Int, flags: Int)
+//
+//
+// // S->C calls
+//
+// /**
+// * The response to `requestBacklog`, with the messages encoded as QVariants
+// * in the `messages` parameter.
+// */
+// receiveBacklog(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int, additional: Int, messages: QVariantList)
+// /**
+// * The response to `requestBacklogFiltered`, with the messages encoded as
+// * QVariants in the `messages` parameter.
+// */
+// receiveBacklogFiltered(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int, additional: Int, type: Int, flags: Int, messages: QVariantList)
+// /**
+// * The response to `requestBacklogAll`, with the messages encoded as
+// * QVariants in the `messages` parameter.
+// */
+// receiveBacklogAll(first: MsgId, last: MsgId, limit: Int, additional: Int, messages: QVariantList)
+// /**
+// * The response to `requestBacklogAllFiltered`, with the messages encoded as
+// * QVariants in the `messages` parameter.
+// */
+// receiveBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int, type: Int, flags: Int, messages: QVariantList)
+// }
+
+use crate::primitive::VariantList;
+
+/// Receive and Request Backlog
+/// All "request" functions are Client to Server and all "receive" functions are Server to Client
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct BacklogManager {}
+
+impl BacklogManager {
+ /// Loads backlog for `bufferId`, starting at message `first`, up to `last`
+ /// (plus `additional` more messages after `last`) but at most `limit`
+ /// messages total.
+ fn requestBacklog(
+ self: Self,
+ buffer_id: u32,
+ first: u32,
+ last: u32,
+ limit: u32,
+ additional: u32,
+ ) {
+ unimplemented!()
+ }
+
+ /// Same as `requestBacklog`, but only messages of a certain message `type`
+ /// with certain `flags` set.
+ fn requestBacklogFiltered(
+ self: Self,
+ buffer_id: u32,
+ first: u32,
+ last: u32,
+ limit: u32,
+ additional: u32,
+ msgtype: u32,
+ flags: u32,
+ ) {
+ unimplemented!()
+ }
+
+ /// Same as `requestBacklog`, but applied to all buffers.
+ fn requestBacklogAll(self: Self, first: u32, last: u32, limit: u32, additional: u32) {
+ unimplemented!()
+ }
+
+ /// Same as `requestBacklogFiltered`, but applied to all buffers.
+ fn requestBacklogAllFiltered(
+ self: Self,
+ first: u32,
+ last: u32,
+ limit: u32,
+ additional: u32,
+ msgtype: u32,
+ flags: u32,
+ ) {
+ unimplemented!()
+ }
+
+ /// The response to `requestBacklog`, with the messages encoded as Variants
+ /// in the `messages` parameter.
+ fn receiveBacklog(
+ self: Self,
+ buffer_id: u32,
+ first: u32,
+ last: u32,
+ limit: u32,
+ additional: u32,
+ messages: VariantList,
+ ) {
+ unimplemented!()
+ }
+
+ /// The response to `requestBacklogFiltered`, with the messages encoded as
+ /// Variants in the `messages` parameter.
+ fn receiveBacklogFiltered(
+ self: Self,
+ buffer_id: u32,
+ first: u32,
+ last: u32,
+ limit: u32,
+ additional: u32,
+ msgtype: u32,
+ flags: u32,
+ messages: VariantList,
+ ) {
+ unimplemented!()
+ }
+
+ /// Same as `receiveBacklog`, but applied to all buffers.
+ fn receiveBacklogAll(
+ self: Self,
+ first: u32,
+ last: u32,
+ limit: u32,
+ additional: u32,
+ messages: VariantList,
+ ) {
+ unimplemented!()
+ }
+
+ /// Same as `receiveBacklogFiltered`, but applied to all buffers.
+ fn receiveBacklogAllFiltered(
+ self: Self,
+ first: u32,
+ last: u32,
+ limit: u32,
+ additional: u32,
+ msgtype: u32,
+ flags: u32,
+ messages: VariantList,
+ ) {
+ unimplemented!()
+ }
+}
diff --git a/src/message/signalproxy/objects/buffersyncer.rs b/src/message/signalproxy/objects/buffersyncer.rs
new file mode 100644
index 0000000..7ee4ca5
--- /dev/null
+++ b/src/message/signalproxy/objects/buffersyncer.rs
@@ -0,0 +1,74 @@
+use crate::message::signalproxy::MessageType;
+use std::collections::HashMap;
+
+// use default_macro::default;
+// #[default(crate::message::signalproxy::objects::BufferSyncerClient)]
+pub struct BufferSyncer {
+ pub activities: HashMap<u32, MessageType>,
+ pub highlight_counts: HashMap<u32, u32>,
+ pub last_seen_msg: HashMap<u32, u32>,
+ pub marker_line: HashMap<u32, u32>,
+}
+
+pub trait BufferSyncerServer {
+ fn activities(self: &Self) -> &HashMap<u32, MessageType>;
+ fn activities_mut(self: &mut Self) -> &mut HashMap<u32, MessageType>;
+
+ fn highlight_counts(self: &Self) -> &HashMap<u32, u32>;
+ fn highlight_counts_mut(self: &mut Self) -> &mut HashMap<u32, u32>;
+
+ fn last_seen_msg(self: &Self) -> &HashMap<u32, u32>;
+ fn last_seen_msg_mut(self: &mut Self) -> &mut HashMap<u32, u32>;
+
+ fn marker_line(self: &Self) -> &HashMap<u32, u32>;
+ fn marker_line_mut(self: &mut Self) -> &mut HashMap<u32, u32>;
+
+ fn request_mark_buffer_as_read(buffer: u32);
+ fn request_merge_buffers_permanently(buffer1: u32, buffer2: u32);
+ fn request_purge_buffer_ids();
+ fn request_remove_buffer(buffer: u32);
+ fn request_rename_buffer(buffer: u32, new_name: String);
+
+ fn request_set_last_seen_msg(self: &mut Self, buffer: u32, msgid: u32) {
+ self.last_seen_msg_mut().insert(buffer, msgid);
+ }
+
+ fn request_set_marker_line(self: &mut Self, buffer: u32, msgid: u32) {
+ self.marker_line_mut().insert(buffer, msgid);
+ }
+}
+
+pub trait BufferSyncerClient {
+ fn activities(self: &Self) -> &HashMap<u32, MessageType>;
+ fn activities_mut(self: &mut Self) -> &mut HashMap<u32, MessageType>;
+
+ fn highlight_counts(self: &Self) -> &HashMap<u32, u32>;
+ fn highlight_counts_mut(self: &mut Self) -> &mut HashMap<u32, u32>;
+
+ fn last_seen_msg(self: &Self) -> &HashMap<u32, u32>;
+ fn last_seen_msg_mut(self: &mut Self) -> &mut HashMap<u32, u32>;
+
+ fn marker_line(self: &Self) -> &HashMap<u32, u32>;
+ fn marker_line_mut(self: &mut Self) -> &mut HashMap<u32, u32>;
+
+ fn mark_buffer_as_read(buffer: u32);
+ fn merge_buffers_permanently(buffer1: u32, buffer2: u32);
+ fn remove_buffer(buffer: u32);
+ fn rename_buffer(buffer: u32, new_name: String);
+
+ fn set_buffer_activity(self: &mut Self, buffer: u32, activity: MessageType) {
+ self.activities_mut().insert(buffer, activity);
+ }
+
+ fn set_highlight_count(self: &mut Self, buffer: u32, count: u32) {
+ self.highlight_counts_mut().insert(buffer, count);
+ }
+
+ fn set_last_seen_msg(self: &mut Self, buffer: u32, msgid: u32) {
+ self.last_seen_msg_mut().insert(buffer, msgid);
+ }
+
+ fn set_marker_line(self: &mut Self, buffer: u32, msgid: u32) {
+ self.marker_line_mut().insert(buffer, msgid);
+ }
+}
diff --git a/src/message/signalproxy/objects/identity.rs b/src/message/signalproxy/objects/identity.rs
new file mode 100644
index 0000000..a710e5d
--- /dev/null
+++ b/src/message/signalproxy/objects/identity.rs
@@ -0,0 +1,119 @@
+use crate::primitive::{Variant, VariantMap};
+
+#[derive(Debug, Clone)]
+pub struct Identity {
+ identity_id: i32,
+ identity_name: String,
+ real_name: String,
+ nicks: Vec<String>,
+ away_nick: String,
+ away_nick_enabled: bool,
+ away_reason: String,
+ away_reason_enabled: bool,
+ auto_away_enabled: bool,
+ auto_away_time: i32,
+ auto_away_reason: String,
+ auto_away_reason_enabled: bool,
+ detach_away_enabled: bool,
+ detach_away_reason: String,
+ detach_away_reason_enabled: bool,
+ ident: String,
+ kick_reason: String,
+ part_reason: String,
+ quit_reason: String,
+}
+
+impl From<VariantMap> for Identity {
+ fn from(input: VariantMap) -> Self {
+ Identity {
+ identity_id: match_variant!(input.get("identityId").unwrap(), Variant::i32),
+ identity_name: match_variant!(input.get("identityName").unwrap(), Variant::String),
+ real_name: match_variant!(input.get("realName").unwrap(), Variant::String),
+ nicks: match_variant!(input.get("nicks").unwrap(), Variant::StringList),
+ away_nick: match_variant!(input.get("awayNick").unwrap(), Variant::String),
+ away_nick_enabled: match_variant!(input.get("awayNickEnabled").unwrap(), Variant::bool),
+ away_reason: match_variant!(input.get("awayReason").unwrap(), Variant::String),
+ away_reason_enabled: match_variant!(
+ input.get("awayReasonEnabled").unwrap(),
+ Variant::bool
+ ),
+ auto_away_enabled: match_variant!(input.get("autoAwayEnabled").unwrap(), Variant::bool),
+ auto_away_time: match_variant!(input.get("autoAwayTime").unwrap(), Variant::i32),
+ auto_away_reason: match_variant!(input.get("autoAwayReason").unwrap(), Variant::String),
+ auto_away_reason_enabled: match_variant!(
+ input.get("autoAwayReasonEnabled").unwrap(),
+ Variant::bool
+ ),
+ detach_away_enabled: match_variant!(
+ input.get("detachAwayEnabled").unwrap(),
+ Variant::bool
+ ),
+ detach_away_reason: match_variant!(
+ input.get("detachAwayReason").unwrap(),
+ Variant::String
+ ),
+ detach_away_reason_enabled: match_variant!(
+ input.get("detachAwayReasonEnabled").unwrap(),
+ Variant::bool
+ ),
+ ident: match_variant!(input.get("ident").unwrap(), Variant::String),
+ kick_reason: match_variant!(input.get("kickReason").unwrap(), Variant::String),
+ part_reason: match_variant!(input.get("partReason").unwrap(), Variant::String),
+ quit_reason: match_variant!(input.get("quitReason").unwrap(), Variant::String),
+ }
+ }
+}
+
+impl Into<std::collections::HashMap<String, Variant>> for Identity {
+ fn into(self) -> VariantMap {
+ let mut res = VariantMap::with_capacity(19);
+
+ res.insert("identityId".to_string(), Variant::i32(self.identity_id));
+ res.insert(
+ "identityName".to_string(),
+ Variant::String(self.identity_name),
+ );
+ res.insert("realName".to_string(), Variant::String(self.real_name));
+ res.insert("nicks".to_string(), Variant::StringList(self.nicks));
+ res.insert("awayNick".to_string(), Variant::String(self.away_nick));
+ res.insert(
+ "awayNickEnabled".to_string(),
+ Variant::bool(self.away_nick_enabled),
+ );
+ res.insert("awayReason".to_string(), Variant::String(self.away_reason));
+ res.insert(
+ "awayReasonEnabled".to_string(),
+ Variant::bool(self.away_reason_enabled),
+ );
+ res.insert(
+ "autoAwayEnabled".to_string(),
+ Variant::bool(self.auto_away_enabled),
+ );
+ res.insert(
+ "autoAwayTime".to_string(),
+ Variant::i32(self.auto_away_time),
+ );
+ res.insert(
+ "autoAwayReason".to_string(),
+ Variant::String(self.auto_away_reason),
+ );
+ res.insert(
+ "detachAwayEnabled".to_string(),
+ Variant::bool(self.detach_away_enabled),
+ );
+ res.insert(
+ "detachAwayReason".to_string(),
+ Variant::String(self.detach_away_reason),
+ );
+ res.insert(
+ "detachAwayReasonEnabled".to_string(),
+ Variant::bool(self.detach_away_reason_enabled),
+ );
+ res.insert("ident".to_string(), Variant::String(self.ident));
+ res.insert("kickReason".to_string(), Variant::String(self.kick_reason));
+ res.insert("partReason".to_string(), Variant::String(self.part_reason));
+ res.insert("quitReason".to_string(), Variant::String(self.quit_reason));
+
+ res
+ }
+}
diff --git a/src/message/signalproxy/objects/mod.rs b/src/message/signalproxy/objects/mod.rs
new file mode 100644
index 0000000..a84f6fa
--- /dev/null
+++ b/src/message/signalproxy/objects/mod.rs
@@ -0,0 +1,12 @@
+// mod aliasmanager;
+// mod backlogmanager;
+
+mod buffersyncer;
+mod identity;
+
+pub use buffersyncer::*;
+pub use identity::*;
+
+pub trait Act {
+ fn act(self: Self);
+}
diff --git a/src/message/signalproxy/rpccall.rs b/src/message/signalproxy/rpccall.rs
new file mode 100644
index 0000000..2eac86d
--- /dev/null
+++ b/src/message/signalproxy/rpccall.rs
@@ -0,0 +1,59 @@
+use crate::message::MessageType;
+use crate::primitive::Message;
+use crate::primitive::{Variant, VariantList};
+use crate::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub enum RpcCall {
+ DisplayMessage(DisplayMessage),
+}
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct DisplayMessage {
+ pub message: Message,
+}
+
+// #[derive(Clone, Debug, std::cmp::PartialEq)]
+// pub struct RpcCall {
+// pub slot_name: String,
+// pub params: VariantList,
+// }
+
+impl Serialize for RpcCall {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::RpcCall as i32));
+
+ match self {
+ RpcCall::DisplayMessage(msg) => {
+ res.push(Variant::StringUTF8("2displayMsg(Message)".to_string()));
+ res.push(Variant::Message(msg.message.clone()));
+ }
+ }
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for RpcCall {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ let rpc = match_variant!(res.remove(0), Variant::StringUTF8);
+
+ match rpc.as_str() {
+ "2displayMsg(Message)" => {
+ return Ok((
+ size,
+ RpcCall::DisplayMessage(DisplayMessage {
+ message: match_variant!(res.remove(0), Variant::Message),
+ }),
+ ))
+ }
+ _ => unimplemented!(),
+ }
+ }
+}
diff --git a/src/message/signalproxy/syncmessage.rs b/src/message/signalproxy/syncmessage.rs
new file mode 100644
index 0000000..737f3e0
--- /dev/null
+++ b/src/message/signalproxy/syncmessage.rs
@@ -0,0 +1,46 @@
+use crate::message::MessageType;
+use crate::primitive::{Variant, VariantList};
+use crate::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct SyncMessage {
+ class_name: String,
+ object_name: String,
+ slot_name: String,
+ params: VariantList,
+}
+
+// impl Act for SyncMessage {}
+
+impl Serialize for SyncMessage {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::SyncMessage as i32));
+ res.push(Variant::StringUTF8(self.class_name.clone()));
+ res.push(Variant::StringUTF8(self.object_name.clone()));
+ res.push(Variant::StringUTF8(self.slot_name.clone()));
+
+ res.append(&mut self.params.clone());
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for SyncMessage {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ class_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ object_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ slot_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ params: res,
+ },
+ ))
+ }
+}
diff --git a/src/primitive/datetime.rs b/src/primitive/datetime.rs
index cbcdd51..fadd6a5 100644
--- a/src/primitive/datetime.rs
+++ b/src/primitive/datetime.rs
@@ -76,10 +76,18 @@ impl Deserialize for OffsetDateTime {
let zone = TimeSpec::from(zone as i8);
+ // Default to unix epoch when one of these is set to -1
+ if julian_day == -1 || millis_of_day == -1 {
+ return Ok((pos, OffsetDateTime::unix_epoch()));
+ }
+
let offset: UtcOffset;
match zone {
TimeSpec::LocalUnknown | TimeSpec::LocalStandard | TimeSpec::LocalDST => {
- offset = UtcOffset::current_local_offset()
+ offset = UtcOffset::try_current_local_offset().unwrap_or_else(|_| {
+ log::warn!("could not get local offset defaulting to utc");
+ UtcOffset::UTC
+ })
}
TimeSpec::UTC => offset = UtcOffset::UTC,
TimeSpec::OffsetFromUTC => {
diff --git a/src/primitive/message.rs b/src/primitive/message.rs
index 64b132d..56bae22 100644
--- a/src/primitive/message.rs
+++ b/src/primitive/message.rs
@@ -15,9 +15,20 @@ extern crate bytes;
#[derive(Clone, Debug, std::cmp::PartialEq)]
pub struct Message {
/// The unique, sequential id for the message
+ /// i32 by default i64 if long-message-id features is enabled
+ #[cfg(feature = "long-message-id")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "long-message-id")))]
+ pub msg_id: i64,
+ #[cfg(not(feature = "long-message-id"))]
+ #[cfg_attr(docsrs, doc(cfg(not(feature = "long-message-id"))))]
pub msg_id: i32,
- /// The timestamp of the message in UNIX time (32-bit, seconds, 64-bit if LONGMESSAGE feature enabled)
+ /// The timestamp of the message in UNIX time (32-bit, seconds, 64-bit if long-time feature enabled)
+ #[cfg(feature = "long-time")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "long-time")))]
pub timestamp: i64,
+ #[cfg(not(feature = "long-time"))]
+ #[cfg_attr(docsrs, doc(cfg(not(feature = "long-time"))))]
+ pub timestamp: i32,
/// The message type as it's own type serialized as i32
pub msg_type: MessageType,
/// The flags
@@ -27,14 +38,17 @@ pub struct Message {
/// The sender as nick!ident@host
pub sender: String,
/// The prefix modes of the sender.
- /// Only Some when the SenderPrefix features is enabled
- pub sender_prefixes: Option<String>,
+ #[cfg(feature = "sender-prefixes")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "sender-prefixes")))]
+ pub sender_prefixes: String,
/// The realName of the sender
- /// Only Some when the RichMessage features is enabled
- pub real_name: Option<String>,
+ #[cfg(feature = "rich-messages")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "rich-messages")))]
+ pub real_name: String,
/// The avatarUrl of the sender, if available
- /// Only Some when the RichMessage features is enabled
- pub avatar_url: Option<String>,
+ #[cfg(feature = "rich-messages")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "rich-messages")))]
+ pub avatar_url: String,
/// The message content, already stripped from CTCP formatting, but containing mIRC format codes
pub content: String,
}
@@ -43,35 +57,28 @@ impl Serialize for Message {
fn serialize(&self) -> Result<Vec<u8>, Error> {
let mut values: Vec<u8> = Vec::new();
+ #[cfg(feature = "long-message-id")]
+ values.append(&mut i64::serialize(&self.msg_id)?);
+ #[cfg(not(feature = "long-message-id"))]
values.append(&mut i32::serialize(&self.msg_id)?);
- // TODO LONGMESSAGE feature
- if false {
- values.append(&mut i64::serialize(&self.timestamp)?);
- } else {
- values.append(&mut i32::serialize(&(self.timestamp as i32))?);
- }
+ #[cfg(feature = "long-time")]
+ values.append(&mut i64::serialize(&self.timestamp)?);
+ #[cfg(not(feature = "long-time"))]
+ values.append(&mut i32::serialize(&(self.timestamp as i32))?);
values.append(&mut i32::serialize(&(self.msg_type as i32))?);
values.append(&mut i8::serialize(&(self.flags as i8))?);
values.append(&mut BufferInfo::serialize(&self.buffer)?);
values.append(&mut String::serialize_utf8(&self.sender)?);
- // TODO SenderPrefixes feature
- if false {
- if let Some(x) = &self.sender_prefixes {
- values.append(&mut String::serialize_utf8(&x)?);
- }
- }
+ #[cfg(feature = "sender-prefixes")]
+ values.append(&mut String::serialize_utf8(&self.sender_prefixes)?);
- // TODO RichMessages feature
- if false {
- if let Some(x) = &self.real_name {
- values.append(&mut String::serialize_utf8(&x)?);
- }
- if let Some(x) = &self.avatar_url {
- values.append(&mut String::serialize_utf8(&x)?);
- }
+ #[cfg(feature = "rich-messages")]
+ {
+ values.append(&mut String::serialize_utf8(&self.real_name)?);
+ values.append(&mut String::serialize_utf8(&self.avatar_url)?);
}
values.append(&mut String::serialize_utf8(&self.content)?);
@@ -83,19 +90,27 @@ impl Serialize for Message {
impl Deserialize for Message {
fn parse(b: &[u8]) -> Result<(usize, Self), Error> {
let mut pos = 0;
+ #[cfg(feature = "long-message-id")]
+ let (parsed, msg_id) = i64::parse(&b[pos..])?;
+ #[cfg(not(feature = "long-message-id"))]
let (parsed, msg_id) = i32::parse(&b[pos..])?;
pos += parsed;
// TODO LONGMESSAGES feature
let timestamp;
- if false {
+
+ #[cfg(feature = "long-time")]
+ {
let (parsed, temp_timestamp) = i64::parse(&b[pos..])?;
pos += parsed;
timestamp = temp_timestamp;
- } else {
+ }
+
+ #[cfg(not(feature = "long-time"))]
+ {
let (parsed, temp_timestamp) = i32::parse(&b[pos..])?;
pos += parsed;
- timestamp = temp_timestamp as i64;
+ timestamp = temp_timestamp;
}
let (parsed, msg_type) = i32::parse(&b[pos..])?;
@@ -107,24 +122,27 @@ impl Deserialize for Message {
let (parsed, sender) = String::parse_utf8(&b[pos..])?;
pos += parsed;
- // TODO SenderPrefixes feature
- let mut sender_prefixes = None;
- if false {
+ #[cfg(feature = "sender-prefixes")]
+ let sender_prefixes: String;
+ #[cfg(feature = "sender-prefixes")]
+ {
let (parsed, temp) = String::parse_utf8(&b[pos..])?;
- sender_prefixes = Some(temp);
+ sender_prefixes = temp;
pos += parsed;
}
- // TODO SenderPrefixes feature
- let mut real_name = None;
- let mut avatar_url = None;
- if false {
+ #[cfg(feature = "rich-messages")]
+ let real_name: String;
+ #[cfg(feature = "rich-messages")]
+ let avatar_url: String;
+ #[cfg(feature = "rich-messages")]
+ {
let (parsed, temp) = String::parse_utf8(&b[pos..])?;
- real_name = Some(temp);
+ real_name = temp;
pos += parsed;
let (parsed, temp) = String::parse_utf8(&b[pos..])?;
- avatar_url = Some(temp);
+ avatar_url = temp;
pos += parsed;
}
@@ -140,8 +158,11 @@ impl Deserialize for Message {
flags,
buffer,
sender,
+ #[cfg(feature = "sender-prefixes")]
sender_prefixes,
+ #[cfg(feature = "rich-messages")]
real_name,
+ #[cfg(feature = "rich-messages")]
avatar_url,
content,
},
diff --git a/src/primitive/string.rs b/src/primitive/string.rs
index 86bcdec..dcd4f7c 100644
--- a/src/primitive/string.rs
+++ b/src/primitive/string.rs
@@ -7,8 +7,8 @@ use failure::Error;
use log::trace;
-use crate::{Deserialize, DeserializeUTF8, Serialize, SerializeUTF8};
use crate::util;
+use crate::{Deserialize, DeserializeUTF8, Serialize, SerializeUTF8};
/// We Shadow the String type here as we can only use impl on types in our own scope.
///
@@ -33,7 +33,6 @@ impl SerializeUTF8 for String {
fn serialize_utf8(&self) -> Result<Vec<u8>, Error> {
let mut res: Vec<u8> = Vec::new();
res.extend(self.clone().into_bytes());
- res.extend(vec![0x00]);
util::prepend_byte_len(&mut res);
return Ok(res);
}
@@ -64,6 +63,7 @@ impl Deserialize for String {
}
let res: String = String::from_utf16(&chars).unwrap();
+ trace!("parsed string: {}", res);
return Ok((pos, res));
}
}
@@ -81,6 +81,7 @@ impl DeserializeUTF8 for String {
let ulen = len as usize;
let mut res: String = String::from_utf8(b[4..(ulen + 4)].to_vec())?;
+ trace!("parsed string: {}", res);
// If the last byte is zero remove it
// Receiving a string as bytearray will sometimes have
@@ -89,6 +90,8 @@ impl DeserializeUTF8 for String {
let _ = res.pop();
}
+ trace!("parsed string after trunc: {}", res);
+
return Ok((ulen + 4, res));
}
}
diff --git a/src/primitive/variant.rs b/src/primitive/variant.rs
index 9242d90..be2bebe 100644
--- a/src/primitive/variant.rs
+++ b/src/primitive/variant.rs
@@ -36,6 +36,7 @@ pub enum Variant {
VariantList(VariantList),
String(String),
StringUTF8(String),
+ ByteArray(String),
StringList(StringList),
bool(bool),
u64(u64),
@@ -77,6 +78,11 @@ impl Serialize for Variant {
res.extend(unknown.to_be_bytes().iter());
res.extend(v.serialize_utf8()?.iter());
}
+ Variant::ByteArray(v) => {
+ res.extend(primitive::QBYTEARRAY.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.serialize_utf8()?.iter());
+ }
Variant::StringList(v) => {
res.extend(primitive::QSTRINGLIST.to_be_bytes().iter());
res.extend(unknown.to_be_bytes().iter());
@@ -201,8 +207,7 @@ impl Deserialize for Variant {
return Ok((len + vlen, Variant::StringList(value.clone())));
}
primitive::QDATETIME => {
- trace!(target: "primitive::Variant", "Parsing Variant: Date");
- // let (vlen, value) = DateTime::parse(&b[len..])?;
+ trace!(target: "primitive::Variant", "Parsing Variant: DateTime");
let (vlen, value): (usize, DateTime) = Deserialize::parse(&b[len..])?;
return Ok((len + vlen, Variant::DateTime(value.clone())));
}
@@ -269,12 +274,26 @@ impl Deserialize for Variant {
return Ok((len + user_type_len + vlen, Variant::VariantMap(value)));
}
// As i32
- "BufferId" | "IdentityId" | "NetworkId" | "MsgId" => {
+ "BufferId" | "IdentityId" | "NetworkId" => {
trace!(target: "primitive::Variant", "UserType is i32");
let (vlen, value) = i32::parse(&b[(len + user_type_len)..])?;
return Ok((len + user_type_len + vlen, Variant::i32(value)));
}
+ #[cfg(not(feature = "long-message-id"))]
+ "MsgId" => {
+ trace!(target: "primitive::Variant", "UserType is i32");
+
+ let (vlen, value) = i32::parse(&b[(len + user_type_len)..])?;
+ return Ok((len + user_type_len + vlen, Variant::i32(value)));
+ }
+ #[cfg(feature = "long-message-id")]
+ "MsgId" => {
+ trace!(target: "primitive::Variant", "UserType is i64");
+
+ let (vlen, value) = i64::parse(&b[(len + user_type_len)..])?;
+ return Ok((len + user_type_len + vlen, Variant::i64(value)));
+ }
// As i64
"PeerPtr" => {
trace!(target: "primitive::Variant", "UserType is i64");
diff --git a/src/primitive/variantlist.rs b/src/primitive/variantlist.rs
index 452b927..a802864 100644
--- a/src/primitive/variantlist.rs
+++ b/src/primitive/variantlist.rs
@@ -40,6 +40,7 @@ impl Deserialize for VariantList {
for i in 0..len {
trace!(target: "primitive::VariantList", "Parsing VariantList element: {:?}", i);
let (vlen, val) = Variant::parse(&b[pos..])?;
+ trace!("parsed variant: {:?}", val);
res.push(val);
pos += vlen;
}
diff --git a/src/session/mod.rs b/src/session/mod.rs
new file mode 100644
index 0000000..67045c9
--- /dev/null
+++ b/src/session/mod.rs
@@ -0,0 +1,2 @@
+/// The Session Trait is the main point of entry and implements the basic logic
+pub trait Session {}