diff --git a/Cargo.toml b/Cargo.toml
index 5414ca8..e84df8a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,10 @@ gtk = { version = "0.9.2", features = ["v3_24"] }
gtk-macros = "0.2.0"
gstreamer = "0.16.4"
gstreamer-player = "0.16.3"
+isahc = "0.9.12"
libhandy = "0.7.0"
pango = "0.9.1"
rand = "0.7.3"
+secret-service = "1.1.1"
+serde = { version = "1.0.117", features = ["derive"] }
+serde_json = "1.0.59"
diff --git a/data/de.johrpan.musicus.gschema.xml b/data/de.johrpan.musicus.gschema.xml
index 2609270..db93385 100644
--- a/data/de.johrpan.musicus.gschema.xml
+++ b/data/de.johrpan.musicus.gschema.xml
@@ -5,5 +5,9 @@
""
Path to the music library folder
+
+ "https://musicus.johrpan.de"
+ URL of the Musicus server to use
+
diff --git a/meson.build b/meson.build
index 0c4ba05..01999e8 100644
--- a/meson.build
+++ b/meson.build
@@ -4,10 +4,12 @@ project('musicus', 'rust',
license: 'AGPLv3+',
)
+dependency('dbus-1', version: '>= 1.3')
dependency('glib-2.0', version: '>= 2.56')
dependency('gio-2.0', version: '>= 2.56')
dependency('gstreamer-1.0', version: '>= 1.12')
dependency('gtk+-3.0', version: '>= 3.24.7')
+dependency('libcurl', version: '>= 7.24.0')
dependency('libhandy-1', version: '>= 1.0.0')
dependency('pango', version: '>= 1.0')
dependency('sqlite3', version: '>= 3.20')
diff --git a/res/musicus.gresource.xml b/res/musicus.gresource.xml
index 92e3c7d..2e487fc 100644
--- a/res/musicus.gresource.xml
+++ b/res/musicus.gresource.xml
@@ -6,6 +6,7 @@
ui/ensemble_screen.ui
ui/instrument_editor.ui
ui/instrument_selector.ui
+ ui/login_dialog.ui
ui/part_editor.ui
ui/performance_editor.ui
ui/person_editor.ui
@@ -21,6 +22,7 @@
ui/recording_selector.ui
ui/recording_selector_screen.ui
ui/section_editor.ui
+ ui/server_dialog.ui
ui/tracks_editor.ui
ui/track_editor.ui
ui/window.ui
diff --git a/res/ui/login_dialog.ui b/res/ui/login_dialog.ui
new file mode 100644
index 0000000..25c83f2
--- /dev/null
+++ b/res/ui/login_dialog.ui
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
diff --git a/res/ui/preferences.ui b/res/ui/preferences.ui
index 7c7d80b..3ba91cd 100644
--- a/res/ui/preferences.ui
+++ b/res/ui/preferences.ui
@@ -40,7 +40,59 @@
+
+
+ True
+ False
+ Server connection
+
+
+ True
+ True
+ False
+ Server URL
+ url_button
+ Not set
+
+
+ Change
+ True
+ True
+ True
+ center
+
+
+
+
+
+
+ True
+ True
+ False
+ Login credentials
+ login_button
+ Not logged in
+
+
+ Change
+ True
+ True
+ True
+ center
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/ui/server_dialog.ui b/res/ui/server_dialog.ui
new file mode 100644
index 0000000..c4401b5
--- /dev/null
+++ b/res/ui/server_dialog.ui
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+ False
+ True
+ True
+ dialog
+
+
+ True
+ False
+ vertical
+
+
+
+ False
+ True
+ 0
+
+
+
+
+
+ True
+ False
+ 18
+ 12
+ 6
+
+
+ True
+ False
+ end
+ URL
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ True
+ True
+ True
+
+
+ 1
+ 0
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+
diff --git a/src/backend.rs b/src/backend/backend.rs
similarity index 88%
rename from src/backend.rs
rename to src/backend/backend.rs
index bd412c2..eaf15f0 100644
--- a/src/backend.rs
+++ b/src/backend/backend.rs
@@ -1,13 +1,23 @@
-use super::database::*;
+use super::secure;
+use crate::database::*;
use crate::player::*;
use anyhow::{anyhow, Result};
use futures_channel::oneshot::Sender;
use futures_channel::{mpsc, oneshot};
use gio::prelude::*;
+use serde::Serialize;
use std::cell::RefCell;
use std::path::PathBuf;
use std::rc::Rc;
+/// Credentials used for login.
+#[derive(Serialize, Debug, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct LoginData {
+ pub username: String,
+ pub password: String,
+}
+
pub enum BackendState {
NoMusicLibrary,
Loading,
@@ -50,6 +60,10 @@ pub struct Backend {
state_sender: RefCell>,
action_sender: RefCell