Implement network drive connection, part of #202

This commit is contained in:
Jeremy Soller 2024-09-12 15:54:54 -06:00
parent f41730978c
commit 0d8fd00dd3
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
8 changed files with 542 additions and 86 deletions

View file

@ -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