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 crate::frame::QuasselCodec;
use failure::Error;
use log::{trace, debug, info, error};
use crate::message::ConnAck;
extern crate log;
pub struct Client<T: AsyncRead + AsyncWrite + Unpin> {
stream: Framed<T, QuasselCodec>,
pub tls: bool,
pub compression: bool,
pub state: ClientState,
pub user: User,
pub funcs: Funcs,
}
pub struct Funcs {
pub rpc_call: crate::message::RpcCallClient,
}
pub struct User {
pub name: String,
pub password: String,
}
pub enum ClientState {
Handshake,
Connected,
}
impl <T: AsyncRead + AsyncWrite + Unpin> Client<T> {
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();
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) -> Result<Client<TcpStream>, 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) -> Result<Client<TlsStream<TcpStream>>, 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>(client: &mut Client<T>, 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>(client: &mut Client<T>, buf: &[u8]) -> Result<(), Error> {
use crate::message::Message;
use crate::primitive::{VariantList, Variant};
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(msg) => {
match msg.slot_name.as_str() {
"2displayMsg(Message)" => {
(client.funcs.rpc_call.display_message)(match_variant!(msg.params[0], Variant::Message));
},
_ => {},
}
}
Message::InitRequest(_) => {}
Message::InitData(_) => {}
Message::HeartBeat(_) => {}
Message::HeartBeatReply(_) => {}
}
return Ok(());
}
pub async fn init(stream: &mut TcpStream, tls: bool, compression: bool) -> Result<ConnAck, Error> {
use crate::Deserialize;
let mut init: Vec<u8> = vec![];
let mut handshake: u32 = 0x42b33f00;
if tls {
info!(target: "init", "Enabled TLS");
handshake |= 0x01;
}
if compression {
info!(target: "init", "Enabled Compression");
handshake |= 0x02;
}
let mut proto: u32 = 0x00000002;
let fin: u32 = 0x80000000;
proto |= fin;
init.extend(handshake.to_be_bytes().iter());
init.extend(proto.to_be_bytes().iter());
stream.write(&init).await?;
let mut buf = [0; 4];
stream.read(&mut buf).await?;
let (_, connack) = ConnAck::parse(&buf)?;
Ok(connack)
}