Skip to content

Commit fbf3ff0

Browse files
authored
refactor: Remove unused blob functions (#6563)
1 parent b916937 commit fbf3ff0

File tree

14 files changed

+88
-411
lines changed

14 files changed

+88
-411
lines changed

src/blob.rs

Lines changed: 1 addition & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ use image::codecs::jpeg::JpegEncoder;
1414
use image::ImageReader;
1515
use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat, Pixel, Rgba};
1616
use num_traits::FromPrimitive;
17-
use tokio::io::AsyncWriteExt;
18-
use tokio::{fs, io, task};
17+
use tokio::{fs, task};
1918
use tokio_stream::wrappers::ReadDirStream;
2019

2120
use crate::config::Config;
@@ -48,73 +47,6 @@ enum ImageOutputFormat {
4847
}
4948

5049
impl<'a> BlobObject<'a> {
51-
/// Creates a new file, returning a tuple of the name and the handle.
52-
async fn create_new_file(
53-
context: &Context,
54-
dir: &Path,
55-
stem: &str,
56-
ext: &str,
57-
) -> Result<(String, fs::File)> {
58-
const MAX_ATTEMPT: u32 = 16;
59-
let mut attempt = 0;
60-
let mut name = format!("{stem}{ext}");
61-
loop {
62-
attempt += 1;
63-
let path = dir.join(&name);
64-
match fs::OpenOptions::new()
65-
// Using `create_new(true)` in order to avoid race conditions
66-
// when creating multiple files with the same name.
67-
.create_new(true)
68-
.write(true)
69-
.open(&path)
70-
.await
71-
{
72-
Ok(file) => return Ok((name, file)),
73-
Err(err) => {
74-
if attempt >= MAX_ATTEMPT {
75-
return Err(err).context("failed to create file");
76-
} else if attempt == 1 && !dir.exists() {
77-
fs::create_dir_all(dir).await.log_err(context).ok();
78-
} else {
79-
name = format!("{}-{}{}", stem, rand::random::<u32>(), ext);
80-
}
81-
}
82-
}
83-
}
84-
}
85-
86-
/// Creates a new blob object with unique name by copying an existing file.
87-
///
88-
/// This creates a new blob
89-
/// and copies an existing file into it. This is done in a
90-
/// in way which avoids race-conditions when multiple files are
91-
/// concurrently created.
92-
pub async fn create_and_copy(context: &'a Context, src: &Path) -> Result<BlobObject<'a>> {
93-
let mut src_file = fs::File::open(src)
94-
.await
95-
.with_context(|| format!("failed to open file {}", src.display()))?;
96-
let (stem, ext) = BlobObject::sanitize_name_and_split_extension(&src.to_string_lossy());
97-
let (name, mut dst_file) =
98-
BlobObject::create_new_file(context, context.get_blobdir(), &stem, &ext).await?;
99-
let name_for_err = name.clone();
100-
if let Err(err) = io::copy(&mut src_file, &mut dst_file).await {
101-
// Attempt to remove the failed file, swallow errors resulting from that.
102-
let path = context.get_blobdir().join(&name_for_err);
103-
fs::remove_file(path).await.ok();
104-
return Err(err).context("failed to copy file");
105-
}
106-
107-
// Ensure that all buffered bytes are written
108-
dst_file.flush().await?;
109-
110-
let blob = BlobObject {
111-
blobdir: context.get_blobdir(),
112-
name: format!("$BLOBDIR/{name}"),
113-
};
114-
context.emit_event(EventType::NewBlobFile(blob.as_name().to_string()));
115-
Ok(blob)
116-
}
117-
11850
/// Creates a blob object by copying or renaming an existing file.
11951
/// If the source file is already in the blobdir, it will be renamed,
12052
/// otherwise it will be copied to the blobdir first.
@@ -209,27 +141,6 @@ impl<'a> BlobObject<'a> {
209141
})
210142
}
211143

