use chrono::offset::Utc; use curl::{easy, easy::Easy2}; use rand::prelude::*; use ring_compat::signature::ed25519::SigningKey; use rusqlite::{Connection, DatabaseName}; use tauri::{Manager, State}; use tokio::sync::Mutex; use crate::server::app_state::AppState; #[derive(Debug)] struct Collector(Vec, Vec, usize); impl easy::Handler for Collector { fn write(&mut self, data: &[u8]) -> Result { self.0.extend_from_slice(data); Ok(data.len()) } fn read(&mut self, data: &mut [u8]) -> Result { let p = self.2; let src: &[u8] = self.1.as_ref(); let n = usize::min(src.len() - p, data.len()); data[..n].copy_from_slice(&src[p..(p + n)]); self.2 = n + p; Ok(n) } fn seek(&mut self, whence: std::io::SeekFrom) -> easy::SeekResult { use std::io::SeekFrom::{Current, End, Start}; match whence { Start(p) => { self.2 = p as usize; easy::SeekResult::Ok } End(d) => { let p = self.1.len() as i64; if p + d < 0 { easy::SeekResult::Fail } else { self.2 = (p + d) as usize; easy::SeekResult::Ok } } Current(d) => { let p = self.2 as i64; if p + d < 0 { easy::SeekResult::Fail } else { self.2 = (p + d) as usize; easy::SeekResult::Ok } } } } } fn data_client(file: &str) -> Result, ()> { let mut client = Easy2::new(Collector(Vec::new(), Vec::new(), 0)); let url = format!( "https://cloud.seebruecke.org/public.php/webdav/data/{}", file ); client.url(&url).map_err(|_| ())?; client.username(include_str!("cloud_user.txt")) .map_err(|_| ())?; client.http_auth(easy::Auth::new().auto(true)) .map_err(|_| ())?; client.ssl_cainfo_blob(include_bytes!("isrg-root-x1.pem")) .map_err(|_| ())?; Ok(client) } fn put_client(file: &str, payload: &[u8]) -> Result, ()> { let mut client = data_client(&file)?; client.put(true).map_err(|_| ())?; client.get_mut().1.extend_from_slice(payload); client.in_filesize(payload.len() as u64) .map_err(|e| println!("{:?}", e))?; client.upload(true).map_err(|e| println!("{:?}", e))?; Ok(client) } #[tauri::command] pub async fn pull_data( _state: State<'_, Mutex>, ) -> Result { let mut client = data_client("")?; client.custom_request("PROPFIND").map_err(|_| ())?; client.perform().map_err(|_| ())?; let content = &client.get_ref().0; Ok(String::from_utf8_lossy(content).to_string()) } async fn push_key(id: &u64, key: &SigningKey) -> Result<(), ()> { let file = format!("{:016X}.key", id); let v_key = key.verifying_key(); let client = put_client(&file, v_key.as_ref())?; let _perf = client.perform().map_err(|e| println!("{:?}", e))?; Ok(()) } fn push_db(id: &u64, db: &Connection, app: tauri::AppHandle) -> Result<(), ()> { let filename = format!("{:016X}.sqlite", id); let path = app .path() .resolve(&filename, tauri::path::BaseDirectory::Temp) .map_err(|e| println!("{:?}", e))?; db.backup(DatabaseName::Main, &path, None) .map_err(|e| println!("{:?}", e))?; let buf = std::fs::read(&path).map_err(|e| println!("{:?}", e))?; let client = put_client(&filename, buf.as_ref())?; let _perf = client.perform().map_err(|e| println!("{:?}", e))?; Ok(()) } #[tauri::command] pub async fn push_data( app: tauri::AppHandle, state: State<'_, Mutex>, ) -> Result<(), ()> { let state = state.lock().await; push_key(&state.id, &state.key).await?; push_db(&state.id, &state.db, app)?; Ok(()) }