Merge pull request #4383 from fufesou/feat/plugin_framework_uninstall
plugin_framework, uninstall
This commit is contained in:
commit
d927ece556
@ -158,19 +158,6 @@ class PluginInfo with ChangeNotifier {
|
|||||||
bool get installed => installedVersion.isNotEmpty;
|
bool get installed => installedVersion.isNotEmpty;
|
||||||
bool get needUpdate => installed && installedVersion != meta.version;
|
bool get needUpdate => installed && installedVersion != meta.version;
|
||||||
|
|
||||||
void update(PluginInfo plugin) {
|
|
||||||
assert(plugin.meta.id == meta.id, 'Plugin id not match');
|
|
||||||
if (plugin.meta.id != meta.id) {
|
|
||||||
// log error
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sourceInfo = plugin.sourceInfo;
|
|
||||||
meta = plugin.meta;
|
|
||||||
installedVersion = plugin.installedVersion;
|
|
||||||
invalidReason = plugin.invalidReason;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setInstall(String msg) {
|
void setInstall(String msg) {
|
||||||
if (msg == "finished") {
|
if (msg == "finished") {
|
||||||
msg = '';
|
msg = '';
|
||||||
@ -213,8 +200,6 @@ class PluginManager with ChangeNotifier {
|
|||||||
void handleEvent(Map<String, dynamic> evt) {
|
void handleEvent(Map<String, dynamic> evt) {
|
||||||
if (evt['plugin_list'] != null) {
|
if (evt['plugin_list'] != null) {
|
||||||
_handlePluginList(evt['plugin_list']);
|
_handlePluginList(evt['plugin_list']);
|
||||||
} else if (evt['plugin_update'] != null) {
|
|
||||||
_handlePluginUpdate(evt['plugin_update']);
|
|
||||||
} else if (evt['plugin_install'] != null && evt['id'] != null) {
|
} else if (evt['plugin_install'] != null && evt['id'] != null) {
|
||||||
_handlePluginInstall(evt['id'], evt['plugin_install']);
|
_handlePluginInstall(evt['id'], evt['plugin_install']);
|
||||||
} else if (evt['plugin_uninstall'] != null && evt['id'] != null) {
|
} else if (evt['plugin_uninstall'] != null && evt['id'] != null) {
|
||||||
@ -236,21 +221,6 @@ class PluginManager with ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handlePluginUpdate(Map<String, dynamic> evt) {
|
|
||||||
final plugin = _getPluginFromEvent(evt);
|
|
||||||
if (plugin == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < _plugins.length; i++) {
|
|
||||||
if (_plugins[i].meta.id == plugin.meta.id) {
|
|
||||||
_plugins[i].update(plugin);
|
|
||||||
_sortPlugins();
|
|
||||||
notifyListeners();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handlePluginList(String pluginList) {
|
void _handlePluginList(String pluginList) {
|
||||||
_plugins.clear();
|
_plugins.clear();
|
||||||
try {
|
try {
|
||||||
@ -269,6 +239,7 @@ class PluginManager with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handlePluginInstall(String id, String msg) {
|
void _handlePluginInstall(String id, String msg) {
|
||||||
|
debugPrint('Plugin \'$id\' install msg $msg');
|
||||||
for (var i = 0; i < _plugins.length; i++) {
|
for (var i = 0; i < _plugins.length; i++) {
|
||||||
if (_plugins[i].meta.id == id) {
|
if (_plugins[i].meta.id == id) {
|
||||||
_plugins[i].setInstall(msg);
|
_plugins[i].setInstall(msg);
|
||||||
@ -280,6 +251,7 @@ class PluginManager with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handlePluginUninstall(String id, String msg) {
|
void _handlePluginUninstall(String id, String msg) {
|
||||||
|
debugPrint('Plugin \'$id\' uninstall msg $msg');
|
||||||
for (var i = 0; i < _plugins.length; i++) {
|
for (var i = 0; i < _plugins.length; i++) {
|
||||||
if (_plugins[i].meta.id == id) {
|
if (_plugins[i].meta.id == id) {
|
||||||
_plugins[i].setUninstall(msg);
|
_plugins[i].setUninstall(msg);
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
use crate::platform::breakdown_callback;
|
use crate::platform::breakdown_callback;
|
||||||
use hbb_common::log;
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
use hbb_common::platform::register_breakdown_handler;
|
use hbb_common::platform::register_breakdown_handler;
|
||||||
|
use hbb_common::{allow_err, log};
|
||||||
|
|
||||||
/// shared by flutter and sciter main function
|
/// shared by flutter and sciter main function
|
||||||
///
|
///
|
||||||
@ -106,15 +106,7 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
}
|
}
|
||||||
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
if args.is_empty() || "--server" == (&args[0] as &str) {
|
init_plugins(&args);
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
let load_plugins = true;
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
let load_plugins = crate::platform::is_installed();
|
|
||||||
if load_plugins {
|
|
||||||
crate::plugin::init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
std::thread::spawn(move || crate::start_server(false));
|
std::thread::spawn(move || crate::start_server(false));
|
||||||
} else {
|
} else {
|
||||||
@ -244,10 +236,17 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
if args[0] == "--plugin-install" {
|
if args[0] == "--plugin-install" {
|
||||||
if args.len() == 3 {
|
if args.len() == 2 {
|
||||||
|
crate::plugin::change_uninstall_plugin(&args[1], false);
|
||||||
|
} else if args.len() == 3 {
|
||||||
crate::plugin::install_plugin_with_url(&args[1], &args[2]);
|
crate::plugin::install_plugin_with_url(&args[1], &args[2]);
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
|
} else if args[0] == "--plugin-uninstall" {
|
||||||
|
if args.len() == 2 {
|
||||||
|
crate::plugin::change_uninstall_plugin(&args[1], true);
|
||||||
|
}
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,6 +257,23 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
return Some(args);
|
return Some(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
fn init_plugins(args: &Vec<String>) {
|
||||||
|
if args.is_empty() || "--server" == (&args[0] as &str) {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
let load_plugins = true;
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
let load_plugins = crate::platform::is_installed();
|
||||||
|
if load_plugins {
|
||||||
|
crate::plugin::init();
|
||||||
|
}
|
||||||
|
} else if "--service" == (&args[0] as &str) {
|
||||||
|
allow_err!(crate::plugin::remove_uninstalled());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn import_config(path: &str) {
|
fn import_config(path: &str) {
|
||||||
use hbb_common::{config::*, get_exe_time, get_modified_time};
|
use hbb_common::{config::*, get_exe_time, get_modified_time};
|
||||||
let path2 = path.replace(".toml", "2.toml");
|
let path2 = path.replace(".toml", "2.toml");
|
||||||
|
@ -217,7 +217,6 @@ impl PeerConfig {
|
|||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct PluginStatus {
|
pub struct PluginStatus {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub uninstalled: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MANAGER_VERSION: &str = "0.1.0";
|
const MANAGER_VERSION: &str = "0.1.0";
|
||||||
@ -268,9 +267,16 @@ impl ManagerConfig {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_plugin_option(id: &str, key: &str) -> Option<String> {
|
pub fn get_plugin_option(id: &str, key: &str) -> Option<String> {
|
||||||
let lock = CONFIG_MANAGER.lock().unwrap();
|
let lock = CONFIG_MANAGER.lock().unwrap();
|
||||||
let status = lock.plugins.get(id)?;
|
|
||||||
match key {
|
match key {
|
||||||
"enabled" => Some(status.enabled.to_string()),
|
"enabled" => {
|
||||||
|
let enabled = lock
|
||||||
|
.plugins
|
||||||
|
.get(id)
|
||||||
|
.map(|status| status.enabled.to_owned())
|
||||||
|
.unwrap_or(true.to_owned())
|
||||||
|
.to_string();
|
||||||
|
Some(enabled)
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,13 +286,7 @@ impl ManagerConfig {
|
|||||||
if let Some(status) = lock.plugins.get_mut(id) {
|
if let Some(status) = lock.plugins.get_mut(id) {
|
||||||
status.enabled = enabled;
|
status.enabled = enabled;
|
||||||
} else {
|
} else {
|
||||||
lock.plugins.insert(
|
lock.plugins.insert(id.to_owned(), PluginStatus { enabled });
|
||||||
id.to_owned(),
|
|
||||||
PluginStatus {
|
|
||||||
enabled,
|
|
||||||
uninstalled: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
hbb_common::config::store_path(Self::path(), &*lock)
|
hbb_common::config::store_path(Self::path(), &*lock)
|
||||||
}
|
}
|
||||||
@ -309,13 +309,8 @@ impl ManagerConfig {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn add_plugin(id: &str) -> ResultType<()> {
|
pub fn add_plugin(id: &str) -> ResultType<()> {
|
||||||
let mut lock = CONFIG_MANAGER.lock().unwrap();
|
let mut lock = CONFIG_MANAGER.lock().unwrap();
|
||||||
lock.plugins.insert(
|
lock.plugins
|
||||||
id.to_owned(),
|
.insert(id.to_owned(), PluginStatus { enabled: true });
|
||||||
PluginStatus {
|
|
||||||
enabled: true,
|
|
||||||
uninstalled: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
hbb_common::config::store_path(Self::path(), &*lock)
|
hbb_common::config::store_path(Self::path(), &*lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,35 +320,6 @@ impl ManagerConfig {
|
|||||||
lock.plugins.remove(id);
|
lock.plugins.remove(id);
|
||||||
hbb_common::config::store_path(Self::path(), &*lock)
|
hbb_common::config::store_path(Self::path(), &*lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_uninstalled(id: &str) -> bool {
|
|
||||||
CONFIG_MANAGER
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.plugins
|
|
||||||
.get(id)
|
|
||||||
.map(|p| p.uninstalled)
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_uninstall(id: &str, uninstall: bool) -> ResultType<()> {
|
|
||||||
let mut lock = CONFIG_MANAGER.lock().unwrap();
|
|
||||||
if let Some(status) = lock.plugins.get_mut(id) {
|
|
||||||
status.uninstalled = uninstall;
|
|
||||||
} else {
|
|
||||||
lock.plugins.insert(
|
|
||||||
id.to_owned(),
|
|
||||||
PluginStatus {
|
|
||||||
enabled: true,
|
|
||||||
uninstalled: uninstall,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
hbb_common::config::store_path(Self::path(), &*lock)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) extern "C" fn cb_get_local_peer_id() -> *const c_char {
|
pub(super) extern "C" fn cb_get_local_peer_id() -> *const c_char {
|
||||||
|
@ -4,7 +4,7 @@ use serde_json;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::{c_char, CStr};
|
use std::ffi::{c_char, CStr};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct UiButton {
|
pub struct UiButton {
|
||||||
key: String,
|
key: String,
|
||||||
text: String,
|
text: String,
|
||||||
@ -13,7 +13,7 @@ pub struct UiButton {
|
|||||||
action: String, // The action to be triggered when the button is clicked.
|
action: String, // The action to be triggered when the button is clicked.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct UiCheckbox {
|
pub struct UiCheckbox {
|
||||||
key: String,
|
key: String,
|
||||||
text: String,
|
text: String,
|
||||||
@ -21,14 +21,14 @@ pub struct UiCheckbox {
|
|||||||
action: String, // The action to be triggered when the checkbox is checked or unchecked.
|
action: String, // The action to be triggered when the checkbox is checked or unchecked.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(tag = "t", content = "c")]
|
#[serde(tag = "t", content = "c")]
|
||||||
pub enum UiType {
|
pub enum UiType {
|
||||||
Button(UiButton),
|
Button(UiButton),
|
||||||
Checkbox(UiCheckbox),
|
Checkbox(UiCheckbox),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Location {
|
pub struct Location {
|
||||||
pub ui: HashMap<String, Vec<UiType>>,
|
pub ui: HashMap<String, Vec<UiType>>,
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ pub struct Meta {
|
|||||||
pub publish_info: PublishInfo,
|
pub publish_info: PublishInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Desc {
|
pub struct Desc {
|
||||||
meta: Meta,
|
meta: Meta,
|
||||||
need_reboot: bool,
|
need_reboot: bool,
|
||||||
|
@ -217,7 +217,6 @@ pub async fn handle_plugin(plugin: Plugin, stream: &mut Connection) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Plugin::Load(id) => {
|
Plugin::Load(id) => {
|
||||||
allow_err!(super::config::ManagerConfig::set_uninstall(&id, false));
|
|
||||||
allow_err!(super::load_plugin(&id));
|
allow_err!(super::load_plugin(&id));
|
||||||
}
|
}
|
||||||
Plugin::Reload(id) => {
|
Plugin::Reload(id) => {
|
||||||
|
@ -7,13 +7,13 @@ use hbb_common::{allow_err, bail, log, tokio, toml};
|
|||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{HashMap, HashSet},
|
||||||
fs,
|
fs::{read_to_string, remove_dir_all, OpenOptions},
|
||||||
|
io::Write,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
const MSG_TO_UI_PLUGIN_MANAGER_LIST: &str = "plugin_list";
|
const MSG_TO_UI_PLUGIN_MANAGER_LIST: &str = "plugin_list";
|
||||||
const MSG_TO_UI_PLUGIN_MANAGER_UPDATE: &str = "plugin_update";
|
|
||||||
const MSG_TO_UI_PLUGIN_MANAGER_INSTALL: &str = "plugin_install";
|
const MSG_TO_UI_PLUGIN_MANAGER_INSTALL: &str = "plugin_install";
|
||||||
const MSG_TO_UI_PLUGIN_MANAGER_UNINSTALL: &str = "plugin_uninstall";
|
const MSG_TO_UI_PLUGIN_MANAGER_UNINSTALL: &str = "plugin_uninstall";
|
||||||
|
|
||||||
@ -186,6 +186,18 @@ fn elevate_install(
|
|||||||
crate::platform::elevate(args)
|
crate::platform::elevate(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn elevate_uninstall(plugin_id: &str) -> ResultType<bool> {
|
||||||
|
crate::platform::elevate(&format!("--plugin-uninstall {}", plugin_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
|
fn elevate_install(plugin_id: &str) -> ResultType<bool> {
|
||||||
|
crate::platform::elevate(vec!["--plugin-uninstall", plugin_id])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn install_plugin(id: &str) -> ResultType<()> {
|
pub fn install_plugin(id: &str) -> ResultType<()> {
|
||||||
match PLUGIN_INFO.lock().unwrap().get(id) {
|
match PLUGIN_INFO.lock().unwrap().get(id) {
|
||||||
Some(plugin) => {
|
Some(plugin) => {
|
||||||
@ -218,7 +230,7 @@ pub fn install_plugin(id: &str) -> ResultType<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_uninstalled_plugins() -> ResultType<Vec<String>> {
|
fn get_uninstalled_plugins(uninstalled_plugin_set: &HashSet<String>) -> ResultType<Vec<String>> {
|
||||||
let plugins_dir = super::get_plugins_dir()?;
|
let plugins_dir = super::get_plugins_dir()?;
|
||||||
let mut plugins = Vec::new();
|
let mut plugins = Vec::new();
|
||||||
if plugins_dir.exists() {
|
if plugins_dir.exists() {
|
||||||
@ -228,7 +240,7 @@ fn get_uninstalled_plugins() -> ResultType<Vec<String>> {
|
|||||||
let plugin_dir = entry.path();
|
let plugin_dir = entry.path();
|
||||||
if plugin_dir.is_dir() {
|
if plugin_dir.is_dir() {
|
||||||
if let Some(id) = plugin_dir.file_name().and_then(|n| n.to_str()) {
|
if let Some(id) = plugin_dir.file_name().and_then(|n| n.to_str()) {
|
||||||
if super::config::ManagerConfig::is_uninstalled(id) {
|
if uninstalled_plugin_set.contains(id) {
|
||||||
plugins.push(id.to_string());
|
plugins.push(id.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,19 +255,24 @@ fn get_uninstalled_plugins() -> ResultType<Vec<String>> {
|
|||||||
Ok(plugins)
|
Ok(plugins)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn remove_plugins() -> ResultType<()> {
|
pub fn remove_uninstalled() -> ResultType<()> {
|
||||||
for id in get_uninstalled_plugins()?.iter() {
|
let mut uninstalled_plugin_set = get_uninstall_id_set()?;
|
||||||
|
for id in get_uninstalled_plugins(&uninstalled_plugin_set)?.iter() {
|
||||||
super::config::remove(id as _);
|
super::config::remove(id as _);
|
||||||
if let Ok(dir) = super::get_plugin_dir(id as _) {
|
if let Ok(dir) = super::get_plugin_dir(id as _) {
|
||||||
allow_err!(fs::remove_dir_all(dir));
|
allow_err!(remove_dir_all(dir.clone()));
|
||||||
|
if !dir.exists() {
|
||||||
|
uninstalled_plugin_set.remove(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
allow_err!(update_uninstall_id_set(uninstalled_plugin_set));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uninstall_plugin(id: &str, called_by_ui: bool) {
|
pub fn uninstall_plugin(id: &str, called_by_ui: bool) {
|
||||||
if called_by_ui {
|
if called_by_ui {
|
||||||
match crate::platform::check_super_user_permission() {
|
match elevate_uninstall(id) {
|
||||||
Ok(true) => {
|
Ok(true) => {
|
||||||
if let Err(e) = super::ipc::uninstall_plugin(id) {
|
if let Err(e) = super::ipc::uninstall_plugin(id) {
|
||||||
log::error!("Failed to uninstall plugin '{}': {}", id, e);
|
log::error!("Failed to uninstall plugin '{}': {}", id, e);
|
||||||
@ -284,8 +301,6 @@ pub fn uninstall_plugin(id: &str, called_by_ui: bool) {
|
|||||||
|
|
||||||
if is_server() {
|
if is_server() {
|
||||||
super::plugins::unload_plugin(&id);
|
super::plugins::unload_plugin(&id);
|
||||||
// allow_err is Ok here.
|
|
||||||
allow_err!(super::config::ManagerConfig::set_uninstall(&id, true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,6 +394,28 @@ pub async fn start_ipc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_uninstall_id_set() -> ResultType<HashSet<String>> {
|
||||||
|
let uninstall_file_path = super::get_uninstall_file_path()?;
|
||||||
|
if !uninstall_file_path.exists() {
|
||||||
|
std::fs::create_dir_all(&super::get_plugins_dir()?)?;
|
||||||
|
return Ok(HashSet::new());
|
||||||
|
}
|
||||||
|
let s = read_to_string(uninstall_file_path)?;
|
||||||
|
Ok(serde_json::from_str::<HashSet<String>>(&s)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_uninstall_id_set(set: HashSet<String>) -> ResultType<()> {
|
||||||
|
let content = serde_json::to_string(&set)?;
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.create(true)
|
||||||
|
.open(super::get_uninstall_file_path()?)?;
|
||||||
|
let mut writer = std::io::BufWriter::new(file);
|
||||||
|
writer.write_all(content.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// install process
|
// install process
|
||||||
pub(super) mod install {
|
pub(super) mod install {
|
||||||
use super::IPC_PLUGIN_POSTFIX;
|
use super::IPC_PLUGIN_POSTFIX;
|
||||||
@ -463,7 +500,27 @@ pub(super) mod install {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn change_uninstall_plugin(id: &str, add: bool) {
|
||||||
|
match super::get_uninstall_id_set() {
|
||||||
|
Ok(mut set) => {
|
||||||
|
if add {
|
||||||
|
set.insert(id.to_string());
|
||||||
|
} else {
|
||||||
|
set.remove(id);
|
||||||
|
}
|
||||||
|
if let Err(e) = super::update_uninstall_id_set(set) {
|
||||||
|
log::error!("Failed to write uninstall list, {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => log::error!(
|
||||||
|
"Failed to get plugins dir, unable to read uninstall list, {}",
|
||||||
|
e
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn install_plugin_with_url(id: &str, url: &str) {
|
pub fn install_plugin_with_url(id: &str, url: &str) {
|
||||||
|
log::info!("Installing plugin '{}', url: {}", id, url);
|
||||||
let plugin_dir = match super::super::get_plugin_dir(id) {
|
let plugin_dir = match super::super::get_plugin_dir(id) {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -480,12 +537,14 @@ pub(super) mod install {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = plugin_dir.join(format!("{}.zip", id));
|
let filename = match url.rsplit('/').next() {
|
||||||
|
Some(filename) => plugin_dir.join(filename),
|
||||||
// download
|
None => {
|
||||||
if !download_file(id, url, &filename) {
|
send_install_status(id, InstallStatus::FailedDownloading);
|
||||||
return;
|
log::error!("Failed to download plugin file, invalid url: {}", url);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let filename_to_remove = filename.clone();
|
let filename_to_remove = filename.clone();
|
||||||
let _call_on_ret = crate::common::SimpleCallOnReturn {
|
let _call_on_ret = crate::common::SimpleCallOnReturn {
|
||||||
@ -497,6 +556,11 @@ pub(super) mod install {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// download
|
||||||
|
if !download_file(id, url, &filename) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// install
|
// install
|
||||||
send_install_status(id, InstallStatus::Installing);
|
send_install_status(id, InstallStatus::Installing);
|
||||||
if let Err(e) = do_install_file(&filename, &plugin_dir) {
|
if let Err(e) = do_install_file(&filename, &plugin_dir) {
|
||||||
|
@ -20,7 +20,8 @@ mod plog;
|
|||||||
mod plugins;
|
mod plugins;
|
||||||
|
|
||||||
pub use manager::{
|
pub use manager::{
|
||||||
install::install_plugin_with_url, install_plugin, load_plugin_list, uninstall_plugin,
|
install::{change_uninstall_plugin, install_plugin_with_url},
|
||||||
|
install_plugin, load_plugin_list, remove_uninstalled, uninstall_plugin,
|
||||||
};
|
};
|
||||||
pub use plugins::{
|
pub use plugins::{
|
||||||
handle_client_event, handle_listen_event, handle_server_event, handle_ui_event, load_plugin,
|
handle_client_event, handle_listen_event, handle_server_event, handle_ui_event, load_plugin,
|
||||||
@ -92,12 +93,19 @@ pub fn init() {
|
|||||||
if !is_server() {
|
if !is_server() {
|
||||||
std::thread::spawn(move || manager::start_ipc());
|
std::thread::spawn(move || manager::start_ipc());
|
||||||
} else {
|
} else {
|
||||||
if let Err(e) = manager::remove_plugins() {
|
if let Err(e) = remove_uninstalled() {
|
||||||
log::error!("Failed to remove plugins: {}", e);
|
log::error!("Failed to remove plugins: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(e) = plugins::load_plugins() {
|
match manager::get_uninstall_id_set() {
|
||||||
log::error!("Failed to load plugins: {}", e);
|
Ok(ids) => {
|
||||||
|
if let Err(e) = plugins::load_plugins(&ids) {
|
||||||
|
log::error!("Failed to load plugins: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to load plugins: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +139,11 @@ fn get_plugin_dir(id: &str) -> ResultType<PathBuf> {
|
|||||||
Ok(get_plugins_dir()?.join(id))
|
Ok(get_plugins_dir()?.join(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_uninstall_file_path() -> ResultType<PathBuf> {
|
||||||
|
Ok(get_plugins_dir()?.join("uninstall_list"))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn cstr_to_string(cstr: *const c_char) -> ResultType<String> {
|
fn cstr_to_string(cstr: *const c_char) -> ResultType<String> {
|
||||||
Ok(String::from_utf8(unsafe {
|
Ok(String::from_utf8(unsafe {
|
||||||
|
@ -8,9 +8,8 @@ use std::{
|
|||||||
use flutter_rust_bridge::StreamSink;
|
use flutter_rust_bridge::StreamSink;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
define_method_prefix,
|
define_method_prefix, flutter::FlutterHandler, flutter_ffi::EventToUI,
|
||||||
flutter::{FlutterHandler},
|
plugin::MSG_TO_UI_TYPE_PLUGIN_EVENT, ui_session_interface::Session,
|
||||||
ui_session_interface::Session, plugin::MSG_TO_UI_TYPE_PLUGIN_EVENT, flutter_ffi::EventToUI,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const MSG_TO_UI_TYPE_SESSION_CREATED: &str = "session_created";
|
const MSG_TO_UI_TYPE_SESSION_CREATED: &str = "session_created";
|
||||||
@ -18,12 +17,12 @@ const MSG_TO_UI_TYPE_SESSION_CREATED: &str = "session_created";
|
|||||||
use super::PluginNativeHandler;
|
use super::PluginNativeHandler;
|
||||||
|
|
||||||
pub type OnSessionRgbaCallback = unsafe extern "C" fn(
|
pub type OnSessionRgbaCallback = unsafe extern "C" fn(
|
||||||
*const c_char, // Session ID
|
*const c_char, // Session ID
|
||||||
*mut c_void, // raw data
|
*mut c_void, // raw data
|
||||||
*mut usize, // width
|
*mut usize, // width
|
||||||
*mut usize, // height,
|
*mut usize, // height,
|
||||||
*mut usize, // stride,
|
*mut usize, // stride,
|
||||||
*mut scrap::ImageFormat, // ImageFormat
|
*mut scrap::ImageFormat, // ImageFormat
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -124,7 +123,7 @@ impl PluginNativeHandler for PluginNativeSessionHandler {
|
|||||||
impl PluginNativeSessionHandler {
|
impl PluginNativeSessionHandler {
|
||||||
fn create_session(&self, session_id: String) -> String {
|
fn create_session(&self, session_id: String) -> String {
|
||||||
let session =
|
let session =
|
||||||
crate::flutter::session_add(&session_id, false, false, "", false, "".to_owned());
|
crate::flutter::session_add(&session_id, false, false, false, "", false, "".to_owned());
|
||||||
if let Ok(session) = session {
|
if let Ok(session) = session {
|
||||||
let mut sessions = self.sessions.write().unwrap();
|
let mut sessions = self.sessions.write().unwrap();
|
||||||
sessions.push(session);
|
sessions.push(session);
|
||||||
@ -132,7 +131,10 @@ impl PluginNativeSessionHandler {
|
|||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
m.insert("name", MSG_TO_UI_TYPE_SESSION_CREATED);
|
m.insert("name", MSG_TO_UI_TYPE_SESSION_CREATED);
|
||||||
m.insert("session_id", &session_id);
|
m.insert("session_id", &session_id);
|
||||||
crate::flutter::push_global_event(crate::flutter::APP_TYPE_DESKTOP_REMOTE, serde_json::to_string(&m).unwrap_or("".to_string()));
|
crate::flutter::push_global_event(
|
||||||
|
crate::flutter::APP_TYPE_DESKTOP_REMOTE,
|
||||||
|
serde_json::to_string(&m).unwrap_or("".to_string()),
|
||||||
|
);
|
||||||
return session_id;
|
return session_id;
|
||||||
} else {
|
} else {
|
||||||
return "".to_string();
|
return "".to_string();
|
||||||
@ -200,7 +202,7 @@ impl PluginNativeSessionHandler {
|
|||||||
if session.id == session_id {
|
if session.id == session_id {
|
||||||
*session.event_stream.write().unwrap() = Some(stream);
|
*session.event_stream.write().unwrap() = Some(stream);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use hbb_common::{
|
|||||||
};
|
};
|
||||||
use serde_derive::Serialize;
|
use serde_derive::Serialize;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{HashMap, HashSet},
|
||||||
ffi::{c_char, c_void},
|
ffi::{c_char, c_void},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
@ -263,7 +263,7 @@ const DYLIB_SUFFIX: &str = ".so";
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
const DYLIB_SUFFIX: &str = ".dylib";
|
const DYLIB_SUFFIX: &str = ".dylib";
|
||||||
|
|
||||||
pub(super) fn load_plugins() -> ResultType<()> {
|
pub(super) fn load_plugins(uninstalled_ids: &HashSet<String>) -> ResultType<()> {
|
||||||
let plugins_dir = super::get_plugins_dir()?;
|
let plugins_dir = super::get_plugins_dir()?;
|
||||||
if !plugins_dir.exists() {
|
if !plugins_dir.exists() {
|
||||||
std::fs::create_dir_all(&plugins_dir)?;
|
std::fs::create_dir_all(&plugins_dir)?;
|
||||||
@ -273,7 +273,16 @@ pub(super) fn load_plugins() -> ResultType<()> {
|
|||||||
Ok(entry) => {
|
Ok(entry) => {
|
||||||
let plugin_dir = entry.path();
|
let plugin_dir = entry.path();
|
||||||
if plugin_dir.is_dir() {
|
if plugin_dir.is_dir() {
|
||||||
load_plugin_dir(&plugin_dir);
|
if let Some(plugin_id) = plugin_dir.file_name().and_then(|f| f.to_str()) {
|
||||||
|
if uninstalled_ids.contains(plugin_id) {
|
||||||
|
log::debug!(
|
||||||
|
"Ignore loading '{}' as it should be uninstalled",
|
||||||
|
plugin_id
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
load_plugin_dir(&plugin_dir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -345,6 +354,14 @@ fn load_plugin_path(path: &str) -> ResultType<()> {
|
|||||||
// to-do validate plugin
|
// to-do validate plugin
|
||||||
// to-do check the plugin id (make sure it does not use another plugin's id)
|
// to-do check the plugin id (make sure it does not use another plugin's id)
|
||||||
|
|
||||||
|
let id = desc.meta().id.clone();
|
||||||
|
let plugin_info = PluginInfo {
|
||||||
|
path: path.to_string(),
|
||||||
|
uninstalled: false,
|
||||||
|
desc: desc.clone(),
|
||||||
|
};
|
||||||
|
PLUGIN_INFO.write().unwrap().insert(id.clone(), plugin_info);
|
||||||
|
|
||||||
let init_info = serde_json::to_string(&InitInfo {
|
let init_info = serde_json::to_string(&InitInfo {
|
||||||
is_server: crate::common::is_server(),
|
is_server: crate::common::is_server(),
|
||||||
})?;
|
})?;
|
||||||
@ -359,7 +376,10 @@ fn load_plugin_path(path: &str) -> ResultType<()> {
|
|||||||
native: super::native::cb_native_data,
|
native: super::native::cb_native_data,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
plugin.init(&init_data, path)?;
|
// If do not load the plugin when init failed, the ui will not show the installed plugin.
|
||||||
|
if let Err(e) = plugin.init(&init_data, path) {
|
||||||
|
log::error!("Failed to init plugin '{}', {}", desc.meta().id, e);
|
||||||
|
}
|
||||||
|
|
||||||
if is_server() {
|
if is_server() {
|
||||||
super::config::ManagerConfig::add_plugin(&desc.meta().id)?;
|
super::config::ManagerConfig::add_plugin(&desc.meta().id)?;
|
||||||
@ -370,13 +390,6 @@ fn load_plugin_path(path: &str) -> ResultType<()> {
|
|||||||
reload_ui(&desc, None);
|
reload_ui(&desc, None);
|
||||||
|
|
||||||
// add plugins
|
// add plugins
|
||||||
let id = desc.meta().id.clone();
|
|
||||||
let plugin_info = PluginInfo {
|
|
||||||
path: path.to_string(),
|
|
||||||
uninstalled: false,
|
|
||||||
desc,
|
|
||||||
};
|
|
||||||
PLUGIN_INFO.write().unwrap().insert(id.clone(), plugin_info);
|
|
||||||
PLUGINS.write().unwrap().insert(id.clone(), plugin);
|
PLUGINS.write().unwrap().insert(id.clone(), plugin);
|
||||||
|
|
||||||
log::info!("Plugin {} loaded", id);
|
log::info!("Plugin {} loaded", id);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user