mirror of
https://github.com/chatmail/core.git
synced 2026-04-24 08:56:29 +03:00
Introduce a BlobObject type for blobs
This creates a specific type for blobs, with well defined conversions at the borders. It also introduces a strong type for the Param::File value since that param is often used used by the public API to set filenames using absolute paths, but then core changes the param to a blob before it gets to the database. This eliminates a few more functions with very mallable C-like arguments behaviour which combine a number of operations in one. Because blob filenames are stored so often in arbitrary strings this does add more code when receiving those, until the storage is fixed. File name sanitisation is now deletated to the sanitize-filename crate which should do a slightly better job at this.
This commit is contained in:
committed by
holger krekel
parent
f0fc50d5a9
commit
6c9e16d31a
103
src/context.rs
103
src/context.rs
@@ -1,7 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||
|
||||
@@ -11,7 +9,6 @@ use crate::chat::*;
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::dc_tools::{dc_copy_file, dc_derive_safe_stem_ext};
|
||||
use crate::error::*;
|
||||
use crate::events::Event;
|
||||
use crate::imap::*;
|
||||
@@ -24,7 +21,6 @@ use crate::message::{self, Message};
|
||||
use crate::param::Params;
|
||||
use crate::smtp::*;
|
||||
use crate::sql::Sql;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
/// Callback function type for [Context]
|
||||
///
|
||||
@@ -165,59 +161,6 @@ impl Context {
|
||||
self.blobdir.as_path()
|
||||
}
|
||||
|
||||
pub fn copy_to_blobdir(&self, orig_filename: impl AsRef<str>) -> Result<String> {
|
||||
// return a $BLOBDIR/<filename> with the content of orig_filename
|
||||
// copied into it. The <filename> will be safely derived from
|
||||
// orig_filename, and will not clash with existing filenames.
|
||||
let dest = self.new_blob_file(&orig_filename, b"")?;
|
||||
if dc_copy_file(
|
||||
&self,
|
||||
PathBuf::from(orig_filename.as_ref()),
|
||||
PathBuf::from(&dest),
|
||||
) {
|
||||
Ok(dest)
|
||||
} else {
|
||||
bail!("could not copy {} to {}", orig_filename.as_ref(), dest);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_blob_file(&self, orig_filename: impl AsRef<str>, data: &[u8]) -> Result<String> {
|
||||
// return a $BLOBDIR/<FILENAME> string which corresponds to the
|
||||
// respective file in the blobdir, and which contains the data.
|
||||
// FILENAME is computed by looking and possibly mangling the
|
||||
// basename of orig_filename. The resulting filenames are meant
|
||||
// to be human-readable.
|
||||
let (stem, ext) = dc_derive_safe_stem_ext(orig_filename.as_ref());
|
||||
|
||||
// ext starts with "." or is empty string, so we can always resconstruct
|
||||
|
||||
for i in 0..3 {
|
||||
let candidate_basename = match i {
|
||||
// first a try to just use the (possibly mangled) original basename
|
||||
0 => format!("{}{}", stem, ext),
|
||||
|
||||
// otherwise extend stem with random numbers
|
||||
_ => {
|
||||
let mut rng = thread_rng();
|
||||
let random_id: u32 = rng.gen();
|
||||
format!("{}-{}{}", stem, random_id, ext)
|
||||
}
|
||||
};
|
||||
let path = self.get_blobdir().join(&candidate_basename);
|
||||
if let Ok(mut file) = fs::OpenOptions::new()
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
.open(&path)
|
||||
{
|
||||
file.write_all(data)?;
|
||||
let db_entry = format!("$BLOBDIR/{}", candidate_basename);
|
||||
self.call_cb(Event::NewBlobFile(db_entry.clone()));
|
||||
return Ok(db_entry);
|
||||
}
|
||||
}
|
||||
bail!("out of luck to create new blob file");
|
||||
}
|
||||
|
||||
pub fn call_cb(&self, event: Event) -> uintptr_t {
|
||||
(*self.cb)(self, event)
|
||||
}
|
||||
@@ -536,7 +479,6 @@ pub fn get_version_str() -> &'static str {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::dc_tools::*;
|
||||
use crate::test_utils::*;
|
||||
|
||||
#[test]
|
||||
@@ -574,51 +516,6 @@ mod tests {
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_blob_file() {
|
||||
let t = dummy_context();
|
||||
let context = t.ctx;
|
||||
let x = &context.new_blob_file("hello", b"data").unwrap();
|
||||
assert!(dc_file_exist(&context, x));
|
||||
assert!(x.starts_with("$BLOBDIR"));
|
||||
assert!(dc_read_file(&context, x).unwrap() == b"data");
|
||||
|
||||
let y = &context.new_blob_file("hello", b"data").unwrap();
|
||||
assert!(dc_file_exist(&context, y));
|
||||
assert!(y.starts_with("$BLOBDIR/hello-"));
|
||||
|
||||
let x = &context.new_blob_file("xyz/hello.png", b"data").unwrap();
|
||||
assert!(dc_file_exist(&context, x));
|
||||
assert_eq!(x, "$BLOBDIR/hello.png");
|
||||
|
||||
let y = &context.new_blob_file("hello\\world.png", b"data").unwrap();
|
||||
assert!(dc_file_exist(&context, y));
|
||||
assert_eq!(y, "$BLOBDIR/world.png");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_blob_file_long_names() {
|
||||
let t = dummy_context();
|
||||
let context = t.ctx;
|
||||
let s = "12312312039182039182039812039810293810293810293810293801293801293123123";
|
||||
let x = &context.new_blob_file(s, b"data").unwrap();
|
||||
println!("blobfilename '{}'", x);
|
||||
println!("xxxxfilename '{}'", s);
|
||||
assert!(x.len() < s.len());
|
||||
assert!(dc_file_exist(&context, x));
|
||||
assert!(x.starts_with("$BLOBDIR"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_blob_file_unicode() {
|
||||
let t = dummy_context();
|
||||
let context = t.ctx;
|
||||
let s = "helloäworld.qwe";
|
||||
let x = &context.new_blob_file(s, b"data").unwrap();
|
||||
assert_eq!(x, "$BLOBDIR/hello-world.qwe");
|
||||
assert_eq!(dc_read_file(&context, x).unwrap(), b"data");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sqlite_parent_not_exists() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user