refonte
This commit is contained in:
@ -1,25 +1,55 @@
|
||||
use crate::crypto::Crypto;
|
||||
use crate::database::Database;
|
||||
use crate::models::ClipboardData;
|
||||
use crate::models::{ClipboardData, ClipboardEntry};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct HistoryItem {
|
||||
pub content: String,
|
||||
pub timestamp: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum IpcRequest {
|
||||
GetHistory { limit: usize },
|
||||
SetClipboard { content: String },
|
||||
DeleteEntry { content: String },
|
||||
GetHistory {
|
||||
limit: usize,
|
||||
},
|
||||
SetClipboard {
|
||||
content: String,
|
||||
},
|
||||
DeleteEntry {
|
||||
content: String,
|
||||
},
|
||||
UpdateEntry {
|
||||
old_content: String,
|
||||
new_content: String,
|
||||
},
|
||||
AddEntry {
|
||||
content: String,
|
||||
},
|
||||
ClearHistory,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum IpcResponse {
|
||||
History(Vec<String>),
|
||||
History(Vec<HistoryItem>),
|
||||
Ok,
|
||||
Error(String),
|
||||
}
|
||||
|
||||
pub fn start_server(db: Arc<Mutex<Database>>, socket_path: &Path) {
|
||||
fn reply(stream: &mut std::os::unix::net::UnixStream, resp: IpcResponse) {
|
||||
if let Ok(json) = serde_json::to_string(&resp) {
|
||||
let _ = stream.write_all(json.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_server(db: Arc<Mutex<Database>>, crypto: Arc<Crypto>, socket_path: &Path) {
|
||||
if socket_path.exists() {
|
||||
let _ = fs::remove_file(socket_path);
|
||||
}
|
||||
@ -27,99 +57,157 @@ pub fn start_server(db: Arc<Mutex<Database>>, socket_path: &Path) {
|
||||
let listener = match UnixListener::bind(socket_path) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
eprintln!("Error while creating socket {}", e);
|
||||
eprintln!("Erreur socket IPC : {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
println!("ipc server listening {:?}", socket_path);
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
if let Err(e) = fs::set_permissions(socket_path, fs::Permissions::from_mode(0o600)) {
|
||||
eprintln!("Impossible de restreindre les permissions du socket : {e}");
|
||||
}
|
||||
}
|
||||
|
||||
println!("IPC server en écoute sur {:?}", socket_path);
|
||||
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
Ok(mut stream) => {
|
||||
let db_clone = Arc::clone(&db);
|
||||
let crypto_clone = Arc::clone(&crypto);
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut buffer = String::new();
|
||||
let mut buf = String::new();
|
||||
if stream.read_to_string(&mut buf).is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
if stream.read_to_string(&mut buffer).is_ok() {
|
||||
if let Ok(request) = serde_json::from_str::<IpcRequest>(&buffer) {
|
||||
match request {
|
||||
IpcRequest::GetHistory { limit } => {
|
||||
let db_lock = db_clone.lock().unwrap();
|
||||
let req = match serde_json::from_str::<IpcRequest>(&buf) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
eprintln!("IPC parse error : {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO Implem read_history(limit)
|
||||
let history = db_lock.read_history().unwrap_or_default();
|
||||
|
||||
let items: Vec<String> = history
|
||||
.into_iter()
|
||||
.rev()
|
||||
.take(limit)
|
||||
.map(|entry| match entry.content {
|
||||
ClipboardData::Text(t) => t,
|
||||
ClipboardData::Image(img) => format!("{}.jpg", img.id),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let response = IpcResponse::History(items);
|
||||
let response_json = serde_json::to_string(&response).unwrap();
|
||||
let _ = stream.write_all(response_json.as_bytes());
|
||||
}
|
||||
IpcRequest::SetClipboard { content } => {
|
||||
if let Ok(mut clipboard) = arboard::Clipboard::new() {
|
||||
if content.ends_with(".jpg") || content.ends_with(".png") {
|
||||
if let Some(proj_dirs) = directories::ProjectDirs::from(
|
||||
"com", "zefad", "rklipd",
|
||||
) {
|
||||
let img_path = proj_dirs
|
||||
.data_dir()
|
||||
.join("images")
|
||||
.join(&content);
|
||||
if let Ok(img) = image::open(&img_path) {
|
||||
let rgba = img.into_rgba8();
|
||||
let img_data = arboard::ImageData {
|
||||
width: rgba.width() as usize,
|
||||
height: rgba.height() as usize,
|
||||
bytes: std::borrow::Cow::Borrowed(
|
||||
rgba.as_raw(),
|
||||
),
|
||||
};
|
||||
let _ = clipboard.set_image(img_data);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let _ = clipboard.set_text(content);
|
||||
}
|
||||
match req {
|
||||
IpcRequest::GetHistory { limit } => {
|
||||
let lock = db_clone.lock().unwrap();
|
||||
let history = lock.read_history(limit).unwrap_or_default();
|
||||
let items: Vec<HistoryItem> = history
|
||||
.into_iter()
|
||||
.map(|e| {
|
||||
let content = match e.content {
|
||||
ClipboardData::Text(t) => t,
|
||||
ClipboardData::Image(img) => format!("{}.jpg", img.id),
|
||||
};
|
||||
let ts = e
|
||||
.timestamp
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_millis()
|
||||
as i64;
|
||||
HistoryItem {
|
||||
content,
|
||||
timestamp: ts,
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
reply(&mut stream, IpcResponse::History(items));
|
||||
}
|
||||
|
||||
IpcRequest::DeleteEntry { content } => {
|
||||
{
|
||||
let db_lock = db_clone.lock().unwrap();
|
||||
let _ = db_lock.delete_entry_by_content(&content);
|
||||
}
|
||||
IpcRequest::SetClipboard { content } => {
|
||||
let actual =
|
||||
if content.starts_with("enc:") || content.starts_with("enc2:") {
|
||||
crypto_clone.decrypt(&content).unwrap_or(content)
|
||||
} else {
|
||||
content
|
||||
};
|
||||
|
||||
if content.ends_with(".jpg") || content.ends_with(".png") {
|
||||
if let Some(proj_dirs) =
|
||||
match arboard::Clipboard::new() {
|
||||
Ok(mut cb) => {
|
||||
if actual.ends_with(".jpg") || actual.ends_with(".png") {
|
||||
if let Some(dirs) =
|
||||
directories::ProjectDirs::from("com", "zefad", "rklipd")
|
||||
{
|
||||
let img_path =
|
||||
proj_dirs.data_dir().join("images").join(&content);
|
||||
if img_path.exists() {
|
||||
if let Err(e) = std::fs::remove_file(&img_path) {
|
||||
eprintln!("Error while deleting image: {}", e);
|
||||
} else {
|
||||
println!("Image deleted : {}", content);
|
||||
}
|
||||
let path = dirs.data_dir().join("images").join(&actual);
|
||||
if let Ok(img) = image::open(&path) {
|
||||
let rgba = img.into_rgba8();
|
||||
let (w, h) =
|
||||
(rgba.width() as usize, rgba.height() as usize);
|
||||
let _ = cb.set_image(arboard::ImageData {
|
||||
width: w,
|
||||
height: h,
|
||||
bytes: std::borrow::Cow::Owned(rgba.into_raw()),
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let _ = cb.set_text(actual);
|
||||
}
|
||||
reply(&mut stream, IpcResponse::Ok);
|
||||
}
|
||||
Err(e) => reply(&mut stream, IpcResponse::Error(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
IpcRequest::DeleteEntry { content } => {
|
||||
{
|
||||
let lock = db_clone.lock().unwrap();
|
||||
let _ = lock.delete_entry_by_content(&content);
|
||||
}
|
||||
if !content.starts_with("enc:")
|
||||
&& !content.starts_with("enc2:")
|
||||
&& (content.ends_with(".jpg") || content.ends_with(".png"))
|
||||
{
|
||||
if let Some(dirs) =
|
||||
directories::ProjectDirs::from("com", "zefad", "rklipd")
|
||||
{
|
||||
let p = dirs.data_dir().join("images").join(&content);
|
||||
if p.exists() {
|
||||
let _ = fs::remove_file(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
reply(&mut stream, IpcResponse::Ok);
|
||||
}
|
||||
|
||||
IpcRequest::UpdateEntry {
|
||||
old_content,
|
||||
new_content,
|
||||
} => {
|
||||
let lock = db_clone.lock().unwrap();
|
||||
match lock.update_entry_content(&old_content, &new_content) {
|
||||
Ok(_) => reply(&mut stream, IpcResponse::Ok),
|
||||
Err(e) => reply(&mut stream, IpcResponse::Error(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
IpcRequest::AddEntry { content } => {
|
||||
let entry = ClipboardEntry {
|
||||
content: ClipboardData::Text(content),
|
||||
timestamp: SystemTime::now(),
|
||||
};
|
||||
let lock = db_clone.lock().unwrap();
|
||||
match lock.append(entry) {
|
||||
Ok(_) => reply(&mut stream, IpcResponse::Ok),
|
||||
Err(e) => reply(&mut stream, IpcResponse::Error(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
IpcRequest::ClearHistory => {
|
||||
let lock = db_clone.lock().unwrap();
|
||||
match lock.clear_history() {
|
||||
Ok(_) => reply(&mut stream, IpcResponse::Ok),
|
||||
Err(e) => reply(&mut stream, IpcResponse::Error(e.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => eprintln!("Erreur de connexion IPC: {}", e),
|
||||
Err(e) => eprintln!("Erreur connexion IPC : {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user