mirror of https://github.com/djcb/mu.git
message: support cooked/raw filenames
Supported a "cooked" mode for attachment filenames, which gets rid of any unacceptable characters. Add "raw_filename" to get the filename as specified in the part. Update tests.
This commit is contained in:
parent
9f062ae482
commit
da8eee0e69
|
@ -19,6 +19,7 @@
|
|||
|
||||
|
||||
#include "mu-message-part.hh"
|
||||
#include "glibconfig.h"
|
||||
#include "mu-mime-object.hh"
|
||||
#include "utils/mu-utils.hh"
|
||||
|
||||
|
@ -36,7 +37,41 @@ MessagePart::~MessagePart() = default;
|
|||
|
||||
|
||||
Option<std::string>
|
||||
MessagePart::filename() const noexcept
|
||||
MessagePart::cooked_filename() const noexcept
|
||||
{
|
||||
// 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);
|
||||
|
||||
return clean;
|
||||
};
|
||||
|
||||
// a MimePart... use the name if there is one.
|
||||
if (mime_obj->is_part())
|
||||
return MimePart(*mime_obj).filename().map(cleanup);
|
||||
|
||||
// MimeMessagepart. Construct a name based on subject.
|
||||
if (mime_obj->is_message_part()) {
|
||||
auto msg{MimeMessagePart(*mime_obj).get_message()};
|
||||
return msg.subject()
|
||||
.map(cleanup)
|
||||
.value_or("no-subject") + ".eml";
|
||||
}
|
||||
|
||||
return Nothing;
|
||||
|
||||
}
|
||||
|
||||
Option<std::string>
|
||||
MessagePart::raw_filename() const noexcept
|
||||
{
|
||||
if (!mime_obj->is_part())
|
||||
return Nothing;
|
||||
|
@ -44,6 +79,8 @@ MessagePart::filename() const noexcept
|
|||
return MimePart(*mime_obj).filename();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Option<std::string>
|
||||
MessagePart::mime_type() const noexcept
|
||||
{
|
||||
|
@ -62,6 +99,15 @@ MessagePart::size() const noexcept
|
|||
return MimePart(*mime_obj).size();
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePart::is_attachment() const noexcept
|
||||
{
|
||||
if (!mime_obj->is_part())
|
||||
return false;
|
||||
else
|
||||
return MimePart(*mime_obj).is_attachment();
|
||||
}
|
||||
|
||||
|
||||
Option<std::string>
|
||||
MessagePart::to_string() const noexcept
|
||||
|
|
|
@ -53,11 +53,23 @@ public:
|
|||
~MessagePart();
|
||||
|
||||
/**
|
||||
* Filename for the mime-part
|
||||
* 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).
|
||||
*
|
||||
* @see raw_filename()
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
Option<std::string> cooked_filename() const noexcept;
|
||||
|
||||
/**
|
||||
* Name for the mime-part file, i.e., MimePart::filename
|
||||
*
|
||||
* @return the filename or Nothing if there is none
|
||||
*/
|
||||
Option<std::string> filename() const noexcept;
|
||||
Option<std::string> raw_filename() const noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* Mime-type for the mime-part (e.g. "text/plain")
|
||||
|
@ -73,6 +85,15 @@ public:
|
|||
*/
|
||||
size_t size() const noexcept;
|
||||
|
||||
/**
|
||||
* Does this part have an "attachment" disposition? Otherwise it is
|
||||
* "inline". Note that does *not* map 1:1 to a message's HasAttachment
|
||||
* flag.
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
bool is_attachment() const noexcept;
|
||||
|
||||
/**
|
||||
* Write (decoded) mime-part contents to string
|
||||
*
|
||||
|
@ -80,7 +101,6 @@ public:
|
|||
*/
|
||||
Option<std::string> to_string() const noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* Write (decoded) mime part to a file
|
||||
*
|
||||
|
|
|
@ -438,7 +438,7 @@ fill_document(Message::Private& priv)
|
|||
break;
|
||||
case Field::Id::File:
|
||||
for (auto&& part: priv.parts)
|
||||
doc.add(field.id, part.filename());
|
||||
doc.add(field.id, part.raw_filename());
|
||||
break;
|
||||
case Field::Id::Flags:
|
||||
doc.add(priv.flags);
|
||||
|
@ -684,7 +684,7 @@ Content-Description: test file 1
|
|||
MDAwAQID
|
||||
--=-=-=
|
||||
Content-Type: audio/ogg
|
||||
Content-Disposition: inline; filename=file-02.bin
|
||||
Content-Disposition: inline; filename=/tmp/file-02.bin
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
MDA0BQYH
|
||||
|
@ -721,27 +721,28 @@ R"(Hello,World!)");
|
|||
g_assert_cmpuint(message->parts().size(),==,4);
|
||||
{
|
||||
auto&& part{message->parts().at(0)};
|
||||
g_assert_false(!!part.filename());
|
||||
g_assert_false(!!part.raw_filename());
|
||||
assert_equal(part.mime_type().value(), "text/plain");
|
||||
assert_equal(part.to_string().value(), "Hello,");
|
||||
}
|
||||
{
|
||||
auto&& part{message->parts().at(1)};
|
||||
assert_equal(part.filename().value(), "file-01.bin");
|
||||
assert_equal(part.raw_filename().value(), "file-01.bin");
|
||||
assert_equal(part.mime_type().value(), "image/jpeg");
|
||||
// file consist of 6 bytes "000" 0x01,0x02.0x03.
|
||||
assert_equal(part.to_string().value(), "000\001\002\003");
|
||||
}
|
||||
{
|
||||
auto&& part{message->parts().at(2)};
|
||||
assert_equal(part.filename().value(), "file-02.bin");
|
||||
assert_equal(part.raw_filename().value(), "/tmp/file-02.bin");
|
||||
assert_equal(part.cooked_filename().value(), "tmp-file-02.bin");
|
||||
assert_equal(part.mime_type().value(), "audio/ogg");
|
||||
// file consist of the string "004" followed by 0x5,0x6,0x7.
|
||||
assert_equal(part.to_string().value(), "004\005\006\007");
|
||||
}
|
||||
{
|
||||
auto&& part{message->parts().at(3)};
|
||||
g_assert_false(!!part.filename());
|
||||
g_assert_false(!!part.raw_filename());
|
||||
g_assert_true(!!part.mime_type());
|
||||
assert_equal(part.mime_type().value(), "text/plain");
|
||||
assert_equal(part.to_string().value(), "World!");
|
||||
|
|
Loading…
Reference in New Issue