Compare commits
2 commits
ed3b837c79
...
a027922b0e
Author | SHA1 | Date | |
---|---|---|---|
a027922b0e | |||
bc9b61aac0 |
9 changed files with 273 additions and 183 deletions
|
@ -25,7 +25,7 @@ template $AudreyUiSetup: Adw.PreferencesDialog {
|
||||||
sensitive: bind template.authn_can_edit;
|
sensitive: bind template.authn_can_edit;
|
||||||
text: bind template.server_url bidirectional;
|
text: bind template.server_url bidirectional;
|
||||||
|
|
||||||
changed => $on_authn_changed ();
|
changed => $on_authn_changed () swapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.EntryRow {
|
Adw.EntryRow {
|
||||||
|
@ -33,7 +33,7 @@ template $AudreyUiSetup: Adw.PreferencesDialog {
|
||||||
sensitive: bind template.authn_can_edit;
|
sensitive: bind template.authn_can_edit;
|
||||||
text: bind template.username bidirectional;
|
text: bind template.username bidirectional;
|
||||||
|
|
||||||
changed => $on_authn_changed ();
|
changed => $on_authn_changed () swapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.PasswordEntryRow {
|
Adw.PasswordEntryRow {
|
||||||
|
@ -41,7 +41,7 @@ template $AudreyUiSetup: Adw.PreferencesDialog {
|
||||||
sensitive: bind template.authn_can_edit;
|
sensitive: bind template.authn_can_edit;
|
||||||
text: bind template.password bidirectional;
|
text: bind template.password bidirectional;
|
||||||
|
|
||||||
changed => $on_authn_changed ();
|
changed => $on_authn_changed () swapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.ActionRow {
|
Adw.ActionRow {
|
||||||
|
@ -57,7 +57,7 @@ template $AudreyUiSetup: Adw.PreferencesDialog {
|
||||||
title: _("Connect and save");
|
title: _("Connect and save");
|
||||||
sensitive: bind template.authn_can_validate;
|
sensitive: bind template.authn_can_validate;
|
||||||
|
|
||||||
activated => $on_authn_validate_activated ();
|
activated => $on_authn_validate_activated () swapped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -44,24 +44,5 @@ fn main() -> glib::ExitCode {
|
||||||
setlocale(LocaleCategory::LcNumeric, "C.UTF-8");
|
setlocale(LocaleCategory::LcNumeric, "C.UTF-8");
|
||||||
let app = Application::new();
|
let app = Application::new();
|
||||||
|
|
||||||
// smol test for the subsonic client
|
|
||||||
glib::spawn_future_local(async {
|
|
||||||
let keyring = oo7::Keyring::new().await.unwrap();
|
|
||||||
let attributes = vec![("xdg:schema", APP_ID)];
|
|
||||||
let items = keyring.search_items(&attributes).await.unwrap();
|
|
||||||
if !items.is_empty() {
|
|
||||||
let item = &items[0];
|
|
||||||
let attributes = item.attributes().await.unwrap();
|
|
||||||
let client = subsonic::Client::with_password(
|
|
||||||
&attributes["server-url"],
|
|
||||||
&attributes["username"],
|
|
||||||
&item.secret().await.unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
client.ping().await.unwrap();
|
|
||||||
println!("{:#?}", client.get_random_songs(10).await.unwrap());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.run()
|
app.run()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ audrey_sources = [
|
||||||
'playbin.vala',
|
'playbin.vala',
|
||||||
'rust.vapi',
|
'rust.vapi',
|
||||||
'subsonic.vala',
|
'subsonic.vala',
|
||||||
'ui/setup.vala',
|
|
||||||
'ui/window.vala',
|
'ui/window.vala',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -24,3 +24,8 @@ typedef void AudreyUiPlayQueue;
|
||||||
typedef void AudreyMpris;
|
typedef void AudreyMpris;
|
||||||
AudreyMpris *audrey_mpris_new(void *window);
|
AudreyMpris *audrey_mpris_new(void *window);
|
||||||
guint audrey_mpris_register_object(AudreyMpris *self, GDBusConnection *conn, const gchar *object_path, GError **error);
|
guint audrey_mpris_register_object(AudreyMpris *self, GDBusConnection *conn, const gchar *object_path, GError **error);
|
||||||
|
|
||||||
|
// ui::Setup
|
||||||
|
typedef void AudreyUiSetup;
|
||||||
|
AudreyUiSetup *audrey_ui_setup_new();
|
||||||
|
void audrey_ui_setup_load(AudreyUiSetup *self);
|
||||||
|
|
|
@ -28,4 +28,19 @@ namespace Audrey {
|
||||||
public bool can_clear_all { get; }
|
public bool can_clear_all { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class Ui.Setup : Adw.PreferencesDialog {
|
||||||
|
public string status { get; }
|
||||||
|
public bool authn_can_edit { get; }
|
||||||
|
public bool authn_can_validate { get; }
|
||||||
|
public string server_url { get; set; }
|
||||||
|
public string username { get; set; }
|
||||||
|
public string password { get; set; }
|
||||||
|
|
||||||
|
public signal void connected (Subsonic.Client api);
|
||||||
|
|
||||||
|
public Setup ();
|
||||||
|
public void load ();
|
||||||
|
public void save ();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,28 @@ pub enum Error {
|
||||||
OtherError(&'static str),
|
OtherError(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::UrlParseError(err) => fmt::Display::fmt(err, f),
|
||||||
|
Self::ReqwestError(err) => fmt::Display::fmt(err, f),
|
||||||
|
Self::SubsonicError(err) => fmt::Display::fmt(&err.message, f),
|
||||||
|
Self::OtherError(err) => fmt::Display::fmt(err, f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
Self::UrlParseError(err) => err.source(),
|
||||||
|
Self::ReqwestError(err) => err.source(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<reqwest::Error> for Error {
|
impl From<reqwest::Error> for Error {
|
||||||
fn from(err: reqwest::Error) -> Self {
|
fn from(err: reqwest::Error) -> Self {
|
||||||
// don't print secret salt/token combo
|
// don't print secret salt/token combo
|
||||||
|
@ -71,7 +93,7 @@ impl Client {
|
||||||
.map_err(Error::UrlParseError)?;
|
.map_err(Error::UrlParseError)?;
|
||||||
|
|
||||||
if base_url.scheme() != "http" && base_url.scheme() != "https" {
|
if base_url.scheme() != "http" && base_url.scheme() != "https" {
|
||||||
return Err(Error::OtherError("Url scheme is not HTTP(s)"));
|
return Err(Error::OtherError("url scheme is not HTTP(s)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mod ffi {
|
mod ffi {
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
|
use std::ffi::c_char;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct AudreySubsonicClient {
|
pub struct AudreySubsonicClient {
|
||||||
|
@ -13,9 +14,16 @@ mod ffi {
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn audrey_subsonic_client_get_type() -> glib::ffi::GType;
|
pub fn audrey_subsonic_client_get_type() -> glib::ffi::GType;
|
||||||
|
pub fn audrey_subsonic_client_new_with_token(
|
||||||
|
server_url: *mut c_char,
|
||||||
|
username: *mut c_char,
|
||||||
|
token: *mut c_char,
|
||||||
|
salt: *mut c_char,
|
||||||
|
) -> *mut AudreySubsonicClient;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use glib::translate::{from_glib_none, ToGlibPtr};
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
|
@ -25,3 +33,16 @@ glib::wrapper! {
|
||||||
type_ => || ffi::audrey_subsonic_client_get_type(),
|
type_ => || ffi::audrey_subsonic_client_get_type(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn with_token(server_url: &str, username: &str, token: &str, salt: &str) -> Self {
|
||||||
|
unsafe {
|
||||||
|
from_glib_none(ffi::audrey_subsonic_client_new_with_token(
|
||||||
|
server_url.to_glib_none().0,
|
||||||
|
username.to_glib_none().0,
|
||||||
|
token.to_glib_none().0,
|
||||||
|
salt.to_glib_none().0,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
218
src/ui/setup.rs
218
src/ui/setup.rs
|
@ -1,29 +1,221 @@
|
||||||
mod ffi {
|
mod imp {
|
||||||
use gtk::glib;
|
use adw::{glib, prelude::*, subclass::prelude::*};
|
||||||
|
use glib::subclass::{InitializingObject, Signal};
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
#[repr(C)]
|
#[derive(gtk::CompositeTemplate, glib::Properties, Default)]
|
||||||
pub struct AudreyUiSetup {
|
#[template(resource = "/eu/callcc/audrey/setup.ui")]
|
||||||
parent_instance: adw::ffi::AdwPreferencesDialog,
|
#[properties(wrapper_type = super::Setup)]
|
||||||
|
pub struct Setup {
|
||||||
|
#[property(get, set)]
|
||||||
|
status: RefCell<String>,
|
||||||
|
|
||||||
|
#[property(get, set)]
|
||||||
|
authn_can_edit: Cell<bool>,
|
||||||
|
#[property(get, set)]
|
||||||
|
authn_can_validate: Cell<bool>,
|
||||||
|
|
||||||
|
#[property(get, set)]
|
||||||
|
server_url: RefCell<String>,
|
||||||
|
#[property(get, set)]
|
||||||
|
username: RefCell<String>,
|
||||||
|
#[property(get, set)]
|
||||||
|
password: RefCell<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[glib::object_subclass]
|
||||||
pub struct AudreyUiSetupClass {
|
impl ObjectSubclass for Setup {
|
||||||
parent_class: adw::ffi::AdwPreferencesDialogClass,
|
const NAME: &'static str = "AudreyUiSetup";
|
||||||
|
type Type = super::Setup;
|
||||||
|
type ParentType = adw::PreferencesDialog;
|
||||||
|
|
||||||
|
fn class_init(klass: &mut Self::Class) {
|
||||||
|
klass.bind_template();
|
||||||
|
klass.bind_template_callbacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
fn instance_init(obj: &InitializingObject<Self>) {
|
||||||
pub fn audrey_ui_setup_get_type() -> glib::ffi::GType;
|
obj.init_template();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[glib::derived_properties]
|
||||||
|
impl ObjectImpl for Setup {
|
||||||
|
fn signals() -> &'static [Signal] {
|
||||||
|
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
|
||||||
|
SIGNALS.get_or_init(|| {
|
||||||
|
vec![Signal::builder("connected")
|
||||||
|
.param_types([crate::subsonic_vala::Client::static_type()])
|
||||||
|
.build()]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetImpl for Setup {}
|
||||||
|
|
||||||
|
impl AdwDialogImpl for Setup {}
|
||||||
|
|
||||||
|
impl PreferencesDialogImpl for Setup {}
|
||||||
|
|
||||||
|
#[gtk::template_callbacks]
|
||||||
|
impl Setup {
|
||||||
|
#[template_callback]
|
||||||
|
fn on_authn_changed(&self) {
|
||||||
|
self.obj().set_authn_can_validate(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
pub(super) async fn on_authn_validate_activated(&self) {
|
||||||
|
self.obj().set_authn_can_validate(false);
|
||||||
|
self.obj().set_authn_can_edit(false);
|
||||||
|
self.obj().set_status("Connecting...");
|
||||||
|
|
||||||
|
let api = match crate::subsonic::Client::with_password(
|
||||||
|
&self.obj().server_url(),
|
||||||
|
&self.obj().username(),
|
||||||
|
self.obj().password().as_bytes(),
|
||||||
|
) {
|
||||||
|
Ok(api) => api,
|
||||||
|
Err(err) => {
|
||||||
|
self.obj().set_status(format!("Error: {err}"));
|
||||||
|
self.obj().set_authn_can_validate(true);
|
||||||
|
self.obj().set_authn_can_edit(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match api.ping().await {
|
||||||
|
Ok(()) => {
|
||||||
|
self.obj().set_status("Connected");
|
||||||
|
self.save().await;
|
||||||
|
|
||||||
|
// please REMOVEME once we've killed vala
|
||||||
|
fn get_random_salt(length: usize) -> String {
|
||||||
|
use rand::Rng;
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
std::iter::repeat(())
|
||||||
|
// 0.9: s/distributions/distr
|
||||||
|
.map(|()| rng.sample(rand::distributions::Alphanumeric))
|
||||||
|
.map(char::from)
|
||||||
|
.take(length)
|
||||||
|
.collect::<String>()
|
||||||
|
}
|
||||||
|
let new_salt = get_random_salt(8);
|
||||||
|
use md5::Digest;
|
||||||
|
let mut hasher = md5::Md5::new();
|
||||||
|
hasher.update(self.obj().password().as_bytes());
|
||||||
|
hasher.update(new_salt.as_bytes());
|
||||||
|
let new_token_bytes = hasher.finalize();
|
||||||
|
let new_token = base16ct::lower::encode_string(&new_token_bytes);
|
||||||
|
|
||||||
|
let vala_api = crate::subsonic_vala::Client::with_token(
|
||||||
|
&self.obj().server_url(),
|
||||||
|
&self.obj().username(),
|
||||||
|
&new_token,
|
||||||
|
&new_salt,
|
||||||
|
);
|
||||||
|
self.obj().emit_by_name::<()>("connected", &[&vala_api]);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
self.obj().set_status(format!("Error: {err}"));
|
||||||
|
self.obj().set_authn_can_validate(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.obj().set_authn_can_edit(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn save(&self) {
|
||||||
|
self.obj().set_authn_can_edit(false);
|
||||||
|
|
||||||
|
// TODO remove unwraps etc etc
|
||||||
|
let keyring = oo7::Keyring::new().await.unwrap();
|
||||||
|
let mut attributes = vec![("xdg:schema", crate::APP_ID.to_string())];
|
||||||
|
// clear previous passwords
|
||||||
|
keyring.delete(&attributes).await.unwrap();
|
||||||
|
|
||||||
|
attributes.push(("server-url", self.obj().server_url()));
|
||||||
|
attributes.push(("username", self.obj().username()));
|
||||||
|
keyring
|
||||||
|
.create_item(
|
||||||
|
"Audrey Subsonic password",
|
||||||
|
&attributes,
|
||||||
|
self.obj().password(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
self.obj().set_authn_can_edit(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use adw::subclass::prelude::*;
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct Setup(Object<ffi::AudreyUiSetup, ffi::AudreyUiSetupClass>)
|
pub struct Setup(ObjectSubclass<imp::Setup>)
|
||||||
@extends adw::PreferencesDialog, adw::Dialog, gtk::Widget,
|
@extends adw::PreferencesDialog, adw::Dialog, gtk::Widget,
|
||||||
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
|
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
|
||||||
|
}
|
||||||
|
|
||||||
match fn {
|
impl Setup {
|
||||||
type_ => || ffi::audrey_ui_setup_get_type(),
|
pub fn load(&self) {
|
||||||
|
glib::spawn_future_local(glib::clone!(
|
||||||
|
#[weak(rename_to = self_)]
|
||||||
|
self,
|
||||||
|
async move {
|
||||||
|
self_.set_authn_can_edit(false);
|
||||||
|
|
||||||
|
// TODO remove unwraps, make sure authn_can_edit is set back to true
|
||||||
|
let keyring = oo7::Keyring::new().await.unwrap();
|
||||||
|
let attributes = vec![("xdg:schema", crate::APP_ID)];
|
||||||
|
let items = keyring.search_items(&attributes).await.unwrap();
|
||||||
|
|
||||||
|
if items.is_empty() {
|
||||||
|
// didn't find shit, leave all empty
|
||||||
|
self_.set_server_url("");
|
||||||
|
self_.set_username("");
|
||||||
|
self_.set_password("");
|
||||||
|
// TODO: onboarding
|
||||||
|
self_.set_authn_can_edit(true);
|
||||||
|
self_.set_authn_can_validate(true);
|
||||||
|
} else {
|
||||||
|
let item = &items[0];
|
||||||
|
let attributes = item.attributes().await.unwrap();
|
||||||
|
|
||||||
|
self_.set_server_url(attributes["server-url"].clone());
|
||||||
|
self_.set_username(attributes["username"].clone());
|
||||||
|
// strip non-utf8 elements from the pw, i guess
|
||||||
|
self_.set_password(String::from_utf8_lossy(&item.secret().await.unwrap()));
|
||||||
|
|
||||||
|
// first connection
|
||||||
|
self_.set_authn_can_validate(true);
|
||||||
|
self_.imp().on_authn_validate_activated().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod ffi {
|
||||||
|
use glib::subclass::basic::InstanceStruct;
|
||||||
|
use glib::translate::{from_glib_none, IntoGlibPtr};
|
||||||
|
use glib::Object;
|
||||||
|
use gtk::glib;
|
||||||
|
|
||||||
|
type AudreyUiSetup = InstanceStruct<super::imp::Setup>;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn audrey_ui_setup_new() -> *mut AudreyUiSetup {
|
||||||
|
unsafe { Object::new::<super::Setup>().into_glib_ptr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn audrey_ui_setup_load(self_: *mut AudreyUiSetup) {
|
||||||
|
let self_: &super::Setup = unsafe { &from_glib_none(self_) };
|
||||||
|
self_.load()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,145 +0,0 @@
|
||||||
static void salt_password (string password, out string token, out string salt) {
|
|
||||||
const int SALT_BYTES = 8;
|
|
||||||
uchar salt_bytes[SALT_BYTES];
|
|
||||||
GCrypt.Random.randomize (salt_bytes, GCrypt.Random.Level.STRONG);
|
|
||||||
uchar salt_chars[2*SALT_BYTES+1];
|
|
||||||
for (int i = 0; i < SALT_BYTES; i += 1) {
|
|
||||||
salt_chars[2*i+0] = "0123456789abcdef"[(salt_bytes[i]>>4)&0xf];
|
|
||||||
salt_chars[2*i+1] = "0123456789abcdef"[(salt_bytes[i]>>0)&0xf];
|
|
||||||
}
|
|
||||||
salt_chars[2*SALT_BYTES] = 0;
|
|
||||||
var checksum = new GLib.Checksum (ChecksumType.MD5);
|
|
||||||
checksum.update ((uchar[]) password, -1);
|
|
||||||
checksum.update (salt_chars, -1);
|
|
||||||
|
|
||||||
token = checksum.get_string ();
|
|
||||||
salt = (string) salt_chars;
|
|
||||||
}
|
|
||||||
|
|
||||||
[GtkTemplate (ui = "/eu/callcc/audrey/setup.ui")]
|
|
||||||
public class Audrey.Ui.Setup : Adw.PreferencesDialog {
|
|
||||||
public string status { get; private set; default = _("Not connected"); }
|
|
||||||
|
|
||||||
public bool authn_can_edit { get; private set; default = true; }
|
|
||||||
public bool authn_can_validate { get; private set; default = false; }
|
|
||||||
|
|
||||||
public string server_url { get; set; default = ""; }
|
|
||||||
public string username { get; set; default = ""; }
|
|
||||||
public string password { get; set; default = ""; }
|
|
||||||
public string token;
|
|
||||||
public string salt;
|
|
||||||
|
|
||||||
public signal void connected (Subsonic.Client api);
|
|
||||||
|
|
||||||
private static Secret.Schema secret_schema = new Secret.Schema (
|
|
||||||
"eu.callcc.audrey",
|
|
||||||
Secret.SchemaFlags.NONE,
|
|
||||||
"server-url", Secret.SchemaAttributeType.STRING,
|
|
||||||
"username", Secret.SchemaAttributeType.STRING
|
|
||||||
);
|
|
||||||
|
|
||||||
[GtkCallback] private void on_authn_changed () {
|
|
||||||
this.authn_can_validate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[GtkCallback] private void on_authn_validate_activated () {
|
|
||||||
this.authn_can_validate = false;
|
|
||||||
this.authn_can_edit = false;
|
|
||||||
this.status = _("Connecting...");
|
|
||||||
|
|
||||||
string new_token, new_salt;
|
|
||||||
salt_password (this.password, out new_token, out new_salt);
|
|
||||||
var api = new Subsonic.Client.with_token (
|
|
||||||
this.server_url,
|
|
||||||
this.username,
|
|
||||||
new_token,
|
|
||||||
new_salt);
|
|
||||||
|
|
||||||
api.ping.begin ((obj, res) => {
|
|
||||||
try {
|
|
||||||
api.ping.end (res);
|
|
||||||
this.status = _("Connected");
|
|
||||||
this.token = new_token;
|
|
||||||
this.salt = new_salt;
|
|
||||||
this.save ();
|
|
||||||
|
|
||||||
this.connected (api);
|
|
||||||
} catch (Error e) {
|
|
||||||
this.status = @"$(_("Ping failed")): $(e.message)";
|
|
||||||
this.authn_can_validate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.authn_can_edit = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void load () {
|
|
||||||
this.authn_can_edit = false;
|
|
||||||
Secret.password_searchv.begin (
|
|
||||||
secret_schema,
|
|
||||||
new HashTable<string, string> (null, null),
|
|
||||||
Secret.SearchFlags.UNLOCK,
|
|
||||||
null,
|
|
||||||
(obj, res) => {
|
|
||||||
try {
|
|
||||||
var list = Secret.password_searchv.end (res);
|
|
||||||
if (list == null) {
|
|
||||||
// didn't find shit, leave all empty
|
|
||||||
this.server_url = "";
|
|
||||||
this.username = "";
|
|
||||||
this.password = "";
|
|
||||||
// TODO: onboarding
|
|
||||||
this.authn_can_edit = true;
|
|
||||||
this.authn_can_validate = true;
|
|
||||||
} else {
|
|
||||||
var first = list.data;
|
|
||||||
assert (first != null);
|
|
||||||
|
|
||||||
this.server_url = first.attributes["server-url"];
|
|
||||||
this.username = first.attributes["username"];
|
|
||||||
|
|
||||||
first.retrieve_secret.begin (null, (obj, res) => {
|
|
||||||
try {
|
|
||||||
var value = first.retrieve_secret.end (res);
|
|
||||||
this.password = value.get_text () ?? "";
|
|
||||||
} catch (Error e) {
|
|
||||||
error ("could not retrieve password from credentials: %s", e.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// first connection
|
|
||||||
this.authn_can_validate = true;
|
|
||||||
this.on_authn_validate_activated ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (Error e) {
|
|
||||||
error ("could not search for password in keyring: %s", e.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void save_internal () {
|
|
||||||
this.authn_can_edit = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
yield Secret.password_clear (secret_schema, null);
|
|
||||||
} catch (Error e) {
|
|
||||||
error ("could not clear previous passwords from keyring: %s", e.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
yield Secret.password_store (secret_schema, null, "Audrey Subsonic password", this.password, null, "server-url", this.server_url, "username", this.username);
|
|
||||||
} catch (Error e) {
|
|
||||||
error ("could not store password in keyring: %s", e.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.authn_can_edit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save () {
|
|
||||||
this.save_internal.begin ();
|
|
||||||
}
|
|
||||||
|
|
||||||
~Setup () {
|
|
||||||
debug ("destroying setup dialog");
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue