diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index 62962cc83..c3a5a24d6 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -1,13 +1,8 @@ use super::cstr_to_string; use crate::flutter::{self, APP_TYPE_CM, APP_TYPE_MAIN, SESSIONS}; -use hbb_common::{lazy_static, libc, log, message_proto::Plugin, ResultType}; -use serde_derive::{Deserialize, Serialize}; +use hbb_common::{lazy_static, log, message_proto::Plugin}; use serde_json; -use std::{ - collections::HashMap, - ffi::{c_char, CStr}, - sync::Arc, -}; +use std::{collections::HashMap, ffi::c_char, sync::Arc}; const MSG_TO_PEER_TARGET: &str = "peer"; const MSG_TO_UI_TARGET: &str = "ui"; @@ -85,7 +80,7 @@ pub fn callback_msg( let content = std::string::String::from_utf8(content_slice[2..].to_vec()) .unwrap_or("".to_string()); let mut m = HashMap::new(); - m.insert("name", "plugin"); + m.insert("name", "plugin_event"); m.insert("peer", &peer); m.insert("content", &content); let event = serde_json::to_string(&m).unwrap_or("".to_string()); @@ -99,7 +94,7 @@ pub fn callback_msg( || channel & MSG_TO_UI_FLUTTER_CHANNEL_FORWARD != 0 { let _res = flutter::push_session_event( - APP_TYPE_CM, + &peer, "plugin", vec![("peer", &peer), ("content", &content)], ); diff --git a/src/plugin/config.rs b/src/plugin/config.rs index e69de29bb..7a9228425 100644 --- a/src/plugin/config.rs +++ b/src/plugin/config.rs @@ -0,0 +1,196 @@ +use super::desc::ConfigItem; +use hbb_common::{bail, config::Config as HbbConfig, lazy_static, ResultType}; +use serde_derive::{Deserialize, Serialize}; +use std::{ + ops::{Deref, DerefMut}, + sync::{Arc, Mutex}, + {collections::HashMap, path::PathBuf}, +}; + +lazy_static::lazy_static! { + static ref CONFIG_LOCAL: Arc>> = Default::default(); + static ref CONFIG_LOCAL_ITEMS: Arc>>> = Default::default(); + static ref CONFIG_PEERS: Arc>> = Default::default(); + static ref CONFIG_PEER_ITEMS: Arc>>> = Default::default(); +} + +#[derive(Debug, Default, Serialize, Deserialize)] +struct LocalConfig(HashMap); +#[derive(Debug, Default, Serialize, Deserialize)] +struct PeerConfig(HashMap); +type PeersConfig = HashMap; + +#[inline] +fn path_plugins(id: &str) -> PathBuf { + HbbConfig::path("plugins").join(id) +} + +impl Deref for LocalConfig { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for LocalConfig { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Deref for PeerConfig { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for PeerConfig { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl LocalConfig { + #[inline] + fn path(id: &str) -> PathBuf { + path_plugins(id).join("local.toml") + } + + #[inline] + pub fn load(id: &str) { + let mut conf = hbb_common::config::load_path::(Self::path(id)); + if let Some(items) = CONFIG_LOCAL_ITEMS.lock().unwrap().get(id) { + for item in items { + if !conf.contains_key(&item.key) { + conf.insert(item.key.to_owned(), item.default.to_owned()); + } + } + } + CONFIG_LOCAL.lock().unwrap().insert(id.to_owned(), conf); + } + + #[inline] + fn save(id: &str) -> ResultType<()> { + match CONFIG_LOCAL.lock().unwrap().get(id) { + Some(config) => hbb_common::config::store_path(Self::path(id), config), + None => bail!("No such plugin {}", id), + } + } + + #[inline] + pub fn get(id: &str, key: &str) -> Option { + if let Some(conf) = CONFIG_LOCAL.lock().unwrap().get(id) { + return conf.get(key).map(|s| s.to_owned()); + } + Self::load(id); + CONFIG_LOCAL + .lock() + .unwrap() + .get(id)? + .get(key) + .map(|s| s.to_owned()) + } + + #[inline] + pub fn set(id: &str, key: &str, value: &str) -> ResultType<()> { + match CONFIG_LOCAL.lock().unwrap().get_mut(id) { + Some(config) => { + config.insert(key.to_owned(), value.to_owned()); + hbb_common::config::store_path(Self::path(id), config) + } + None => bail!("No such plugin {}", id), + } + } +} + +impl PeerConfig { + #[inline] + fn path(id: &str, peer: &str) -> PathBuf { + path_plugins(id) + .join("peers") + .join(format!("{}.toml", peer)) + } + + #[inline] + pub fn load(id: &str, peer: &str) { + let mut conf = hbb_common::config::load_path::(Self::path(id, peer)); + if let Some(items) = CONFIG_PEER_ITEMS.lock().unwrap().get(id) { + for item in items { + if !conf.contains_key(&item.key) { + conf.insert(item.key.to_owned(), item.default.to_owned()); + } + } + } + match CONFIG_PEERS.lock().unwrap().get_mut(id) { + Some(peers) => { + peers.insert(peer.to_owned(), conf); + } + None => { + let mut peers = HashMap::new(); + peers.insert(peer.to_owned(), conf); + CONFIG_PEERS.lock().unwrap().insert(id.to_owned(), peers); + } + } + } + + #[inline] + fn save(id: &str, peer: &str) -> ResultType<()> { + match CONFIG_PEERS.lock().unwrap().get(id) { + Some(peers) => match peers.get(peer) { + Some(config) => hbb_common::config::store_path(Self::path(id, peer), config), + None => bail!("No such peer {}", peer), + }, + None => bail!("No such plugin {}", id), + } + } + + #[inline] + pub fn get(id: &str, peer: &str, key: &str) -> Option { + if let Some(peers) = CONFIG_PEERS.lock().unwrap().get(id) { + if let Some(conf) = peers.get(peer) { + return conf.get(key).map(|s| s.to_owned()); + } + } + Self::load(id, peer); + CONFIG_PEERS + .lock() + .unwrap() + .get(id)? + .get(peer)? + .get(key) + .map(|s| s.to_owned()) + } + + #[inline] + pub fn set(id: &str, peer: &str, key: &str, value: &str) -> ResultType<()> { + match CONFIG_PEERS.lock().unwrap().get_mut(id) { + Some(peers) => match peers.get_mut(peer) { + Some(config) => { + config.insert(key.to_owned(), value.to_owned()); + hbb_common::config::store_path(Self::path(id, peer), config) + } + None => bail!("No such peer {}", peer), + }, + None => bail!("No such plugin {}", id), + } + } +} + +#[inline] +pub(super) fn set_local_items(id: &str, items: &Vec) { + CONFIG_LOCAL_ITEMS + .lock() + .unwrap() + .insert(id.to_owned(), items.clone()); +} + +#[inline] +pub(super) fn set_peer_items(id: &str, items: &Vec) { + CONFIG_PEER_ITEMS + .lock() + .unwrap() + .insert(id.to_owned(), items.clone()); +} diff --git a/src/plugin/desc.rs b/src/plugin/desc.rs index 8d345b7d2..26eda187a 100644 --- a/src/plugin/desc.rs +++ b/src/plugin/desc.rs @@ -1,10 +1,10 @@ use hbb_common::ResultType; -use serde_derive::Deserialize; +use serde_derive::{Deserialize, Serialize}; use serde_json; use std::collections::HashMap; use std::ffi::{c_char, CStr}; -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct UiButton { key: String, text: String, @@ -13,7 +13,7 @@ pub struct UiButton { action: String, // The action to be triggered when the button is clicked. } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct UiCheckbox { key: String, text: String, @@ -21,7 +21,7 @@ pub struct UiCheckbox { action: String, // The action to be triggered when the checkbox is checked or unchecked. } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(tag = "t", content = "c")] pub enum UiType { Button(UiButton), @@ -30,28 +30,21 @@ pub enum UiType { #[derive(Debug, Deserialize)] pub struct Location { - core: String, - ui: HashMap, + pub ui: HashMap, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Clone, Deserialize)] pub struct ConfigItem { - key: String, - value: String, - default: String, - description: String, -} - -#[derive(Debug, Deserialize)] -pub struct Configs { - pub local: Vec, - pub session: Vec, + pub key: String, + pub value: String, + pub default: String, + pub description: String, } #[derive(Debug, Deserialize)] pub struct Config { - pub host: Configs, - pub client: Configs, + pub local: Vec, + pub peer: Vec, } #[derive(Debug, Deserialize)] diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index ab233a503..9e3e5993d 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -1,8 +1,5 @@ -use hbb_common::{dlopen::symbor::Library, log, ResultType}; -use std::{ - ffi::{c_char, CStr}, - path::Path, -}; +use hbb_common::ResultType; +use std::ffi::{c_char, CStr}; mod callback_msg; mod config; diff --git a/src/plugin/plugins.rs b/src/plugin/plugins.rs index d93ac5938..a26f7a77c 100644 --- a/src/plugin/plugins.rs +++ b/src/plugin/plugins.rs @@ -1,19 +1,13 @@ use std::{ collections::HashMap, - ffi::{c_char, CStr}, - path::PathBuf, + ffi::c_char, + path::Path, sync::{Arc, RwLock}, }; use super::{callback_msg, desc::Desc}; -use hbb_common::{ - anyhow::Error, - bail, - dlopen::symbor::Library, - lazy_static, libc, log, - log::{debug, error}, - ResultType, -}; +use crate::flutter; +use hbb_common::{bail, dlopen::symbor::Library, lazy_static, libc, log, ResultType}; lazy_static::lazy_static! { pub static ref PLUGINS: Arc>> = Default::default(); @@ -109,7 +103,7 @@ make_plugin!( fn_call: PluginFuncCall ); -pub fn load_plugins(dir: &str) -> ResultType<()> { +pub fn load_plugins>(dir: P) -> ResultType<()> { for entry in std::fs::read_dir(dir)? { match entry { Ok(entry) => { @@ -156,7 +150,39 @@ fn load_plugin(path: &str) -> ResultType<()> { let desc = desc_res?; let id = desc.id().to_string(); (plugin.fn_set_cb_msg)(callback_msg::callback_msg); + update_config(&desc); + reload_ui(&desc); plugin.desc = Some(desc); PLUGINS.write().unwrap().insert(id, plugin); Ok(()) } + +fn update_config(desc: &Desc) { + super::config::set_local_items(desc.id(), &desc.config().local); + super::config::set_peer_items(desc.id(), &desc.config().peer); +} + +fn reload_ui(desc: &Desc) { + for (location, ui) in desc.location().ui.iter() { + let v: Vec<&str> = location.split('|').collect(); + // The first element is the "client" or "host". + // The second element is the "main", "remote", "cm", "file transfer", "port forward". + if v.len() >= 2 { + let available_channels = vec![ + flutter::APP_TYPE_MAIN, + flutter::APP_TYPE_DESKTOP_REMOTE, + flutter::APP_TYPE_CM, + flutter::APP_TYPE_DESKTOP_FILE_TRANSFER, + flutter::APP_TYPE_DESKTOP_PORT_FORWARD, + ]; + if available_channels.contains(&v[1]) { + if let Ok(ui) = serde_json::to_string(&ui) { + let mut m = HashMap::new(); + m.insert("name", "plugin_reload"); + m.insert("ui", &ui); + flutter::push_global_event(v[1], serde_json::to_string(&m).unwrap()); + } + } + } + } +}