use crate::{utils, Result};
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::Path;
#[derive(Debug)]
pub struct CipherFile {
file: File,
}
impl CipherFile {
pub(crate) fn new(path: &Path) -> Result<Self> {
Ok(Self {
file: File::options()
.read(true)
.write(true)
.create(false)
.open(path)?,
})
}
}
impl AsRef<File> for CipherFile {
fn as_ref(&self) -> &File {
&self.file
}
}
impl AsMut<File> for CipherFile {
fn as_mut(&mut self) -> &mut File {
&mut self.file
}
}
#[derive(Debug)]
pub struct RwPlainFile {
file: File,
buffer: Vec<u8>,
last_synced_buffer: Vec<u8>,
encryption_keys: Vec<gpgme::Key>,
}
impl RwPlainFile {
pub(crate) fn new(path: &Path, encryption_keys: Vec<gpgme::Key>) -> Result<Self> {
log::warn!("Opening {} as PlainFile", path.display());
let mut result = Self {
file: File::options()
.read(true)
.write(true)
.create(false)
.open(path)?,
buffer: Vec::with_capacity(path.metadata()?.len() as usize),
last_synced_buffer: Vec::new(),
encryption_keys,
};
result.load_and_decrypt()?;
Ok(result)
}
fn load_and_decrypt(&mut self) -> Result<()> {
log::trace!("Trying to load ciphertext and decrypt it to plaintext");
let mut ciphertext = Vec::with_capacity(self.file.metadata()?.len() as usize);
self.file.seek(SeekFrom::Start(0))?;
self.file.read_to_end(&mut ciphertext)?;
let mut gpg_ctx = utils::create_gpg_context()?;
gpg_ctx.decrypt(&mut ciphertext, &mut self.buffer)?;
self.last_synced_buffer = self.buffer.clone();
Ok(())
}
pub fn sync(&mut self, force: bool) -> Result<()> {
if !force && self.last_synced_buffer != self.buffer {
let mut gpg_ctx = utils::create_gpg_context()?;
let mut ciphertext = Vec::new();
gpg_ctx.encrypt(&self.encryption_keys, &self.buffer, &mut ciphertext)?;
self.file.seek(SeekFrom::Start(0))?;
self.file.set_len(ciphertext.len() as u64)?;
self.file.write_all(&ciphertext)?;
self.last_synced_buffer = self.buffer.clone();
}
self.file.sync_all()?;
Ok(())
}
}
impl AsRef<Vec<u8>> for RwPlainFile {
fn as_ref(&self) -> &Vec<u8> {
&self.buffer
}
}
impl AsMut<Vec<u8>> for RwPlainFile {
fn as_mut(&mut self) -> &mut Vec<u8> {
&mut self.buffer
}
}
impl Drop for RwPlainFile {
fn drop(&mut self) {
if let Err(e) = self.sync(false) {
log::warn!(
"Error during drop of PlainFile, could not store encrypted content in file: {:?}",
e
)
}
}
}
#[derive(Debug)]
pub struct RoPlainFile {
buffer: Vec<u8>,
}
impl RoPlainFile {
pub(crate) fn new(path: &Path) -> Result<Self> {
log::warn!("Opening {} as RoPlainFile", path.display());
let mut file = File::options().read(true).create(false).open(path)?;
Ok(Self {
buffer: Self::load_and_decrypt(&mut file)?,
})
}
fn load_and_decrypt(file: &mut File) -> Result<Vec<u8>> {
log::trace!("Trying to load ciphertext and decrypt it to plaintext");
let file_len = file.metadata()?.len() as usize;
let mut buffer = Vec::with_capacity(file_len);
let mut ciphertext = Vec::with_capacity(file_len);
file.seek(SeekFrom::Start(0))?;
file.read_to_end(&mut ciphertext)?;
let mut gpg_ctx = utils::create_gpg_context()?;
gpg_ctx.decrypt(&mut ciphertext, &mut buffer)?;
Ok(buffer)
}
}
impl AsRef<Vec<u8>> for RoPlainFile {
fn as_ref(&self) -> &Vec<u8> {
&self.buffer
}
}