TNEF (winmail.dat) parsing support via libytnef
This commit is contained in:
parent
ef8f97628e
commit
18fcf0e18f
11 changed files with 1870 additions and 6 deletions
|
|
@ -27,7 +27,7 @@ variables:
|
|||
gtk3-devel iso-codes-devel json-glib-devel itstool
|
||||
libcanberra-devel libgee-devel libhandy-devel
|
||||
libnotify-devel libsecret-devel libunwind-devel
|
||||
libxml2-devel sqlite-devel webkitgtk4-devel
|
||||
libxml2-devel libytnef-devel sqlite-devel webkitgtk4-devel
|
||||
FEDORA_TEST_DEPS: Xvfb tar xz
|
||||
|
||||
# Ubuntu packages
|
||||
|
|
@ -39,7 +39,7 @@ variables:
|
|||
libhandy-0.0-dev libjson-glib-dev libmessaging-menu-dev
|
||||
libnotify-dev libsecret-1-dev libsqlite3-dev
|
||||
libunity-dev libunwind-dev libwebkit2gtk-4.0-dev
|
||||
libxml2-dev
|
||||
libxml2-dev libytnef0-dev
|
||||
UBUNTU_TEST_DEPS: xauth xvfb
|
||||
|
||||
fedora:
|
||||
|
|
|
|||
5
INSTALL
5
INSTALL
|
|
@ -45,7 +45,8 @@ Install them by running this command:
|
|||
glib2-devel gmime-devel gnome-online-accounts-devel gtk3-devel \
|
||||
iso-codes-devel json-glib-devel libcanberra-devel \
|
||||
libgee-devel libhandy-devel libnotify-devel libsecret-devel \
|
||||
libunwind-devel libxml2-devel sqlite-devel webkitgtk4-devel
|
||||
libunwind-devel libxml2-devel libytnef-devel sqlite-devel \
|
||||
webkitgtk4-devel
|
||||
|
||||
Installing dependencies on Ubuntu/Debian
|
||||
----------------------------------------
|
||||
|
|
@ -58,7 +59,7 @@ Install them by running this command:
|
|||
libglib2.0-dev libgmime-2.6-dev libgoa-1.0-dev libgtk-3-dev \
|
||||
libjson-glib-dev libhandy-dev libnotify-dev libsecret-1-dev \
|
||||
libsqlite3-dev libunwind-dev libwebkit2gtk-4.0-dev \
|
||||
libxml2-dev
|
||||
libxml2-dev libytnef0-dev
|
||||
|
||||
And for Ubuntu Unity integration:
|
||||
|
||||
|
|
|
|||
56
bindings/vapi/libytnef.vapi
Normal file
56
bindings/vapi/libytnef.vapi
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Oliver Giles <ohw.giles@gmail.com>
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
[CCode (cheader_filename = "ytnef.h")]
|
||||
namespace Ytnef {
|
||||
[CCode (cname ="variableLength", has_type_id = false)]
|
||||
public struct VariableLength {
|
||||
[CCode (array_length_cname = "size")]
|
||||
uint8[] data;
|
||||
}
|
||||
|
||||
[CCode (cname = "MAPI_UNDEFINED")]
|
||||
public VariableLength* MAPI_UNDEFINED;
|
||||
|
||||
[CCode (cname = "int", cprefix = "PT_", has_type_id = false)]
|
||||
public enum PropType {
|
||||
STRING8
|
||||
}
|
||||
|
||||
[CCode (cname = "int", cprefix = "PR_", has_type_id = false)]
|
||||
public enum PropID {
|
||||
DISPLAY_NAME,
|
||||
ATTACH_LONG_FILENAME
|
||||
}
|
||||
|
||||
[CCode (cname = "PROP_TAG")]
|
||||
public static int PROP_TAG(PropType type, PropID id);
|
||||
|
||||
[CCode (cname = "MAPIProps", has_type_id = false)]
|
||||
public struct MAPIProps {
|
||||
}
|
||||
|
||||
[CCode (cname = "Attachment", has_type_id = false)]
|
||||
public struct Attachment {
|
||||
VariableLength Title;
|
||||
VariableLength FileData;
|
||||
MAPIProps MAPI;
|
||||
Attachment? next;
|
||||
}
|
||||
|
||||
[CCode (cname = "TNEFStruct", destroy_function="TNEFFree", has_type_id = false)]
|
||||
public struct TNEFStruct {
|
||||
Attachment starting_attach;
|
||||
}
|
||||
|
||||
[CCode (cname = "TNEFParseMemory", has_type_id = false)]
|
||||
public static int ParseMemory(uint8[] data, out TNEFStruct tnef);
|
||||
|
||||
[CCode (cname = "MAPIFindProperty")]
|
||||
public static unowned VariableLength* MAPIFindProperty(MAPIProps MAPI, uint tag);
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +78,7 @@ libunwind_generic_dep = dependency(
|
|||
'libunwind-generic', version: '>= 1.1', required: not get_option('libunwind_optional')
|
||||
)
|
||||
libxml = dependency('libxml-2.0', version: '>= 2.7.8')
|
||||
libytnef = dependency('libytnef', version: '>= 1.9.3', required: get_option('tnef-support'))
|
||||
posix = valac.find_library('posix')
|
||||
webkit2gtk_web_extension = dependency('webkit2gtk-web-extension-4.0', version: '>=' + target_webkit)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ option('ref_tracking', type: 'boolean', value: false, description: 'Whether to u
|
|||
option('iso_639_xml', type: 'string', value: '', description: 'Full path to the ISO 639 XML file.')
|
||||
option('iso_3166_xml', type: 'string', value: '', description: 'Full path to the ISO 3166 XML file.')
|
||||
option('libunwind_optional', type: 'boolean', value: false, description: 'Determines if libunwind is required.')
|
||||
option('tnef-support', type: 'boolean', value: true, description: 'Whether to support TNEF attachments (requires libytnef).')
|
||||
|
|
|
|||
|
|
@ -205,6 +205,16 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "libytnef",
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/Yeraze/ytnef.git",
|
||||
"branch": "master"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "geary",
|
||||
"buildsystem": "meson",
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@ geary_engine_dependencies = [
|
|||
gmime,
|
||||
javascriptcoregtk,
|
||||
libxml,
|
||||
libytnef,
|
||||
posix,
|
||||
sqlite
|
||||
]
|
||||
|
|
@ -358,6 +359,13 @@ if libunwind_dep.found()
|
|||
]
|
||||
endif
|
||||
|
||||
if get_option('tnef-support')
|
||||
geary_engine_dependencies += libytnef
|
||||
geary_engine_vala_options += [
|
||||
'-D', 'WITH_TNEF_SUPPORT'
|
||||
]
|
||||
endif
|
||||
|
||||
geary_engine_lib = static_library('geary-engine',
|
||||
geary_engine_sources,
|
||||
dependencies: geary_engine_dependencies,
|
||||
|
|
|
|||
|
|
@ -880,17 +880,50 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
|
|||
Mime.ContentType content_type =
|
||||
part.get_effective_content_type();
|
||||
|
||||
// Skip text/plain and text/html parts that are INLINE
|
||||
// or UNSPECIFIED, as they will be included in the body
|
||||
#if WITH_TNEF_SUPPORT
|
||||
if (content_type.is_type("application", "vnd.ms-tnef")) {
|
||||
GMime.StreamMem stream = new GMime.StreamMem();
|
||||
((GMime.Part) root).get_content_object().write_to_stream(stream);
|
||||
ByteArray tnef_data = stream.get_byte_array();
|
||||
Ytnef.TNEFStruct tn;
|
||||
if (Ytnef.ParseMemory(tnef_data.data, out tn) == 0) {
|
||||
for (unowned Ytnef.Attachment? a = tn.starting_attach.next; a != null; a = a.next) {
|
||||
attachments.add(new Part(tnef_attachment_to_gmime_part(a)));
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif // WITH_TNEF_SUPPORT
|
||||
if (actual_disposition == Mime.DispositionType.ATTACHMENT ||
|
||||
(!content_type.is_type("text", "plain") &&
|
||||
!content_type.is_type("text", "html"))) {
|
||||
// Skip text/plain and text/html parts that are INLINE
|
||||
// or UNSPECIFIED, as they will be included in the body
|
||||
attachments.add(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_TNEF_SUPPORT
|
||||
private GMime.Part tnef_attachment_to_gmime_part(Ytnef.Attachment a) {
|
||||
Ytnef.VariableLength* filenameProp = Ytnef.MAPIFindProperty(a.MAPI, Ytnef.PROP_TAG(Ytnef.PropType.STRING8, Ytnef.PropID.ATTACH_LONG_FILENAME));
|
||||
if (filenameProp == Ytnef.MAPI_UNDEFINED) {
|
||||
filenameProp = Ytnef.MAPIFindProperty(a.MAPI, Ytnef.PROP_TAG(Ytnef.PropType.STRING8, Ytnef.PropID.DISPLAY_NAME));
|
||||
if (filenameProp == Ytnef.MAPI_UNDEFINED) {
|
||||
filenameProp = &a.Title;
|
||||
}
|
||||
}
|
||||
string filename = (string) filenameProp.data;
|
||||
uint8[] data = Bytes.unref_to_data(new Bytes(a.FileData.data));
|
||||
|
||||
GMime.Part part = new GMime.Part();
|
||||
part.set_filename(filename);
|
||||
part.set_content_type(new GMime.ContentType.from_string(GLib.ContentType.guess(filename, data, null)));
|
||||
part.set_content_object(new GMime.DataWrapper.with_stream(new GMime.StreamMem.with_buffer(data), GMime.ContentEncoding.BINARY));
|
||||
return part;
|
||||
}
|
||||
#endif
|
||||
|
||||
public Gee.List<Geary.RFC822.Message> get_sub_messages() {
|
||||
Gee.List<Geary.RFC822.Message> messages = new Gee.ArrayList<Geary.RFC822.Message>();
|
||||
find_sub_messages(messages, message.get_mime_part());
|
||||
|
|
|
|||
1743
test/data/basic-multipart-tnef.eml
Normal file
1743
test/data/basic-multipart-tnef.eml
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -4,6 +4,7 @@
|
|||
<file>basic-text-plain.eml</file>
|
||||
<file>basic-text-html.eml</file>
|
||||
<file>basic-multipart-alternative.eml</file>
|
||||
<file>basic-multipart-tnef.eml</file>
|
||||
<file>geary-0.6-db.tar.xz</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ class Geary.RFC822.MessageTest : TestCase {
|
|||
private const string BASIC_TEXT_HTML = "basic-text-html.eml";
|
||||
private const string BASIC_MULTIPART_ALTERNATIVE =
|
||||
"basic-multipart-alternative.eml";
|
||||
private const string BASIC_MULTIPART_TNEF = "basic-multipart-tnef.eml";
|
||||
|
||||
private const string HTML_CONVERSION_TEMPLATE =
|
||||
"<div class=\"plaintext\" style=\"white-space: pre-wrap;\">%s</div>";
|
||||
|
|
@ -38,6 +39,7 @@ This is the second line.
|
|||
add_test("text_plain_as_html", text_plain_as_html);
|
||||
add_test("text_html_as_html", text_html_as_html);
|
||||
add_test("text_html_as_plain", text_html_as_plain);
|
||||
add_test("tnef_extract_attachments", tnef_extract_attachments);
|
||||
add_test("multipart_alternative_as_plain",
|
||||
multipart_alternative_as_plain);
|
||||
add_test("multipart_alternative_as_converted_html",
|
||||
|
|
@ -124,6 +126,14 @@ This is the second line.
|
|||
assert_string(BASIC_HTML_BODY, test.get_html_body(null));
|
||||
}
|
||||
|
||||
public void tnef_extract_attachments() throws Error {
|
||||
Message test = resource_to_message(BASIC_MULTIPART_TNEF);
|
||||
Gee.List<Part> attachments = test.get_attachments();
|
||||
assert_true(attachments.size == 2);
|
||||
assert_true(attachments[0].get_clean_filename() == "zappa_av1.jpg");
|
||||
assert_true(attachments[1].get_clean_filename() == "bookmark.htm");
|
||||
}
|
||||
|
||||
public void multipart_alternative_as_plain() throws Error {
|
||||
Message test = resource_to_message(BASIC_MULTIPART_ALTERNATIVE);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue