server/lib: can now have custom-files which clients can access if they know the path.

This commit is contained in:
Mark 2023-10-26 16:35:58 +02:00
parent f61f52fdc7
commit 273eac4b43
3 changed files with 35 additions and 11 deletions

View File

@ -26,6 +26,12 @@ pub struct Database {
albums: HashMap<AlbumId, Album>, albums: HashMap<AlbumId, Album>,
songs: HashMap<SongId, Song>, songs: HashMap<SongId, Song>,
covers: HashMap<CoverId, Cover>, covers: HashMap<CoverId, Cover>,
/// clients can access files in this directory if they know the relative path.
/// can be used to embed custom images in tags of songs/albums/artists.
/// None -> no access
/// Some(None) -> access to lib_directory
/// Some(Some(path)) -> access to path
pub custom_files: Option<Option<PathBuf>>,
// These will be used for autosave once that gets implemented // These will be used for autosave once that gets implemented
db_data_file_change_first: Option<Instant>, db_data_file_change_first: Option<Instant>,
db_data_file_change_last: Option<Instant>, db_data_file_change_last: Option<Instant>,
@ -374,6 +380,7 @@ impl Database {
albums: HashMap::new(), albums: HashMap::new(),
songs: HashMap::new(), songs: HashMap::new(),
covers: HashMap::new(), covers: HashMap::new(),
custom_files: None,
db_data_file_change_first: None, db_data_file_change_first: None,
db_data_file_change_last: None, db_data_file_change_last: None,
queue: QueueContent::Folder(0, vec![], String::new()).into(), queue: QueueContent::Folder(0, vec![], String::new()).into(),
@ -392,6 +399,7 @@ impl Database {
albums: HashMap::new(), albums: HashMap::new(),
songs: HashMap::new(), songs: HashMap::new(),
covers: HashMap::new(), covers: HashMap::new(),
custom_files: None,
db_data_file_change_first: None, db_data_file_change_first: None,
db_data_file_change_last: None, db_data_file_change_last: None,
queue: QueueContent::Folder(0, vec![], String::new()).into(), queue: QueueContent::Folder(0, vec![], String::new()).into(),
@ -412,6 +420,7 @@ impl Database {
albums: ToFromBytes::from_bytes(&mut file)?, albums: ToFromBytes::from_bytes(&mut file)?,
songs: ToFromBytes::from_bytes(&mut file)?, songs: ToFromBytes::from_bytes(&mut file)?,
covers: ToFromBytes::from_bytes(&mut file)?, covers: ToFromBytes::from_bytes(&mut file)?,
custom_files: None,
db_data_file_change_first: None, db_data_file_change_first: None,
db_data_file_change_last: None, db_data_file_change_last: None,
queue: QueueContent::Folder(0, vec![], String::new()).into(), queue: QueueContent::Folder(0, vec![], String::new()).into(),

View File

@ -1,6 +1,8 @@
use std::{ use std::{
fs,
io::BufRead, io::BufRead,
io::{BufReader, Read, Write}, io::{BufReader, Read, Write},
path::Path,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
@ -118,16 +120,24 @@ pub fn handle_one_connection_as_get(
writeln!(connection.get_mut(), "no data")?; writeln!(connection.get_mut(), "no data")?;
} }
} }
"song-file-blocking" => { "custom-file" => {
if let Some(bytes) = if let Some(bytes) = request.next().and_then(|path| {
request
.next()
.and_then(|id| id.parse().ok())
.and_then(|id| {
let db = db.lock().unwrap(); let db = db.lock().unwrap();
db.get_song(&id).and_then(|song| song.cached_data_now(&db)) let mut parent = match &db.custom_files {
}) None => None,
{ Some(None) => Some(db.lib_directory.clone()),
Some(Some(p)) => Some(p.clone()),
};
// check for malicious paths
if Path::new(path).is_absolute() {
parent = None;
}
if let Some(parent) = parent {
fs::read(parent.join(path)).ok()
} else {
None
}
}) {
writeln!(connection.get_mut(), "len: {}", bytes.len())?; writeln!(connection.get_mut(), "len: {}", bytes.len())?;
connection.get_mut().write_all(&bytes)?; connection.get_mut().write_all(&bytes)?;
} else { } else {

View File

@ -31,13 +31,17 @@ struct Args {
/// requires the `assets/` folder to be present! /// requires the `assets/` folder to be present!
#[arg(long)] #[arg(long)]
web: Option<SocketAddr>, web: Option<SocketAddr>,
/// allow clients to access files in this directory, or the lib_dir if not specified.
#[arg(long)]
custom_files: Option<Option<PathBuf>>,
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
// parse args // parse args
let args = Args::parse(); let args = Args::parse();
let database = if args.init { let mut database = if args.init {
Database::new_empty(args.dbfile, args.lib_dir) Database::new_empty(args.dbfile, args.lib_dir)
} else { } else {
match Database::load_database(args.dbfile.clone(), args.lib_dir.clone()) { match Database::load_database(args.dbfile.clone(), args.lib_dir.clone()) {
@ -51,6 +55,7 @@ async fn main() {
} }
} }
}; };
database.custom_files = args.custom_files;
// database can be shared by multiple threads using Arc<Mutex<_>> // database can be shared by multiple threads using Arc<Mutex<_>>
let database = Arc::new(Mutex::new(database)); let database = Arc::new(Mutex::new(database));
if args.tcp.is_some() || args.web.is_some() { if args.tcp.is_some() || args.web.is_some() {