212-
/// Creates a blob from a file, possibly copying it to the blobdir.
213-
///
214-
/// If the source file is not a path to into the blob directory
215-
/// the file will be copied into the blob directory first. If the
216-
/// source file is already in the blobdir it will not be copied
217-
/// and only be created if it is a valid blobname, that is no
218-
/// subdirectory is used and [BlobObject::sanitize_name_and_split_extension] does not
219-
/// modify the filename.
220-
///
221-
/// Paths into the blob directory may be either defined by an absolute path
222-
/// or by the relative prefix `$BLOBDIR`.
223-
pub async fn new_from_path(context: &'a Context, src: &Path) -> Result<BlobObject<'a>> {
224-
if src.starts_with(context.get_blobdir()) {
225-
BlobObject::from_path(context, src)
226-
} else if src.starts_with("$BLOBDIR/") {
227-
BlobObject::from_name(context, src.to_str().unwrap_or_default().to_string())
228-
} else {
229-
BlobObject::create_and_copy(context, src).await
230-
}
231-
}
232-
233144
/// Returns a [BlobObject] for an existing blob from a path.
234145
///
235146
/// The path must designate a file directly in the blobdir and
@@ -301,50 +212,6 @@ impl<'a> BlobObject<'a> {
301212
}
302213
}
303214

304-
/// The name is returned as a tuple, the first part
305-
/// being the stem or basename and the second being an extension,
306-
/// including the dot. E.g. "foo.txt" is returned as `("foo",
307-
/// ".txt")` while "bar" is returned as `("bar", "")`.
308-
///
309-
/// The extension part will always be lowercased.
310-
fn sanitize_name_and_split_extension(name: &str) -> (String, String) {
311-
let name = sanitize_filename(name);
312-
// Let's take a tricky filename,
313-
// "file.with_lots_of_characters_behind_point_and_double_ending.tar.gz" as an example.
314-
// Assume that the extension is 32 chars maximum.
315-
let ext: String = name
316-
.chars()
317-
.rev()
318-
.take_while(|c| {
319-
(!c.is_ascii_punctuation() || *c == '.') && !c.is_whitespace() && !c.is_control()
320-
})
321-
.take(33)
322-
.collect::<Vec<_>>()
323-
.iter()
324-
.rev()
325-
.collect();
326-
// ext == "nd_point_and_double_ending.tar.gz"
327-
328-
// Split it into "nd_point_and_double_ending" and "tar.gz":
329-
let mut iter = ext.splitn(2, '.');
330-
iter.next();
331-
332-
let ext = iter.next().unwrap_or_default();
333-
let ext = if ext.is_empty() {
334-
String::new()
335-
} else {
336-
format!(".{ext}")
337-
// ".tar.gz"
338-
};
339-
let stem = name
340-
.strip_suffix(&ext)
341-
.unwrap_or_default()
342-
.chars()
343-
.take(64)
344-
.collect();
345-
(stem, ext.to_lowercase())
346-
}
347-
348215
/// Checks whether a name is a valid blob name.
349216
///
350217
/// This is slightly less strict than stanitise_name, presumably

src/blob/blob_tests.rs

Lines changed: 10 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -104,55 +104,15 @@ async fn test_create_long_names() {
104104
assert!(blobname.len() < 70);
105105
}
106106

