736 lines
24 KiB
C
736 lines
24 KiB
C
/******************************************************************************
|
|
* @file test_eml_mime_extractor.c
|
|
* @brief Tests synthétiques de l'extracteur MIME récursif.
|
|
******************************************************************************/
|
|
#include "core/eml_mime_extractor.h"
|
|
|
|
#include <glib.h>
|
|
#include <glib/gstdio.h>
|
|
|
|
typedef struct
|
|
{
|
|
char *directory;
|
|
char *eml_path;
|
|
char *output_directory;
|
|
} MimeFixture;
|
|
|
|
static MimeFixture *mime_fixture_new(const char *content)
|
|
{
|
|
GError *error = NULL;
|
|
MimeFixture *fixture = g_new0(MimeFixture, 1);
|
|
fixture->directory = g_dir_make_tmp("labfy-mime-XXXXXX", &error);
|
|
g_assert_no_error(error);
|
|
fixture->eml_path = g_build_filename(
|
|
fixture->directory,
|
|
"synthetic.eml",
|
|
NULL
|
|
);
|
|
fixture->output_directory = g_build_filename(
|
|
fixture->directory,
|
|
"derived",
|
|
NULL
|
|
);
|
|
g_assert_true(g_file_set_contents(
|
|
fixture->eml_path,
|
|
content,
|
|
-1,
|
|
&error
|
|
));
|
|
g_assert_no_error(error);
|
|
return fixture;
|
|
}
|
|
|
|
static void mime_fixture_free(MimeFixture *fixture)
|
|
{
|
|
GDir *directory = g_dir_open(fixture->output_directory, 0, NULL);
|
|
if (directory != NULL)
|
|
{
|
|
const char *name = NULL;
|
|
while ((name = g_dir_read_name(directory)) != NULL)
|
|
{
|
|
char *path = g_build_filename(
|
|
fixture->output_directory,
|
|
name,
|
|
NULL
|
|
);
|
|
g_assert_cmpint(g_remove(path), ==, 0);
|
|
g_free(path);
|
|
}
|
|
g_dir_close(directory);
|
|
g_assert_cmpint(g_rmdir(fixture->output_directory), ==, 0);
|
|
}
|
|
g_assert_cmpint(g_remove(fixture->eml_path), ==, 0);
|
|
g_assert_cmpint(g_rmdir(fixture->directory), ==, 0);
|
|
g_free(fixture->output_directory);
|
|
g_free(fixture->eml_path);
|
|
g_free(fixture->directory);
|
|
g_free(fixture);
|
|
}
|
|
|
|
static char *attachment_contents(EmlAttachment *attachment)
|
|
{
|
|
char *contents = NULL;
|
|
GError *error = NULL;
|
|
g_assert_true(g_file_get_contents(
|
|
attachment->extracted_path,
|
|
&contents,
|
|
NULL,
|
|
&error
|
|
));
|
|
g_assert_no_error(error);
|
|
return contents;
|
|
}
|
|
|
|
static void test_nested_order_and_encodings(void)
|
|
{
|
|
static const char eml[] =
|
|
"MIME-Version: 1.0\r\n"
|
|
"Content-Type: multipart/mixed; boundary=outer\r\n\r\n"
|
|
"--outer\r\nContent-Type: text/plain\r\n\r\nbody\r\n"
|
|
"--outer\r\n"
|
|
"Content-Type: multipart/related; boundary=inner\r\n\r\n"
|
|
"--inner\r\nContent-Type: image/png\r\n"
|
|
"Content-Disposition: inline\r\n"
|
|
"Content-ID: <synthetic-image@test.invalid>\r\n"
|
|
"Content-Transfer-Encoding: base64\r\n\r\n"
|
|
"UE5H\r\n"
|
|
"--inner\r\nContent-Type: text/plain\r\n"
|
|
"Content-Disposition: attachment;\r\n"
|
|
" filename*0*=UTF-8''rapport%20;\r\n"
|
|
" filename*1*=synth%C3%A9tique.txt\r\n"
|
|
"Content-Transfer-Encoding: quoted-printable\r\n\r\n"
|
|
"ligne=20une=\r\nligne=20deux\r\n"
|
|
"--inner--\r\n"
|
|
"--outer\r\nContent-Type: text/plain\r\n"
|
|
"Content-Disposition: attachment;\r\n"
|
|
" filename=\"=?UTF-8?Q?troisi=C3=A8me.txt?=\"\r\n\r\n"
|
|
"third\r\n--outer--\r\n";
|
|
MimeFixture *fixture = mime_fixture_new(eml);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_nonnull(result);
|
|
g_assert_cmpuint(result->attachments->len, ==, 3);
|
|
|
|
EmlAttachment *first = g_ptr_array_index(result->attachments, 0);
|
|
EmlAttachment *second = g_ptr_array_index(result->attachments, 1);
|
|
EmlAttachment *third = g_ptr_array_index(result->attachments, 2);
|
|
g_assert_cmpstr(first->part_index, ==, "1.2.1");
|
|
g_assert_true(first->is_inline);
|
|
g_assert_false(first->is_attachment);
|
|
g_assert_cmpstr(first->normalized_content_id, ==,
|
|
"synthetic-image@test.invalid");
|
|
g_assert_cmpstr(second->part_index, ==, "1.2.2");
|
|
g_assert_cmpstr(second->sanitized_filename, ==,
|
|
"rapport synthétique.txt");
|
|
g_assert_cmpstr(third->part_index, ==, "1.3");
|
|
g_assert_cmpstr(third->sanitized_filename, ==, "troisième.txt");
|
|
|
|
char *first_content = attachment_contents(first);
|
|
char *second_content = attachment_contents(second);
|
|
char *third_content = attachment_contents(third);
|
|
g_assert_cmpstr(first_content, ==, "PNG");
|
|
g_assert_cmpstr(second_content, ==, "ligne uneligne deux");
|
|
g_assert_cmpstr(third_content, ==, "third");
|
|
g_assert_nonnull(first->sha256);
|
|
g_assert_nonnull(first->detected_mime);
|
|
g_free(first_content);
|
|
g_free(second_content);
|
|
g_free(third_content);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
}
|
|
|
|
static void test_three_levels_and_message(void)
|
|
{
|
|
static const char eml[] =
|
|
"Content-Type: multipart/mixed; boundary=a\r\n\r\n"
|
|
"--a\r\nContent-Type: multipart/alternative; boundary=b\r\n\r\n"
|
|
"--b\r\nContent-Type: multipart/related; boundary=c\r\n\r\n"
|
|
"--c\r\nContent-Type: text/plain; name=four.txt\r\n"
|
|
"Content-Disposition: attachment\r\n\r\nfour\r\n--c--\r\n"
|
|
"--b--\r\n--a\r\nContent-Type: message/rfc822\r\n\r\n"
|
|
"Content-Type: text/plain; name=inside.txt\r\n"
|
|
"Content-Disposition: attachment\r\n\r\ninside\r\n"
|
|
"--a--\r\n";
|
|
MimeFixture *fixture = mime_fixture_new(eml);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->attachments->len, ==, 2);
|
|
EmlAttachment *first = g_ptr_array_index(result->attachments, 0);
|
|
EmlAttachment *second = g_ptr_array_index(result->attachments, 1);
|
|
g_assert_cmpstr(first->part_index, ==, "1.1.1.1");
|
|
g_assert_cmpstr(second->part_index, ==, "1.2.1");
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
}
|
|
|
|
static void test_rfc2047_and_rfc2231_priorities(void)
|
|
{
|
|
static const char eml[] =
|
|
"Content-Type: multipart/mixed; boundary=x\r\n\r\n"
|
|
"--x\r\nContent-Type: text/plain; name=fallback.txt\r\n"
|
|
"Content-Disposition: attachment; filename=plain.txt;\r\n"
|
|
" filename*=ISO-8859-1''caf%E9.txt\r\n\r\none\r\n"
|
|
"--x\r\nContent-Type: text/plain\r\n"
|
|
"Content-Disposition: attachment;\r\n"
|
|
" filename=\"ASCII =?UTF-8?B?w6l0dWRl?=.txt\"\r\n\r\ntwo\r\n"
|
|
"--x\r\nContent-Type: text/plain\r\n"
|
|
"Content-Disposition: attachment;\r\n"
|
|
" filename*0=continued-; filename*1=name.txt\r\n\r\nthree\r\n"
|
|
"--x\r\nContent-Type: text/plain;\r\n"
|
|
" name*=UTF-8''type%20fallback.txt\r\n"
|
|
"Content-Disposition: inline\r\n\r\nfour\r\n"
|
|
"--x--\r\n";
|
|
MimeFixture *fixture = mime_fixture_new(eml);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->attachments->len, ==, 4);
|
|
g_assert_cmpstr(
|
|
((EmlAttachment *) g_ptr_array_index(
|
|
result->attachments, 0))->sanitized_filename,
|
|
==,
|
|
"café.txt"
|
|
);
|
|
g_assert_cmpstr(
|
|
((EmlAttachment *) g_ptr_array_index(
|
|
result->attachments, 1))->sanitized_filename,
|
|
==,
|
|
"ASCII étude.txt"
|
|
);
|
|
g_assert_cmpstr(
|
|
((EmlAttachment *) g_ptr_array_index(
|
|
result->attachments, 2))->sanitized_filename,
|
|
==,
|
|
"continued-name.txt"
|
|
);
|
|
g_assert_cmpstr(
|
|
((EmlAttachment *) g_ptr_array_index(
|
|
result->attachments, 3))->sanitized_filename,
|
|
==,
|
|
"type fallback.txt"
|
|
);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
}
|
|
|
|
static void test_invalid_encodings_are_warnings(void)
|
|
{
|
|
static const char eml[] =
|
|
"Content-Type: multipart/mixed; boundary=x\r\n\r\n"
|
|
"--x\r\nContent-Type: text/plain; name=a.txt\r\n"
|
|
"Content-Disposition: attachment\r\n"
|
|
"Content-Transfer-Encoding: base64\r\n\r\nA!AA\r\n"
|
|
"--x\r\nContent-Type: text/plain; name=b.txt\r\n"
|
|
"Content-Disposition: attachment\r\n"
|
|
"Content-Transfer-Encoding: quoted-printable\r\n\r\nbad=QZ\r\n"
|
|
"--x\r\nContent-Type: text/plain; name=c.txt\r\n"
|
|
"Content-Disposition: attachment\r\n"
|
|
"Content-Transfer-Encoding: synthetic\r\n\r\nbad\r\n"
|
|
"--x--\r\n";
|
|
MimeFixture *fixture = mime_fixture_new(eml);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->attachments->len, ==, 0);
|
|
g_assert_cmpuint(result->warnings->len, ==, 3);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
}
|
|
|
|
static void test_paths_and_collisions(void)
|
|
{
|
|
static const char eml[] =
|
|
"Content-Type: multipart/mixed; boundary=x\r\n\r\n"
|
|
"--x\r\nContent-Type: text/plain\r\n"
|
|
"Content-Disposition: attachment; filename=\"../same.txt\"\r\n\r\n1\r\n"
|
|
"--x\r\nContent-Type: text/plain\r\n"
|
|
"Content-Disposition: attachment; filename=\"C:\\\\same.txt\"\r\n\r\n2\r\n"
|
|
"--x\r\nContent-Type: text/plain\r\n"
|
|
"Content-Disposition: attachment; filename=\"/same.txt\"\r\n\r\n3\r\n"
|
|
"--x\r\nContent-Type: text/plain\r\n"
|
|
"Content-Disposition: attachment; filename=\"../same.txt\"\r\n\r\n4\r\n"
|
|
"--x--\r\n";
|
|
MimeFixture *fixture = mime_fixture_new(eml);
|
|
GError *error = NULL;
|
|
g_assert_cmpint(g_mkdir_with_parents(
|
|
fixture->output_directory, 0755), ==, 0);
|
|
char *existing = g_build_filename(
|
|
fixture->output_directory,
|
|
"___same.txt",
|
|
NULL
|
|
);
|
|
g_assert_true(g_file_set_contents(existing, "existing", -1, &error));
|
|
g_assert_no_error(error);
|
|
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->attachments->len, ==, 4);
|
|
for (guint index = 0; index < result->attachments->len; index++)
|
|
{
|
|
EmlAttachment *attachment = g_ptr_array_index(
|
|
result->attachments,
|
|
index
|
|
);
|
|
g_assert_null(strchr(attachment->sanitized_filename, '/'));
|
|
g_assert_null(strchr(attachment->sanitized_filename, '\\'));
|
|
}
|
|
char *existing_content = NULL;
|
|
g_assert_true(g_file_get_contents(
|
|
existing, &existing_content, NULL, &error));
|
|
g_assert_no_error(error);
|
|
g_assert_cmpstr(existing_content, ==, "existing");
|
|
g_free(existing_content);
|
|
g_free(existing);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
}
|
|
|
|
static void test_malformed_and_incomplete_rfc2231(void)
|
|
{
|
|
static const char eml[] =
|
|
"Content-Type: multipart/mixed; boundary=x\r\n\r\n"
|
|
"--x\r\nContent-Type: text/plain\r\n"
|
|
"Content-Disposition: attachment; filename*0*=UTF-8''bad;\r\n"
|
|
" filename*2*=gap.txt; filename=fallback.txt\r\n\r\nok\r\n"
|
|
"--x\r\nContent-Type: text/plain\r\n"
|
|
"Content-Disposition: attachment; filename*0*=UTF-8''one;\r\n"
|
|
" filename*0*=duplicate; filename=duplicate-fallback.txt\r\n\r\ntwo\r\n";
|
|
MimeFixture *fixture = mime_fixture_new(eml);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->attachments->len, ==, 2);
|
|
EmlAttachment *attachment = g_ptr_array_index(result->attachments, 0);
|
|
g_assert_cmpstr(attachment->sanitized_filename, ==, "fallback.txt");
|
|
attachment = g_ptr_array_index(result->attachments, 1);
|
|
g_assert_cmpstr(
|
|
attachment->sanitized_filename,
|
|
==,
|
|
"duplicate-fallback.txt"
|
|
);
|
|
g_assert_cmpuint(result->warnings->len, >=, 1);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
}
|
|
|
|
static void test_missing_boundary(void)
|
|
{
|
|
static const char eml[] =
|
|
"Content-Type: multipart/mixed\r\n\r\nnot structured";
|
|
MimeFixture *fixture = mime_fixture_new(eml);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->attachments->len, ==, 0);
|
|
g_assert_cmpuint(result->warnings->len, ==, 1);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
}
|
|
|
|
static void test_cancelled_before_extraction(void)
|
|
{
|
|
static const char eml[] =
|
|
"Content-Type: text/plain; name=a.txt\r\n"
|
|
"Content-Disposition: attachment\r\n\r\ncontent";
|
|
MimeFixture *fixture = mime_fixture_new(eml);
|
|
GCancellable *cancellable = g_cancellable_new();
|
|
GError *error = NULL;
|
|
g_cancellable_cancel(cancellable);
|
|
EmlMimeResult *result = eml_mime_extract_attachments_cancellable(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
cancellable,
|
|
&error
|
|
);
|
|
g_assert_null(result);
|
|
g_assert_error(error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
|
|
g_clear_error(&error);
|
|
g_object_unref(cancellable);
|
|
mime_fixture_free(fixture);
|
|
}
|
|
|
|
static void test_source_unchanged(void)
|
|
{
|
|
static const char eml[] =
|
|
"Content-Type: text/plain; name=a.txt\r\n"
|
|
"Content-Disposition: attachment\r\n\r\nimmutable";
|
|
MimeFixture *fixture = mime_fixture_new(eml);
|
|
char *before = NULL;
|
|
char *after = NULL;
|
|
GError *error = NULL;
|
|
g_assert_true(g_file_get_contents(
|
|
fixture->eml_path, &before, NULL, &error));
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_true(g_file_get_contents(
|
|
fixture->eml_path, &after, NULL, &error));
|
|
g_assert_no_error(error);
|
|
g_assert_cmpstr(before, ==, after);
|
|
g_free(before);
|
|
g_free(after);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
}
|
|
|
|
static void test_filename_sanitizer_limits(void)
|
|
{
|
|
char *empty = eml_mime_sanitize_filename(" ");
|
|
char *unix_path = eml_mime_sanitize_filename("../../absolute/test");
|
|
char *windows_path = eml_mime_sanitize_filename("C:\\temp\\test");
|
|
char *long_name = g_strnfill(
|
|
EML_MIME_MAX_FILENAME_LENGTH + 100,
|
|
'a'
|
|
);
|
|
char *shortened = eml_mime_sanitize_filename(long_name);
|
|
|
|
g_assert_cmpstr(empty, ==, "attachment.bin");
|
|
g_assert_null(strchr(unix_path, '/'));
|
|
g_assert_null(strchr(windows_path, '\\'));
|
|
g_assert_cmpuint(
|
|
strlen(shortened),
|
|
<=,
|
|
EML_MIME_MAX_FILENAME_LENGTH
|
|
);
|
|
g_free(empty);
|
|
g_free(unix_path);
|
|
g_free(windows_path);
|
|
g_free(long_name);
|
|
g_free(shortened);
|
|
}
|
|
|
|
static void test_part_count_limit(void)
|
|
{
|
|
GString *eml = g_string_new(
|
|
"Content-Type: multipart/mixed; boundary=x\r\n\r\n"
|
|
);
|
|
for (guint index = 0; index < EML_MIME_MAX_PARTS + 4; index++)
|
|
g_string_append_printf(
|
|
eml,
|
|
"--x\r\nContent-Type: text/plain; name=p%u.txt\r\n"
|
|
"Content-Disposition: attachment\r\n\r\n%u\r\n",
|
|
index,
|
|
index
|
|
);
|
|
g_string_append(eml, "--x--\r\n");
|
|
MimeFixture *fixture = mime_fixture_new(eml->str);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(
|
|
result->attachments->len,
|
|
==,
|
|
EML_MIME_MAX_PARTS - 1
|
|
);
|
|
g_assert_cmpuint(result->warnings->len, >, 0);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
g_string_free(eml, TRUE);
|
|
}
|
|
|
|
static void test_depth_limit(void)
|
|
{
|
|
GString *eml = g_string_new(NULL);
|
|
for (guint depth = 1; depth <= EML_MIME_MAX_DEPTH + 1; depth++)
|
|
g_string_append_printf(
|
|
eml,
|
|
"Content-Type: multipart/mixed; boundary=b%u\r\n\r\n--b%u\r\n",
|
|
depth,
|
|
depth
|
|
);
|
|
g_string_append(
|
|
eml,
|
|
"Content-Type: text/plain; name=too-deep.txt\r\n"
|
|
"Content-Disposition: attachment\r\n\r\ndeep\r\n"
|
|
);
|
|
for (gint depth = (gint) EML_MIME_MAX_DEPTH + 1; depth >= 1; depth--)
|
|
g_string_append_printf(eml, "--b%d--\r\n", depth);
|
|
|
|
MimeFixture *fixture = mime_fixture_new(eml->str);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->attachments->len, ==, 0);
|
|
g_assert_cmpuint(result->warnings->len, >, 0);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
g_string_free(eml, TRUE);
|
|
}
|
|
|
|
static void test_part_size_and_no_temporary_file(void)
|
|
{
|
|
GString *eml = g_string_new(
|
|
"Content-Type: text/plain; name=large.txt\r\n"
|
|
"Content-Disposition: attachment\r\n\r\n"
|
|
);
|
|
char *large_content = g_strnfill(
|
|
EML_MIME_MAX_PART_DECODED_SIZE + 1,
|
|
'x'
|
|
);
|
|
g_string_append_len(
|
|
eml,
|
|
large_content,
|
|
EML_MIME_MAX_PART_DECODED_SIZE + 1
|
|
);
|
|
g_free(large_content);
|
|
MimeFixture *fixture = mime_fixture_new(eml->str);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->attachments->len, ==, 0);
|
|
g_assert_cmpuint(result->warnings->len, ==, 1);
|
|
GDir *directory = g_dir_open(fixture->output_directory, 0, &error);
|
|
g_assert_no_error(error);
|
|
g_assert_null(g_dir_read_name(directory));
|
|
g_dir_close(directory);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
g_string_free(eml, TRUE);
|
|
}
|
|
|
|
static void test_empty_base64_and_malformed_header(void)
|
|
{
|
|
static const char valid[] =
|
|
"Content-Type: application/octet-stream; name=empty.bin\r\n"
|
|
"Content-Disposition: attachment\r\n"
|
|
"Content-Transfer-Encoding: base64\r\n\r\n";
|
|
MimeFixture *fixture = mime_fixture_new(valid);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->attachments->len, ==, 1);
|
|
EmlAttachment *attachment = g_ptr_array_index(result->attachments, 0);
|
|
g_assert_cmpuint(attachment->decoded_size, ==, 0);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
|
|
fixture = mime_fixture_new("Malformed header\r\n\r\nbody");
|
|
result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->warnings->len, ==, 1);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
GCancellable *cancellable;
|
|
const char *output_directory;
|
|
} CancellationData;
|
|
|
|
static gpointer cancel_when_extraction_starts(gpointer user_data)
|
|
{
|
|
CancellationData *data = user_data;
|
|
while (!g_file_test(data->output_directory, G_FILE_TEST_IS_DIR))
|
|
g_thread_yield();
|
|
g_cancellable_cancel(data->cancellable);
|
|
return NULL;
|
|
}
|
|
|
|
static void test_cancelled_during_extraction(void)
|
|
{
|
|
GString *eml = g_string_new(
|
|
"Content-Type: text/plain; name=a.txt\r\n"
|
|
"Content-Disposition: attachment\r\n\r\n"
|
|
);
|
|
char *large_content = g_strnfill(
|
|
EML_MIME_MAX_PART_DECODED_SIZE,
|
|
'c'
|
|
);
|
|
g_string_append_len(
|
|
eml,
|
|
large_content,
|
|
EML_MIME_MAX_PART_DECODED_SIZE
|
|
);
|
|
g_free(large_content);
|
|
MimeFixture *fixture = mime_fixture_new(eml->str);
|
|
GCancellable *cancellable = g_cancellable_new();
|
|
CancellationData data = {
|
|
.cancellable = cancellable,
|
|
.output_directory = fixture->output_directory
|
|
};
|
|
GThread *thread = g_thread_new(
|
|
"mime-cancel",
|
|
cancel_when_extraction_starts,
|
|
&data
|
|
);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments_cancellable(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
cancellable,
|
|
&error
|
|
);
|
|
g_thread_join(thread);
|
|
g_assert_null(result);
|
|
g_assert_error(error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
|
|
g_clear_error(&error);
|
|
GDir *directory = g_dir_open(fixture->output_directory, 0, &error);
|
|
g_assert_no_error(error);
|
|
g_assert_null(g_dir_read_name(directory));
|
|
g_dir_close(directory);
|
|
g_object_unref(cancellable);
|
|
mime_fixture_free(fixture);
|
|
g_string_free(eml, TRUE);
|
|
}
|
|
|
|
static void test_total_decoded_limit(void)
|
|
{
|
|
const gsize part_length = 7U * 1024U * 1024U;
|
|
char *part_content = g_strnfill(part_length, 'z');
|
|
GString *eml = g_string_new(
|
|
"Content-Type: multipart/mixed; boundary=total\r\n\r\n"
|
|
);
|
|
for (guint index = 0; index < 5; index++)
|
|
{
|
|
g_string_append_printf(
|
|
eml,
|
|
"--total\r\nContent-Type: application/octet-stream; "
|
|
"name=large-%u.bin\r\n"
|
|
"Content-Disposition: attachment\r\n\r\n",
|
|
index
|
|
);
|
|
g_string_append_len(eml, part_content, (gssize) part_length);
|
|
g_string_append(eml, "\r\n");
|
|
}
|
|
g_string_append(eml, "--total--\r\n");
|
|
g_free(part_content);
|
|
|
|
MimeFixture *fixture = mime_fixture_new(eml->str);
|
|
GError *error = NULL;
|
|
EmlMimeResult *result = eml_mime_extract_attachments(
|
|
fixture->eml_path,
|
|
fixture->output_directory,
|
|
&error
|
|
);
|
|
g_assert_no_error(error);
|
|
g_assert_cmpuint(result->attachments->len, ==, 4);
|
|
g_assert_cmpuint(result->warnings->len, ==, 1);
|
|
eml_mime_result_free(result);
|
|
mime_fixture_free(fixture);
|
|
g_string_free(eml, TRUE);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
g_test_init(&argc, &argv, NULL);
|
|
g_test_add_func(
|
|
"/eml-mime/nested-order-encodings",
|
|
test_nested_order_and_encodings
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/three-levels-message",
|
|
test_three_levels_and_message
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/rfc2047-rfc2231-priorities",
|
|
test_rfc2047_and_rfc2231_priorities
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/invalid-encodings",
|
|
test_invalid_encodings_are_warnings
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/paths-collisions",
|
|
test_paths_and_collisions
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/malformed-rfc2231",
|
|
test_malformed_and_incomplete_rfc2231
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/missing-boundary",
|
|
test_missing_boundary
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/cancelled-before",
|
|
test_cancelled_before_extraction
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/source-unchanged",
|
|
test_source_unchanged
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/filename-sanitizer-limits",
|
|
test_filename_sanitizer_limits
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/part-count-limit",
|
|
test_part_count_limit
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/depth-limit",
|
|
test_depth_limit
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/part-size-no-temporary",
|
|
test_part_size_and_no_temporary_file
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/empty-base64-malformed-header",
|
|
test_empty_base64_and_malformed_header
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/cancelled-during",
|
|
test_cancelled_during_extraction
|
|
);
|
|
g_test_add_func(
|
|
"/eml-mime/total-decoded-limit",
|
|
test_total_decoded_limit
|
|
);
|
|
return g_test_run();
|
|
}
|