mu-message-part: rework cooked_filename a bit
Improve working, add a 'minimal' mode, and add some unit tests.
This commit is contained in:
@ -48,48 +48,55 @@ lib_mu_message_dep = declare_dependency(
|
||||
|
||||
test('test-contact',
|
||||
executable('test-contact',
|
||||
'mu-contact.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
'mu-contact.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
|
||||
test('test-document',
|
||||
executable('test-document',
|
||||
'mu-document.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
'mu-document.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
|
||||
test('test-fields',
|
||||
executable('test-fields',
|
||||
'mu-fields.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
'mu-fields.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
|
||||
test('test-flags',
|
||||
executable('test-flags',
|
||||
'mu-flags.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
'mu-flags.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
|
||||
test('test-message',
|
||||
executable('test-message',
|
||||
'test-mu-message.cc',
|
||||
install: false,
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
'test-mu-message.cc',
|
||||
install: false,
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
|
||||
test('test-priority',
|
||||
executable('test-priority',
|
||||
'mu-priority.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
'mu-priority.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||
|
||||
test('test-message-file',
|
||||
executable('test-message-file',
|
||||
'mu-message-file.cc',
|
||||
install: false,
|
||||
'mu-message-file.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, lib_mu_message_dep]))
|
||||
dependencies: [glib_dep, lib_mu_message_dep]))
|
||||
|
||||
test('test-message-part',
|
||||
executable('test-message-part',
|
||||
'mu-message-part.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, lib_mu_message_dep]))
|
||||
|
||||
@ -42,27 +42,48 @@ MessagePart::mime_object() const noexcept
|
||||
return *mime_obj;
|
||||
}
|
||||
|
||||
Option<std::string>
|
||||
MessagePart::cooked_filename() const noexcept
|
||||
static std::string
|
||||
cook(const std::string& fname, const std::vector<char>& forbidden)
|
||||
{
|
||||
// make a bit more pallatble.
|
||||
auto cleanup = [](const std::string& name)->std::string {
|
||||
std::string clean;
|
||||
clean.reserve(name.length());
|
||||
for (auto& c: name) {
|
||||
auto taboo{(::iscntrl(c) || c == G_DIR_SEPARATOR ||
|
||||
c == ' ' || c == '\\' || c == ':')};
|
||||
clean += (taboo ? '-' : c);
|
||||
}
|
||||
if (clean.size() > 1 && clean[0] == '-')
|
||||
clean.erase(0, 1);
|
||||
std::string clean;
|
||||
clean.reserve(fname.length());
|
||||
|
||||
for (auto& c: to_string_gchar(g_path_get_basename(fname.c_str())))
|
||||
if (seq_some(forbidden,[&](char fc){return ::iscntrl(c) || c == fc;}))
|
||||
clean += '-';
|
||||
else
|
||||
clean += c;
|
||||
|
||||
if (clean[0] == '.' && (clean == "." || clean == ".."))
|
||||
return "-";
|
||||
else
|
||||
return clean;
|
||||
};
|
||||
}
|
||||
|
||||
static std::string
|
||||
cook_minimal(const std::string& fname)
|
||||
{
|
||||
return cook(fname, { '/' });
|
||||
}
|
||||
|
||||
static std::string
|
||||
cook_full(const std::string& fname)
|
||||
{
|
||||
auto cooked = cook(fname, { '/', ' ', '\\', ':' });
|
||||
if (cooked.size() > 1 && cooked[0] == '-')
|
||||
cooked.erase(0, 1);
|
||||
|
||||
return cooked;
|
||||
}
|
||||
|
||||
Option<std::string>
|
||||
MessagePart::cooked_filename(bool minimal) const noexcept
|
||||
{
|
||||
auto&& cooker{minimal ? cook_minimal : cook_full};
|
||||
|
||||
// a MimePart... use the name if there is one.
|
||||
if (mime_object().is_part())
|
||||
return MimePart{mime_object()}.filename().map(cleanup);
|
||||
return MimePart{mime_object()}.filename().map(cooker);
|
||||
|
||||
// MimeMessagepart. Construct a name based on subject.
|
||||
if (mime_object().is_message_part()) {
|
||||
@ -71,7 +92,7 @@ MessagePart::cooked_filename() const noexcept
|
||||
return Nothing;
|
||||
else
|
||||
return msg->subject()
|
||||
.map(cleanup)
|
||||
.map(cooker)
|
||||
.value_or("no-subject") + ".eml";
|
||||
}
|
||||
|
||||
@ -186,3 +207,48 @@ MessagePart::looks_like_attachment() const noexcept
|
||||
// otherwise, rely on the disposition
|
||||
return is_attachment();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef BUILD_TESTS
|
||||
#include "utils/mu-test-utils.hh"
|
||||
|
||||
static void
|
||||
test_cooked_full()
|
||||
{
|
||||
std::array<std::pair<std::string, std::string>, 4> cases = {{
|
||||
{ "/hello/world/foo", "foo" },
|
||||
{ "foo:/\n/bar", "bar"},
|
||||
{ "Aap Noot Mies", "Aap-Noot-Mies"},
|
||||
{ "..", "-"}
|
||||
}};
|
||||
|
||||
for (auto&& test: cases)
|
||||
assert_equal(cook_full(test.first), test.second);
|
||||
}
|
||||
|
||||
static void
|
||||
test_cooked_minimal()
|
||||
{
|
||||
std::array<std::pair<std::string, std::string>, 4> cases = {{
|
||||
{ "/hello/world/foo", "foo" },
|
||||
{ "foo:/\n/bar", "bar"},
|
||||
{ "Aap Noot Mies.doc", "Aap Noot Mies.doc"},
|
||||
{ "..", "-"}
|
||||
}};
|
||||
|
||||
for (auto&& test: cases)
|
||||
assert_equal(cook_minimal(test.first), test.second);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
mu_test_init(&argc, &argv);
|
||||
|
||||
g_test_add_func("/message/message-part/cooked-full", test_cooked_full);
|
||||
g_test_add_func("/message/message-part/cooked-minimal", test_cooked_minimal);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
#endif /*BUILD_TESTS*/
|
||||
|
||||
@ -65,13 +65,16 @@ public:
|
||||
/**
|
||||
* Filename for the mime-part file. This is a "cooked" filename with
|
||||
* unallowed characters removed. If there's no filename specified,
|
||||
* construct one (such as in the case of MimeMessagePart).
|
||||
* construct one (such as in the case of a MimeMessagePart).
|
||||
*
|
||||
* @param minimal if true, only perform *minimal* cookiing, where we
|
||||
* only remove forward-slashes.
|
||||
*
|
||||
* @see raw_filename()
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
Option<std::string> cooked_filename() const noexcept;
|
||||
Option<std::string> cooked_filename(bool minimal=false) const noexcept;
|
||||
|
||||
/**
|
||||
* Name for the mime-part file, i.e., MimePart::filename
|
||||
|
||||
@ -235,7 +235,7 @@ World!
|
||||
{
|
||||
auto&& part{message->parts().at(2)};
|
||||
assert_equal(part.raw_filename().value(), "/tmp/file-02.bin");
|
||||
assert_equal(part.cooked_filename().value(), "tmp-file-02.bin");
|
||||
assert_equal(part.cooked_filename().value(), "file-02.bin");
|
||||
assert_equal(part.mime_type().value(), "audio/ogg");
|
||||
// file consistso of 4 bytes 4..7
|
||||
assert_equal(part.to_string().value(), "\004\005\006\007");
|
||||
|
||||
Reference in New Issue
Block a user