107-
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
108-
async fn test_create_and_copy() {
109-
let t = TestContext::new().await;
110-
let src = t.dir.path().join("src");
111-
fs::write(&src, b"boo").await.unwrap();
112-
let blob = BlobObject::create_and_copy(&t, src.as_ref()).await.unwrap();
113-
assert_eq!(blob.as_name(), "$BLOBDIR/src");
114-
let data = fs::read(blob.to_abs_path()).await.unwrap();
115-
assert_eq!(data, b"boo");
116-
117-
let whoops = t.dir.path().join("whoops");
118-
assert!(BlobObject::create_and_copy(&t, whoops.as_ref())
119-
.await
120-
.is_err());
121-
let whoops = t.get_blobdir().join("whoops");
122-
assert!(!whoops.exists());
123-
}
124-
125-
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
126-
async fn test_create_from_path() {
127-
let t = TestContext::new().await;
128-
129-
let src_ext = t.dir.path().join("external");
130-
fs::write(&src_ext, b"boo").await.unwrap();
131-
let blob = BlobObject::new_from_path(&t, src_ext.as_ref())
132-
.await
133-
.unwrap();
134-
assert_eq!(blob.as_name(), "$BLOBDIR/external");
135-
let data = fs::read(blob.to_abs_path()).await.unwrap();
136-
assert_eq!(data, b"boo");
137-
138-
let src_int = t.get_blobdir().join("internal");
139-
fs::write(&src_int, b"boo").await.unwrap();
140-
let blob = BlobObject::new_from_path(&t, &src_int).await.unwrap();
141-
assert_eq!(blob.as_name(), "$BLOBDIR/internal");
142-
let data = fs::read(blob.to_abs_path()).await.unwrap();
143-
assert_eq!(data, b"boo");
144-
}
145107
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
146108
async fn test_create_from_name_long() {
147109
let t = TestContext::new().await;
148110
let src_ext = t.dir.path().join("autocrypt-setup-message-4137848473.html");
149111
fs::write(&src_ext, b"boo").await.unwrap();
150-
let blob = BlobObject::new_from_path(&t, src_ext.as_ref())
151-
.await
152-
.unwrap();
112+
let blob = BlobObject::create_and_deduplicate(&t, &src_ext, &src_ext).unwrap();
153113
assert_eq!(
154114
blob.as_name(),
155-
"$BLOBDIR/autocrypt-setup-message-4137848473.html"
115+
"$BLOBDIR/06f010b24d1efe57ffab44a8ad20c54.html"
156116
);
157117
}
158118

@@ -166,64 +126,6 @@ fn test_is_blob_name() {
166126
assert!(!BlobObject::is_acceptible_blob_name("foo\x00bar"));
167127
}
168128

169-
#[test]
170-
fn test_sanitise_name() {
171-
let (stem, ext) = BlobObject::sanitize_name_and_split_extension(
172-
"Я ЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯ.txt",
173-
);
174-
assert_eq!(ext, ".txt");
175-
assert!(!stem.is_empty());
176-
177-
// the extensions are kept together as between stem and extension a number may be added -
178-
// and `foo.tar.gz` should become `foo-1234.tar.gz` and not `foo.tar-1234.gz`
179-
let (stem, ext) = BlobObject::sanitize_name_and_split_extension("wot.tar.gz");
180-
assert_eq!(stem, "wot");
181-
assert_eq!(ext, ".tar.gz");
182-
183-
let (stem, ext) = BlobObject::sanitize_name_and_split_extension(".foo.bar");
184-
assert_eq!(stem, "file");
185-
assert_eq!(ext, ".foo.bar");
186-
187-
let (stem, ext) = BlobObject::sanitize_name_and_split_extension("foo?.bar");
188-
assert!(stem.contains("foo"));
189-
assert!(!stem.contains('?'));
190-
assert_eq!(ext, ".bar");
191-
192-
let (stem, ext) = BlobObject::sanitize_name_and_split_extension("no-extension");
193-
assert_eq!(stem, "no-extension");
194-
assert_eq!(ext, "");
195-
196-
let (stem, ext) =
197-
BlobObject::sanitize_name_and_split_extension("path/ignored\\this: is* forbidden?.c");
198-
assert_eq!(ext, ".c");
199-
assert!(!stem.contains("path"));
200-
assert!(!stem.contains("ignored"));
201-
assert!(stem.contains("this"));
202-
assert!(stem.contains("forbidden"));
203-
assert!(!stem.contains('/'));
204-
assert!(!stem.contains('\\'));
205-
assert!(!stem.contains(':'));
206-
assert!(!stem.contains('*'));
207-
assert!(!stem.contains('?'));
208-
209-
let (stem, ext) = BlobObject::sanitize_name_and_split_extension(
210-
"file.with_lots_of_characters_behind_point_and_double_ending.tar.gz",
211-
);
212-
assert_eq!(
213-
stem,
214-
"file.with_lots_of_characters_behind_point_and_double_ending"
215-
);
216-
assert_eq!(ext, ".tar.gz");
217-
218-
let (stem, ext) = BlobObject::sanitize_name_and_split_extension("a. tar.tar.gz");
219-
assert_eq!(stem, "a. tar");
220-
assert_eq!(ext, ".tar.gz");
221-
222-
let (stem, ext) = BlobObject::sanitize_name_and_split_extension("Guia_uso_GNB (v0.8).pdf");
223-
assert_eq!(stem, "Guia_uso_GNB (v0.8)");
224-
assert_eq!(ext, ".pdf");
225-
}
226-
227129
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
228130
async fn test_add_white_bg() {
229131
let t = TestContext::new().await;
@@ -236,7 +138,7 @@ async fn test_add_white_bg() {
236138
let avatar_src = t.dir.path().join("avatar.png");
237139
fs::write(&avatar_src, bytes).await.unwrap();
238140

239-
let mut blob = BlobObject::new_from_path(&t, &avatar_src).await.unwrap();
141+
let mut blob = BlobObject::create_and_deduplicate(&t, &avatar_src, &avatar_src).unwrap();
240142
let img_wh = 128;
241143
let maybe_sticker = &mut false;
242144
let strict_limits = true;
@@ -285,7 +187,7 @@ async fn test_selfavatar_outside_blobdir() {
285187
constants::BALANCED_AVATAR_SIZE,
286188
);
287189

288-
let mut blob = BlobObject::new_from_path(&t, avatar_path).await.unwrap();
190+
let mut blob = BlobObject::create_and_deduplicate(&t, avatar_path, avatar_path).unwrap();
289191
let maybe_sticker = &mut false;
290192
let strict_limits = true;
291193
blob.recode_to_size(&t, None, maybe_sticker, 1000, 3000, strict_limits)
@@ -643,9 +545,9 @@ impl SendImageCheckMediaquality<'_> {
643545
check_image_size(file_saved, compressed_width, compressed_height);
644546

645547
if original_width == compressed_width {
646-
assert_extension(&alice, alice_msg, extension).await;
548+
assert_extension(&alice, alice_msg, extension);
647549
} else {
648-
assert_extension(&alice, alice_msg, "jpg").await;
550+
assert_extension(&alice, alice_msg, "jpg");
649551
}
650552

651553
let bob_msg = bob.recv_msg(&sent).await;
@@ -668,16 +570,16 @@ impl SendImageCheckMediaquality<'_> {
668570
let img = check_image_size(file_saved, compressed_width, compressed_height);
669571

670572
if original_width == compressed_width {
671-
assert_extension(&bob, bob_msg, extension).await;
573+
assert_extension(&bob, bob_msg, extension);
672574
} else {
673-
assert_extension(&bob, bob_msg, "jpg").await;
575+
assert_extension(&bob, bob_msg, "jpg");
674576
}
675577

676578
Ok(img)
677579
}
678580
}
679581

680-
async fn assert_extension(context: &TestContext, msg: Message, extension: &str) {
582+
fn assert_extension(context: &TestContext, msg: Message, extension: &str) {
681583
assert!(msg
682584
.param
683585
.get(Param::File)
@@ -703,8 +605,7 @@ async fn assert_extension(context: &TestContext, msg: Message, extension: &str)
703605
);
704606
assert_eq!(
705607
msg.param
706-
.get_blob(Param::File, context)
707-
.await
608+
.get_file_blob(context)
708609
.unwrap()
709610
.unwrap()
710611
.suffix()

src/chat.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -904,8 +904,7 @@ impl ChatId {
904904
if msg.viewtype == Viewtype::Vcard {
905905
let blob = msg
906906
.param
907-
.get_blob(Param::File, context)
908-
.await?
907+
.get_file_blob(context)?
909908
.context("no file stored in params")?;
910909
msg.try_set_vcard(context, &blob.to_abs_path()).await?;
911910
}
@@ -2751,8 +2750,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
27512750
} else if msg.viewtype.has_file() {
27522751
let mut blob = msg
27532752
.param
2754-
.get_blob(Param::File, context)
2755-
.await?
2753+
.get_file_blob(context)?
27562754
.with_context(|| format!("attachment missing for message of type #{}", msg.viewtype))?;
27572755
let send_as_is = msg.viewtype == Viewtype::File;
27582756

0 commit comments

Comments
 (0)