use crate::ui::login_event::Login;
use mac_address::get_mac_address;
use md5::{Digest, Md5};
use serde::{Deserialize, Serialize};
use serde_llsd_benthic::{ser::xml_rpc, LLSDValue};
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::fs::File;
use std::io::Read;
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct SimulatorLoginProtocol {
pub first: String,
pub last: String,
pub passwd: String,
pub start: String,
pub channel: String,
pub version: String,
pub platform: String,
pub platform_string: String,
pub platform_version: String,
pub mac: String,
pub id0: String,
pub agree_to_tos: bool,
pub read_critical: bool,
pub viewer_digest: Option<String>,
pub address_size: i32,
pub extended_errors: bool,
pub last_exec_event: Option<i32>,
pub last_exec_duration: i32,
pub skipoptional: Option<bool>,
pub host_id: String,
pub mfa_hash: String,
pub token: String,
pub options: SimulatorLoginOptions,
}
impl SimulatorLoginProtocol {
pub fn to_llsd_map(&self) -> LLSDValue {
let mut map = HashMap::new();
map.insert("first".to_string(), LLSDValue::String(self.first.clone()));
map.insert("last".to_string(), LLSDValue::String(self.last.clone()));
map.insert("passwd".to_string(), LLSDValue::String(self.passwd.clone()));
map.insert("start".to_string(), LLSDValue::String(self.start.clone()));
map.insert(
"channel".to_string(),
LLSDValue::String(self.channel.clone()),
);
map.insert(
"version".to_string(),
LLSDValue::String(self.version.clone()),
);
map.insert(
"platform".to_string(),
LLSDValue::String(self.platform.clone()),
);
map.insert(
"platform_string".to_string(),
LLSDValue::String(self.platform_string.clone()),
);
map.insert(
"platform_version".to_string(),
LLSDValue::String(self.platform_version.clone()),
);
map.insert("mac".to_string(), LLSDValue::String(self.mac.clone()));
map.insert("id0".to_string(), LLSDValue::String(self.id0.clone()));
map.insert(
"agree_to_tos".to_string(),
LLSDValue::Boolean(self.agree_to_tos),
);
map.insert(
"read_critical".to_string(),
LLSDValue::Boolean(self.read_critical),
);
if let Some(digest) = &self.viewer_digest {
map.insert(
"viewer_digest".to_string(),
LLSDValue::String(digest.clone()),
);
}
map.insert(
"address_size".to_string(),
LLSDValue::Integer(self.address_size),
);
map.insert(
"extended_errors".to_string(),
LLSDValue::Boolean(self.extended_errors),
);
if let Some(last) = self.last_exec_event {
map.insert("last_exec_event".to_string(), LLSDValue::Integer(last));
}
map.insert(
"last_exec_duration".to_string(),
LLSDValue::Integer(self.last_exec_duration),
);
if let Some(skip) = self.skipoptional {
map.insert("skipoptional".to_string(), LLSDValue::Boolean(skip));
}
map.insert(
"host_id".to_string(),
LLSDValue::String(self.host_id.clone()),
);
map.insert(
"mfa_hash".to_string(),
LLSDValue::String(self.mfa_hash.clone()),
);
map.insert("token".to_string(), LLSDValue::String(self.token.clone()));
map.insert("options".to_string(), self.options.to_llsd());
LLSDValue::Map(map)
}
}
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct SimulatorLoginOptions {
pub adult_compliant: Option<bool>,
pub advanced_mode: Option<bool>,
pub avatar_picker_url: Option<bool>,
pub buddy_list: Option<bool>,
pub classified_categories: Option<bool>,
pub currency: Option<bool>,
pub destination_guide_url: Option<bool>,
pub display_names: Option<bool>,
pub event_categories: Option<bool>,
pub gestures: Option<bool>,
pub global_textures: Option<bool>,
pub inventory_root: Option<bool>,
pub inventory_skeleton: Option<bool>,
pub inventory_lib_root: Option<bool>,
pub inventory_lib_owner: Option<bool>,
pub inventory_skel_lib: Option<bool>,
pub login_flags: Option<bool>,
pub max_agent_groups: Option<bool>,
pub max_groups: Option<bool>,
pub map_server_url: Option<bool>,
pub newuser_config: Option<bool>,
pub search: Option<bool>,
pub tutorial_setting: Option<bool>,
pub ui_config: Option<bool>,
pub voice_config: Option<bool>,
}
impl SimulatorLoginOptions {
pub fn to_llsd(&self) -> LLSDValue {
let mut vec = Vec::new();
if self.inventory_root.unwrap_or(false) {
vec.push(LLSDValue::String("inventory-root".to_string()));
}
if self.inventory_skeleton.unwrap_or(false) {
vec.push(LLSDValue::String("inventory-skeleton".to_string()));
}
if self.inventory_lib_root.unwrap_or(false) {
vec.push(LLSDValue::String("inventory-lib-root".to_string()));
}
if self.inventory_lib_owner.unwrap_or(false) {
vec.push(LLSDValue::String("inventory-lib-owner".to_string()));
}
if self.inventory_skel_lib.unwrap_or(false) {
vec.push(LLSDValue::String("inventory-skel-lib".to_string()));
}
if self.gestures.unwrap_or(false) {
vec.push(LLSDValue::String("gestures".to_string()));
}
if self.event_categories.unwrap_or(false) {
vec.push(LLSDValue::String("event_categories".to_string()));
}
if self.classified_categories.unwrap_or(false) {
vec.push(LLSDValue::String("classified_categories".to_string()));
}
if self.adult_compliant.unwrap_or(false) {
vec.push(LLSDValue::String("adult_compliant".to_string()));
}
if self.buddy_list.unwrap_or(false) {
vec.push(LLSDValue::String("buddy-list".to_string()));
}
if self.global_textures.unwrap_or(false) {
vec.push(LLSDValue::String("global-textures".to_string()));
}
if self.login_flags.unwrap_or(false) {
vec.push(LLSDValue::String("login-flags".to_string()));
}
if self.max_agent_groups.unwrap_or(false) {
vec.push(LLSDValue::String("max-agent-groups".to_string()));
}
LLSDValue::Array(vec)
}
}
impl SimulatorLoginProtocol {
pub fn to_xmlrpc(self) -> String {
let llsd = self.to_llsd_map();
xml_rpc::to_string(&llsd, false, "login_to_simulator").unwrap()
}
pub fn new(login: Login) -> Self {
SimulatorLoginProtocol {
first: login.first,
last: login.last,
passwd: hash_passwd(login.passwd),
start: login.start,
channel: login.channel,
version: env!("CARGO_PKG_VERSION").to_string(),
platform: match env::consts::FAMILY {
"mac" => "mac".to_string(),
"win" => "win".to_string(),
"unix" => "lin".to_string(),
_ => "lin".to_string(),
},
platform_string: sys_info::os_release().unwrap_or_default(),
platform_version: sys_info::os_release().unwrap_or_default(),
mac: match get_mac_address() {
Ok(Some(mac)) => format!("{}", mac),
_ => format!("{}", 00000000000000000000000000000000),
},
id0: "unused".to_string(), agree_to_tos: login.agree_to_tos,
read_critical: login.read_critical,
viewer_digest: match hash_viewer_digest() {
Ok(viewer_digest) => Some(viewer_digest),
Err(_) => Some("unused".to_string()),
},
address_size: 64, extended_errors: true, last_exec_event: None, last_exec_duration: 0, skipoptional: None, host_id: "".to_string(), mfa_hash: "".to_string(), token: "".to_string(), options: SimulatorLoginOptions::default(), }
}
}
fn hash_passwd(passwd_raw: String) -> String {
let mut hasher = md5::Md5::new();
hasher.update(passwd_raw);
format!("$1${:x}", hasher.finalize())
}
fn hash_viewer_digest() -> Result<String, Box<dyn Error>> {
let path = env::args().next().ok_or("No argument found")?;
let mut f = File::open(path)?;
let mut byt = Vec::new();
f.read_to_end(&mut byt)?;
let mut hasher = Md5::new();
hasher.update(&byt);
let hash = hasher.finalize();
Ok(format!("{:x}", hash))
}