Implement network drive connection, part of #202
This commit is contained in:
parent
f41730978c
commit
0d8fd00dd3
8 changed files with 542 additions and 86 deletions
|
|
@ -6,7 +6,7 @@ use gio::{glib, prelude::*};
|
|||
use std::{any::TypeId, future::pending, path::PathBuf, sync::Arc};
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
|
||||
use super::{Mounter, MounterItem, MounterItems};
|
||||
use super::{Mounter, MounterAuth, MounterItem, MounterItems, MounterMessage};
|
||||
|
||||
fn gio_icon_to_path(icon: &gio::Icon, size: u16) -> Option<PathBuf> {
|
||||
if let Some(themed_icon) = icon.downcast_ref::<gio::ThemedIcon>() {
|
||||
|
|
@ -24,12 +24,15 @@ fn gio_icon_to_path(icon: &gio::Icon, size: u16) -> Option<PathBuf> {
|
|||
enum Cmd {
|
||||
Rescan,
|
||||
Mount(MounterItem),
|
||||
NetworkDrive(String),
|
||||
Unmount(MounterItem),
|
||||
}
|
||||
|
||||
enum Event {
|
||||
Changed,
|
||||
Items(MounterItems),
|
||||
NetworkAuth(String, MounterAuth, mpsc::Sender<MounterAuth>),
|
||||
NetworkResult(String, Result<bool, String>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -190,6 +193,80 @@ impl Gvfs {
|
|||
);
|
||||
}
|
||||
}
|
||||
Cmd::NetworkDrive(uri) => {
|
||||
let mount_op = gio::MountOperation::new();
|
||||
|
||||
{
|
||||
let event_tx = event_tx.clone();
|
||||
let uri = uri.clone();
|
||||
mount_op.connect_ask_password(move |mount_op, message, default_user, default_domain, flags| {
|
||||
let auth = MounterAuth {
|
||||
message: message.to_string(),
|
||||
username_opt: if flags.contains(gio::AskPasswordFlags::NEED_USERNAME) {
|
||||
Some(default_user.to_string())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
domain_opt: if flags.contains(gio::AskPasswordFlags::NEED_DOMAIN) {
|
||||
Some(default_domain.to_string())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
password_opt: if flags.contains(gio::AskPasswordFlags::NEED_PASSWORD) {
|
||||
Some(String::new())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
remember_opt: if flags.contains(gio::AskPasswordFlags::SAVING_SUPPORTED) {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
anonymous_opt: if flags.contains(gio::AskPasswordFlags::ANONYMOUS_SUPPORTED) {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let (auth_tx, mut auth_rx) = mpsc::channel(1);
|
||||
event_tx.send(Event::NetworkAuth(uri.clone(), auth, auth_tx)).unwrap();
|
||||
//TODO: async recv?
|
||||
if let Some(auth) = auth_rx.blocking_recv() {
|
||||
if auth.anonymous_opt == Some(true) {
|
||||
mount_op.set_anonymous(true);
|
||||
} else {
|
||||
mount_op.set_username(auth.username_opt.as_deref());
|
||||
mount_op.set_domain(auth.domain_opt.as_deref());
|
||||
mount_op.set_password(auth.password_opt.as_deref());
|
||||
if auth.remember_opt == Some(true) {
|
||||
mount_op.set_password_save(gio::PasswordSave::Permanently);
|
||||
}
|
||||
}
|
||||
mount_op.reply(gio::MountOperationResult::Handled);
|
||||
} else {
|
||||
mount_op.reply(gio::MountOperationResult::Aborted);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let file = gio::File::for_uri(&uri);
|
||||
let event_tx = event_tx.clone();
|
||||
file.mount_enclosing_volume(
|
||||
gio::MountMountFlags::empty(),
|
||||
Some(&mount_op),
|
||||
gio::Cancellable::NONE,
|
||||
move |res| {
|
||||
log::info!("network drive {}: result {:?}", uri, res);
|
||||
event_tx.send(Event::NetworkResult(uri, match res {
|
||||
Ok(()) => Ok(true),
|
||||
Err(err) => match err.kind::<gio::IOErrorEnum>() {
|
||||
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
||||
_ => Err(format!("{}", err))
|
||||
}
|
||||
})).unwrap();
|
||||
}
|
||||
);
|
||||
}
|
||||
Cmd::Unmount(mounter_item) => {
|
||||
let MounterItem::Gvfs(item) = mounter_item else { continue };
|
||||
let ItemKind::Mount = item.kind else { continue };
|
||||
|
|
@ -242,6 +319,17 @@ impl Mounter for Gvfs {
|
|||
)
|
||||
}
|
||||
|
||||
fn network_drive(&self, uri: String) -> Command<()> {
|
||||
let command_tx = self.command_tx.clone();
|
||||
Command::perform(
|
||||
async move {
|
||||
command_tx.send(Cmd::NetworkDrive(uri)).unwrap();
|
||||
()
|
||||
},
|
||||
|x| x,
|
||||
)
|
||||
}
|
||||
|
||||
fn unmount(&self, item: MounterItem) -> Command<()> {
|
||||
let command_tx = self.command_tx.clone();
|
||||
Command::perform(
|
||||
|
|
@ -253,17 +341,23 @@ impl Mounter for Gvfs {
|
|||
)
|
||||
}
|
||||
|
||||
fn subscription(&self) -> subscription::Subscription<MounterItems> {
|
||||
fn subscription(&self) -> subscription::Subscription<MounterMessage> {
|
||||
let command_tx = self.command_tx.clone();
|
||||
let event_rx = self.event_rx.clone();
|
||||
subscription::channel(TypeId::of::<Self>(), 1, |mut output| async move {
|
||||
command_tx.send(Cmd::Rescan).unwrap();
|
||||
while let Some(event) = event_rx.lock().await.recv().await {
|
||||
match event {
|
||||
Event::Changed => {
|
||||
command_tx.send(Cmd::Rescan).unwrap();
|
||||
}
|
||||
Event::Items(items) => output.send(items).await.unwrap(),
|
||||
Event::Changed => command_tx.send(Cmd::Rescan).unwrap(),
|
||||
Event::Items(items) => output.send(MounterMessage::Items(items)).await.unwrap(),
|
||||
Event::NetworkAuth(uri, auth, auth_tx) => output
|
||||
.send(MounterMessage::NetworkAuth(uri, auth, auth_tx))
|
||||
.await
|
||||
.unwrap(),
|
||||
Event::NetworkResult(uri, res) => output
|
||||
.send(MounterMessage::NetworkResult(uri, res))
|
||||
.await
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
pending().await
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue