diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 1272358..89391d7 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -1,5 +1,8 @@ -pub use cosmic_bg_config::Color; -pub use cosmic_comp_config::XkbConfig; +use cosmic_config::{ConfigGet, CosmicConfigEntry}; +use std::{fs, path::Path}; + +pub use cosmic_bg_config::{Color, Source}; +pub use cosmic_comp_config::{CosmicCompConfig, XkbConfig}; pub use cosmic_theme::Theme; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -11,8 +14,169 @@ pub struct UserData { pub theme_opt: Option, pub wallpapers_opt: Option>, pub xkb_config_opt: Option, - pub clock_military_time: bool, - // pub clock_show_seconds: bool, + pub clock_military_time_opt: Option, +} + +impl UserData { + pub fn full_name_or_name(&self) -> &str { + if let Some(full_name) = &self.full_name_opt { + if !full_name.is_empty() { + return full_name.as_str(); + } + } + self.name.as_str() + } + + pub fn load_wallpapers_as_user(&mut self, wallpaper_state: &cosmic_bg_config::state::State) { + let mut wallpaper_datas = Vec::new(); + for (output, source) in wallpaper_state.wallpapers.iter() { + match source { + Source::Path(path) => match fs::read(&path) { + Ok(bytes) => { + wallpaper_datas.push((output.clone(), WallpaperData::Bytes(bytes))); + } + Err(err) => { + log::error!("failed to read wallpaper {:?}: {:?}", path, err); + } + }, + Source::Color(color) => { + wallpaper_datas.push((output.clone(), WallpaperData::Color(color.clone()))); + } + } + } + self.wallpapers_opt = Some(wallpaper_datas); + } + + pub fn load_config_as_user(&mut self) { + self.icon_opt = None; + self.theme_opt = None; + self.wallpapers_opt = None; + self.xkb_config_opt = None; + self.clock_military_time_opt = None; + + //TODO: use accountsservice? + //IMPORTANT: This file is owned by root and safe to read (it won't be a link to /etc/shadow for example) + // It may not exist if the user uses one of the system icons. In that case, we should read the + // information in /var/lib/AccountsService/users, and then read the icon path as the user + let icon_path = Path::new("/var/lib/AccountsService/icons").join(&self.name); + if icon_path.is_file() { + match fs::read(&icon_path) { + Ok(icon_data) => { + self.icon_opt = Some(icon_data); + } + Err(err) => { + log::error!("failed to read icon {:?}: {:?}", icon_path, err); + } + } + } + + let mut is_dark = true; + match cosmic_theme::ThemeMode::config() { + Ok(helper) => match cosmic_theme::ThemeMode::get_entry(&helper) { + Ok(theme_mode) => { + is_dark = theme_mode.is_dark; + } + Err((errs, theme_mode)) => { + log::error!("failed to load cosmic-theme config: {:?}", errs); + is_dark = theme_mode.is_dark; + } + }, + Err(err) => { + log::error!("failed to create cosmic-theme mode helper: {:?}", err); + } + } + + match if is_dark { + cosmic_theme::Theme::dark_config() + } else { + cosmic_theme::Theme::light_config() + } { + Ok(helper) => match cosmic_theme::Theme::get_entry(&helper) { + Ok(theme) => { + self.theme_opt = Some(theme); + } + Err((errs, theme)) => { + log::error!("failed to load cosmic-theme config: {:?}", errs); + self.theme_opt = Some(theme); + } + }, + Err(err) => { + log::error!("failed to create cosmic-theme config helper: {:?}", err); + } + } + + //TODO: fallback to background config if background state is not set? + let mut wallpaper_state_opt = None; + match cosmic_bg_config::state::State::state() { + Ok(helper) => match cosmic_bg_config::state::State::get_entry(&helper) { + Ok(state) => { + wallpaper_state_opt = Some(state); + } + Err((errs, state)) => { + log::error!("failed to load cosmic-bg state: {:?}", errs); + wallpaper_state_opt = Some(state); + } + }, + Err(err) => { + log::error!("failed to create cosmic-bg state helper: {:?}", err); + } + } + + if let Some(wallpaper_state) = wallpaper_state_opt { + self.load_wallpapers_as_user(&wallpaper_state); + } + + match cosmic_config::Config::new("com.system76.CosmicComp", CosmicCompConfig::VERSION) { + Ok(config_handler) => match CosmicCompConfig::get_entry(&config_handler) { + Ok(config) => { + self.xkb_config_opt = Some(config.xkb_config); + } + Err((errs, config)) => { + log::error!("errors loading cosmic-comp config: {:?}", errs); + self.xkb_config_opt = Some(config.xkb_config); + } + }, + Err(err) => { + log::error!("failed to create cosmic-comp config handler: {}", err); + } + }; + + match cosmic_config::Config::new("com.system76.CosmicAppletTime", 1) { + Ok(config_handler) => match config_handler.get("military_time") { + Ok(value) => { + self.clock_military_time_opt = Some(value); + } + Err(err) => { + log::error!("failed to load military time config: {:?}", err); + } + }, + Err(err) => { + log::error!( + "failed to create CosmicAppletTime config handler: {:?}", + err + ); + } + }; + } +} + +impl From for UserData { + fn from(user: pwd::Passwd) -> Self { + Self { + uid: user.uid, + name: user.name.clone(), + full_name_opt: user + .gecos + .as_ref() + .and_then(|gecos| gecos.split(',').next()) + .map(|x| x.to_string()), + icon_opt: None, + theme_opt: None, + wallpapers_opt: None, + xkb_config_opt: None, + clock_military_time_opt: None, + } + } } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 8410b16..d388bbd 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -1,8 +1,5 @@ -use cosmic_bg_config::Source; -use cosmic_comp_config::CosmicCompConfig; -use cosmic_config::{ConfigGet, CosmicConfigEntry}; -use cosmic_greeter_daemon::{UserData, WallpaperData}; -use std::{env, error::Error, fs, future::pending, io, path::Path}; +use cosmic_greeter_daemon::UserData; +use std::{env, error::Error, future::pending, io, path::Path}; use zbus::{ConnectionBuilder, DBusError}; //IMPORTANT: this function is critical to the security of this proxy. It must ensure that the @@ -86,148 +83,11 @@ impl GreeterProxy { _ => (), } - //TODO: use accountsservice - //IMPORTANT: This file is owned by root and safe to read (it won't be a link to /etc/shadow for example) - // It may not exist if the user uses one of the system icons. In that case, we should read the - // information in /var/lib/AccountsService/users, and then read the icon path as the user - let icon_path = Path::new("/var/lib/AccountsService/icons").join(&user.name); - let icon_opt = if icon_path.is_file() { - match fs::read(&icon_path) { - Ok(icon_data) => Some(icon_data), - Err(err) => { - log::error!("failed to read icon {:?}: {:?}", icon_path, err); - None - } - } - } else { - None - }; + let mut user_data = UserData::from(user.clone()); - let mut user_data = UserData { - uid: user.uid, - name: user.name.clone(), - full_name_opt: user - .gecos - .as_ref() - .filter(|s| !s.is_empty()) - .map(|gecos| gecos.split(',').next().unwrap_or_default().to_string()), - icon_opt, - theme_opt: None, - //TODO: should wallpapers come from a per-user call? - wallpapers_opt: None, - xkb_config_opt: None, - clock_military_time: false, - // clock_show_seconds: false, - }; - - //IMPORTANT: Assume the identity of the user to ensure we don't read wallpaper file data as root - run_as_user(&user, || { - let mut is_dark = true; - match cosmic_theme::ThemeMode::config() { - Ok(helper) => match cosmic_theme::ThemeMode::get_entry(&helper) { - Ok(theme_mode) => { - is_dark = theme_mode.is_dark; - } - Err((errs, theme_mode)) => { - log::error!("failed to load cosmic-theme config: {:?}", errs); - is_dark = theme_mode.is_dark; - } - }, - Err(err) => { - log::error!("failed to create cosmic-theme mode helper: {:?}", err); - } - } - - match if is_dark { - cosmic_theme::Theme::dark_config() - } else { - cosmic_theme::Theme::light_config() - } { - Ok(helper) => match cosmic_theme::Theme::get_entry(&helper) { - Ok(theme) => { - user_data.theme_opt = Some(theme); - } - Err((errs, theme)) => { - log::error!("failed to load cosmic-theme config: {:?}", errs); - user_data.theme_opt = Some(theme); - } - }, - Err(err) => { - log::error!("failed to create cosmic-theme config helper: {:?}", err); - } - } - - //TODO: fallback to background config if background state is not set? - let mut wallpaper_state_opt = None; - match cosmic_bg_config::state::State::state() { - Ok(helper) => match cosmic_bg_config::state::State::get_entry(&helper) { - Ok(state) => { - wallpaper_state_opt = Some(state); - } - Err((errs, state)) => { - log::error!("failed to load cosmic-bg state: {:?}", errs); - wallpaper_state_opt = Some(state); - } - }, - Err(err) => { - log::error!("failed to create cosmic-bg state helper: {:?}", err); - } - } - - if let Some(wallpaper_state) = wallpaper_state_opt { - let mut wallpaper_datas = Vec::new(); - for (output, source) in wallpaper_state.wallpapers { - match source { - Source::Path(path) => match fs::read(&path) { - Ok(bytes) => { - wallpaper_datas.push((output, WallpaperData::Bytes(bytes))); - } - Err(err) => { - log::error!("failed to read wallpaper {:?}: {:?}", path, err); - } - }, - Source::Color(color) => { - wallpaper_datas.push((output, WallpaperData::Color(color))); - } - } - } - user_data.wallpapers_opt = Some(wallpaper_datas); - } - - match cosmic_config::Config::new( - "com.system76.CosmicComp", - CosmicCompConfig::VERSION, - ) { - Ok(config_handler) => match CosmicCompConfig::get_entry(&config_handler) { - Ok(config) => { - user_data.xkb_config_opt = Some(config.xkb_config); - } - Err((errs, config)) => { - log::error!("errors loading cosmic-comp config: {:?}", errs); - user_data.xkb_config_opt = Some(config.xkb_config); - } - }, - Err(err) => { - log::error!("failed to create cosmic-comp config handler: {}", err); - } - }; - - match cosmic_config::Config::new("com.system76.CosmicAppletTime", 1) { - Ok(config_handler) => { - user_data.clock_military_time = - config_handler.get("military_time").unwrap_or_default(); - // user_data.clock_show_seconds = - // config_handler.get("show_seconds").unwrap_or_default(); - } - Err(err) => { - log::error!( - "failed to create CosmicAppletTime config handler: {:?}", - err - ); - } - }; - }) - .map_err(|err| GreeterError::RunAsUser(err.to_string()))?; + //IMPORTANT: Assume the identity of the user to ensure we don't read user file data as root + run_as_user(&user, || user_data.load_config_as_user()) + .map_err(|err| GreeterError::RunAsUser(err.to_string()))?; user_datas.push(user_data); } diff --git a/examples/server.rs b/examples/server.rs index 76e9c2f..9cd074d 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,4 +1,4 @@ -use greetd_ipc::{codec::TokioCodec, AuthMessageType, ErrorType, Request, Response}; +use greetd_ipc::{AuthMessageType, ErrorType, Request, Response, codec::TokioCodec}; use std::{env, fs, io, thread}; use tokio::net::UnixListener; diff --git a/src/greeter.rs b/src/greeter.rs index 5c9ee6e..17253c0 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -90,36 +90,7 @@ fn user_data_fallback() -> Vec { _ => true, } }) - .map(|user| { - //TODO: use accountsservice - let icon_path = Path::new("/var/lib/AccountsService/icons").join(&user.name); - let icon_opt = if icon_path.is_file() { - match fs::read(&icon_path) { - Ok(icon_data) => Some(icon_data), - Err(err) => { - log::error!("failed to read {:?}: {:?}", icon_path, err); - None - } - } - } else { - None - }; - - UserData { - uid: user.uid, - name: user.name, - full_name_opt: user - .gecos - .filter(|s| !s.is_empty()) - .map(|gecos| gecos.split(',').next().unwrap_or_default().to_string()), - icon_opt, - theme_opt: None, - wallpapers_opt: None, - xkb_config_opt: None, - clock_military_time: false, - // clock_show_seconds: false, - } - }) + .map(UserData::from) .collect() } } @@ -465,7 +436,7 @@ impl App { self.flags .user_datas .get(i) - .map(|user| user.clock_military_time) + .and_then(|user| user.clock_military_time_opt) }) .unwrap_or_default(); let date_time_column = self.time.date_time_widget(military_time); @@ -667,16 +638,13 @@ impl App { } None => {} } - match &user_data.full_name_opt { - Some(full_name) => { - column = column.push( - widget::container(widget::text::title4(full_name)) - .width(Length::Fill) - .align_x(alignment::Horizontal::Center), - ); - } - None => {} - } + column = column.push( + widget::container(widget::text::title4( + user_data.full_name_or_name(), + )) + .width(Length::Fill) + .align_x(alignment::Horizontal::Center), + ); } } match &self.prompt_opt { diff --git a/src/locker.rs b/src/locker.rs index 52ff444..86a52b9 100644 --- a/src/locker.rs +++ b/src/locker.rs @@ -22,7 +22,7 @@ use cosmic::{ iced_runtime::core::window::Id as SurfaceId, widget, }; -use cosmic_config::CosmicConfigEntry; +use cosmic_greeter_daemon::{UserData, WallpaperData}; use std::time::Duration; use std::{ any::TypeId, @@ -31,7 +31,7 @@ use std::{ ffi::{CStr, CString}, fs, os::fd::OwnedFd, - path::{Path, PathBuf}, + path::PathBuf, process, sync::Arc, }; @@ -44,47 +44,21 @@ fn lockfile_opt() -> Option { Some(runtime_dir.join(format!("cosmic-greeter-{}.lock", session_id))) } -pub fn main(current_user: pwd::Passwd) -> Result<(), Box> { +pub fn main(user: pwd::Passwd) -> Result<(), Box> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); crate::localize::localize(); - //TODO: use accountsservice - let icon_path = Path::new("/var/lib/AccountsService/icons").join(¤t_user.name); - let icon_opt = if icon_path.is_file() { - match fs::read(&icon_path) { - Ok(icon_data) => Some(widget::image::Handle::from_bytes(icon_data)), - Err(err) => { - log::error!("failed to read {:?}: {:?}", icon_path, err); - None - } - } - } else { - None - }; + let mut user_data = UserData::from(user); + // We are already the user at this point + user_data.load_config_as_user(); - let mut wallpapers = Vec::new(); - match cosmic_bg_config::state::State::state() { - Ok(helper) => match cosmic_bg_config::state::State::get_entry(&helper) { - Ok(state) => { - wallpapers = state.wallpapers; - } - Err(err) => { - log::error!("failed to load cosmic-bg state: {:?}", err); - } - }, - Err(err) => { - log::error!("failed to create cosmic-bg state helper: {:?}", err); - } - } let fallback_background = widget::image::Handle::from_bytes(include_bytes!("../res/background.jpg").as_slice()); let flags = Flags { - current_user, - icon_opt, + user_data, lockfile_opt: lockfile_opt(), - wallpapers, fallback_background, }; @@ -207,10 +181,8 @@ impl pam_client::ConversationHandler for Conversation { #[derive(Clone)] pub struct Flags { - current_user: pwd::Passwd, - icon_opt: Option, + user_data: UserData, lockfile_opt: Option, - wallpapers: Vec<(String, cosmic_bg_config::Source)>, fallback_background: widget::image::Handle, } @@ -344,11 +316,12 @@ impl App { .spacing(12.0) .max_width(280.0); - match &self.flags.icon_opt { + match &self.flags.user_data.icon_opt { Some(icon) => { column = column.push( widget::container( - widget::Image::new(icon.clone()) + //TODO: cache image handle? + widget::Image::new(widget::image::Handle::from_bytes(icon.clone())) .width(Length::Fixed(78.0)) .height(Length::Fixed(78.0)), ) @@ -358,23 +331,14 @@ impl App { } None => {} } - match self - .flags - .current_user - .gecos - .as_ref() - .filter(|s| !s.is_empty()) - { - Some(gecos) => { - let full_name = gecos.split(",").next().unwrap_or_default(); - column = column.push( - widget::container(widget::text::title4(full_name)) - .width(Length::Fill) - .align_x(alignment::Horizontal::Center), - ); - } - None => {} - } + + column = column.push( + widget::container(widget::text::title4( + self.flags.user_data.full_name_or_name(), + )) + .width(Length::Fill) + .align_x(alignment::Horizontal::Center), + ); match &self.prompt_opt { Some((prompt, secret, value_opt)) => match value_opt { @@ -459,35 +423,26 @@ impl App { continue; } - let output_name = match self.surface_names.get(surface_id) { - Some(some) => some, - None => continue, + let Some(output_name) = self.surface_names.get(surface_id) else { + continue; }; log::info!("updating wallpaper for {:?}", output_name); - for (wallpaper_output_name, wallpaper_source) in self.flags.wallpapers.iter() { + let Some(wallpapers) = &self.flags.user_data.wallpapers_opt else { + continue; + }; + + for (wallpaper_output_name, wallpaper_data) in wallpapers.iter() { if wallpaper_output_name == output_name { - match wallpaper_source { - cosmic_bg_config::Source::Path(path) => { - match fs::read(path) { - Ok(bytes) => { - let image = widget::image::Handle::from_bytes(bytes); - self.surface_images.insert(*surface_id, image); - //TODO: what to do about duplicates? - break; - } - Err(err) => { - log::warn!( - "output {}: failed to load wallpaper {:?}: {:?}", - output.id(), - path, - err - ); - } - } + match wallpaper_data { + WallpaperData::Bytes(bytes) => { + let image = widget::image::Handle::from_bytes(bytes.clone()); + self.surface_images.insert(*surface_id, image); + //TODO: what to do about duplicates? + break; } - cosmic_bg_config::Source::Color(color) => { + WallpaperData::Color(color) => { //TODO: support color sources log::warn!("output {}: unsupported source {:?}", output.id(), color); } @@ -737,7 +692,7 @@ impl cosmic::Application for App { return Task::none(); } - let username = self.flags.current_user.name.clone(); + let username = self.flags.user_data.name.clone(); let (locked_task, locked_handle) = cosmic::task::stream( cosmic::iced_futures::stream::channel(16, |mut msg_tx| async move { // Send heartbeat once a second to update time. @@ -880,7 +835,9 @@ impl cosmic::Application for App { self.value_tx_opt = Some(value_tx); } Message::BackgroundState(background_state) => { - self.flags.wallpapers = background_state.wallpapers; + self.flags + .user_data + .load_wallpapers_as_user(&background_state); self.surface_images.clear(); self.update_wallpapers(); } diff --git a/src/main.rs b/src/main.rs index 5468d7c..7a2b73e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use cosmic_greeter::{greeter, locker}; use clap_lex::RawArgs; +use cosmic_greeter::{greeter, locker}; use std::error::Error; fn main() -> Result<(), Box> { @@ -52,4 +52,3 @@ Options: -v, --version Show the version of cosmic-greeter"# ); } -