/*
* LibGCab
* Copyright (c) 2017, Richard Hughes <richard@hughsie.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "config.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <libgcab.h>
static gchar *
gcab_test_get_filename (const gchar *filename)
{
gchar *tmp;
char full_tmp[PATH_MAX];
g_autofree gchar *path = NULL;
path = g_build_filename (TESTDATADIR, filename, NULL);
tmp = realpath (path, full_tmp);
if (tmp != NULL)
return g_strdup (full_tmp);
return NULL;
}
static void
gcab_test_file_func (void)
{
gboolean ret;
GTimeVal tv;
g_autofree gchar *fn = NULL;
g_autoptr(GCabFile) cabfile = NULL;
g_autoptr(GDateTime) dt = NULL;
g_autoptr(GFile) gfile = NULL;
g_autoptr(GDateTime) dt_bday = NULL;
/* create object */
fn = gcab_test_get_filename ("src/test.sh");
g_assert (fn != NULL);
gfile = g_file_new_for_path (fn);
cabfile = gcab_file_new_with_file ("tæst.bin", gfile);
g_assert (cabfile != NULL);
/* set the time */
dt_bday = g_date_time_new_utc (2017, 9, 15, 0, 0, 0.f);
ret = g_date_time_to_timeval (dt_bday, &tv);
g_assert (ret);
gcab_file_set_date (cabfile, &tv);
/* verify */
g_assert (gcab_file_get_file (cabfile) == gfile);
g_assert_cmpstr (gcab_file_get_name (cabfile), ==, "tæst.bin");
g_assert_cmpstr (gcab_file_get_extract_name (cabfile), ==, "tæst.bin");
g_assert_cmpint (gcab_file_get_size (cabfile), ==, 0);
g_assert_cmpint (gcab_file_get_attributes (cabfile), ==, 0);
ret = gcab_file_get_date (cabfile, &tv);
g_assert (ret);
dt = g_date_time_new_from_timeval_utc (&tv);
g_assert_cmpint (g_date_time_get_year (dt), ==, 2017);
g_assert_cmpint (g_date_time_get_month (dt), ==, 9);
g_assert_cmpint (g_date_time_get_day_of_month (dt), ==, 15);
g_assert_cmpint (g_date_time_get_hour (dt), ==, 0);
g_assert_cmpint (g_date_time_get_minute (dt), ==, 0);
g_assert_cmpint (g_date_time_get_second (dt), ==, 0);
/* test changing the extraction name */
gcab_file_set_extract_name (cabfile, "new.bin");
g_assert_cmpstr (gcab_file_get_extract_name (cabfile), ==, "new.bin");
g_assert_cmpstr (gcab_file_get_name (cabfile), ==, "tæst.bin");
}
static void
gcab_test_folder_func (void)
{
GCabFile *cabfile_tmp;
gboolean ret;
g_autofree gchar *fn = NULL;
g_autoptr(GCabFile) cabfile2 = NULL;
g_autoptr(GCabFile) cabfile = NULL;
g_autoptr(GCabFolder) cabfolder = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) gfile = NULL;
g_autoptr(GSList) cabfiles = NULL;
/* create GCabFile */
fn = gcab_test_get_filename ("test-none.cab");
g_assert (fn != NULL);
gfile = g_file_new_for_path (fn);
cabfile = gcab_file_new_with_file ("test.bin", gfile);
/* add single file */
cabfolder = gcab_folder_new (GCAB_COMPRESSION_NONE);
ret = gcab_folder_add_file (cabfolder, cabfile, FALSE, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
g_assert_cmpint (gcab_folder_get_nfiles (cabfolder), ==, 1);
cabfiles = gcab_folder_get_files (cabfolder);
cabfile_tmp = GCAB_FILE (cabfiles->data);
g_assert (cabfile_tmp == cabfile);
g_assert_cmpstr (gcab_file_get_name (cabfile_tmp), ==, "test.bin");
/* add it again */
cabfile2 = gcab_file_new_with_file ("test.bin", gfile);
ret = gcab_folder_add_file (cabfolder, cabfile2, FALSE, NULL, &error);
g_assert_error (error, GCAB_ERROR, GCAB_ERROR_FORMAT);
g_assert (!ret);
/* find using the hash table */
cabfile_tmp = gcab_folder_get_file_by_name (cabfolder, "test.bin");
g_assert_nonnull (cabfile_tmp);
cabfile_tmp = gcab_folder_get_file_by_name (cabfolder, "notgoingtoexist");
g_assert_null (cabfile_tmp);
/* find the extracted name too */
cabfile_tmp = gcab_folder_get_file_by_name (cabfolder, "test.bin");
gcab_file_set_extract_name (cabfile_tmp, "LVFS-test.bin");
cabfile_tmp = gcab_folder_get_file_by_name (cabfolder, "LVFS-test.bin");
g_assert_nonnull (cabfile_tmp);
}
static void
gcab_test_folder_path_func (void)
{
GCabFile *cabfile_tmp;
gboolean ret;
g_autofree gchar *fn = NULL;
g_autoptr(GCabFile) cabfile = NULL;
g_autoptr(GCabFolder) cabfolder = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) gfile = NULL;
g_autoptr(GSList) cabfiles = NULL;
/* create GCabFile of a path */
fn = gcab_test_get_filename ("src");
g_assert (fn != NULL);
gfile = g_file_new_for_path (fn);
cabfile = gcab_file_new_with_file ("dirname", gfile);
/* add entire subdir */
cabfolder = gcab_folder_new (GCAB_COMPRESSION_NONE);
ret = gcab_folder_add_file (cabfolder, cabfile, TRUE, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
g_assert_cmpint (gcab_folder_get_nfiles (cabfolder), ==, 2);
cabfiles = gcab_folder_get_files (cabfolder);
cabfile_tmp = GCAB_FILE (cabfiles->data);
g_assert (cabfile_tmp != cabfile);
g_assert_cmpstr (gcab_file_get_name (cabfile_tmp), ==, "dirname\\test.txt");
g_assert_cmpstr (gcab_file_get_extract_name (cabfile_tmp), ==, "dirname\\test.txt");
/* add it again */
ret = gcab_folder_add_file (cabfolder, cabfile, TRUE, NULL, &error);
g_assert_error (error, GCAB_ERROR, GCAB_ERROR_FORMAT);
g_assert (!ret);
}
static void
gcab_test_cabinet_error_not_loaded_func (void)
{
gboolean ret;
g_autoptr(GCabCabinet) cabinet = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file = NULL;
/* we can't extract before loading */
cabinet = gcab_cabinet_new ();
file = g_file_new_for_path ("/tmp");
ret = gcab_cabinet_extract_simple(cabinet, file, NULL, NULL, NULL, &error);
g_assert_error (error, GCAB_ERROR, GCAB_ERROR_FAILED);
g_assert (!ret);
}
static void
gcab_test_cabinet_error_no_folders_func (void)
{
gboolean ret;
g_autoptr(GCabCabinet) cabinet = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file = NULL;
g_autoptr(GOutputStream) op = NULL;
/* we can't write with no folders */
cabinet = gcab_cabinet_new ();
file = g_file_new_for_path ("/tmp/foo.cab");
op = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, 0, NULL, &error));
g_assert_no_error (error);
g_assert (op != NULL);
ret = gcab_cabinet_write_simple (cabinet, op, NULL, NULL, NULL, &error);
g_assert_error (error, GCAB_ERROR, GCAB_ERROR_FAILED);
g_assert (!ret);
}
static void
gcab_test_cabinet_func (void)
{
GPtrArray *cabfolders;
gboolean ret;
g_autoptr(GCabCabinet) cabinet = NULL;
g_autoptr(GCabFolder) folder = NULL;
g_autoptr(GError) error = NULL;
/* create cabinet */
cabinet = gcab_cabinet_new ();
g_assert_cmpint (gcab_cabinet_get_size (cabinet), ==, 0);
/* add folder */
folder = gcab_folder_new (GCAB_COMPRESSION_NONE);
ret = gcab_cabinet_add_folder (cabinet, folder, &error);
g_assert_no_error (error);
g_assert (ret);
/* add folder again */
ret = gcab_cabinet_add_folder (cabinet, folder, &error);
g_assert_error (error, GCAB_ERROR, GCAB_ERROR_FORMAT);
g_assert (!ret);
cabfolders = gcab_cabinet_get_folders (cabinet);
g_assert_cmpint (cabfolders->len, ==, 1);
}
static void
gcab_test_cabinet_error_not_seekable_func (void)
{
const GByteArray *signature;
g_autoptr(GCabCabinet) cabinet = NULL;
g_autoptr(GError) error = NULL;
/* verify it has no signature */
cabinet = gcab_cabinet_new ();
signature = gcab_cabinet_get_signature (cabinet, NULL, &error);
g_assert_error (error, GCAB_ERROR, GCAB_ERROR_NOT_SUPPORTED);
g_assert (signature == NULL);
}
static gchar *
_compute_checksum_for_file (GFile *file, GError **error)
{
g_autoptr(GBytes) blob = NULL;
g_autoptr(GInputStream) in = NULL;
in = G_INPUT_STREAM (g_file_read (file, NULL, error));
if (in == NULL)
return NULL;
blob = g_input_stream_read_bytes (in, 1024, NULL, error);
if (blob == NULL)
return NULL;
return g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob);
}
static void
gcab_test_cabinet_blob_func (void)
{
gboolean ret;
g_autoptr(GError) error = NULL;
g_autoptr(GCabCabinet) cabinet = gcab_cabinet_new ();
/* create folder and add to cabinet */
g_autoptr(GCabFolder) folder = gcab_folder_new (GCAB_COMPRESSION_NONE);
ret = gcab_cabinet_add_folder (cabinet, folder, &error);
g_assert_no_error (error);
g_assert (ret);
/* add the two files */
struct {
const gchar *fn;
const gchar *contents;
} files[] = {
{ "test.sh", "echo ola\n" },
{ "test.txt", "Ola!\n" },
{ NULL, NULL }
};
for (guint i = 0; files[i].fn != NULL; i++) {
g_autoptr(GBytes) bytes_tmp = g_bytes_new_static (files[i].contents,
strlen (files[i].contents));
g_autoptr(GCabFile) cabfile = gcab_file_new_with_bytes (files[i].fn, bytes_tmp);
/* set the time and attributes */
g_autoptr(GDateTime) dt = dt = g_date_time_new_utc (2017, 9, 15, 0, 0, 0.f);
GTimeVal tv;
ret = g_date_time_to_timeval (dt, &tv);
g_assert (ret);
gcab_file_set_date (cabfile, &tv);
gcab_file_set_attributes (cabfile, GCAB_FILE_ATTRIBUTE_ARCH);
/* add file to folder */
ret = gcab_folder_add_file (folder, cabfile, FALSE, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
}
/* write to a blob */
g_autoptr(GOutputStream) op = g_memory_output_stream_new_resizable ();
ret = gcab_cabinet_write_simple (cabinet, op, NULL, NULL, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
/* get what the checksum is supposed to be */
g_autofree gchar *fn = gcab_test_get_filename ("test-none.cab");
g_assert (fn != NULL);
g_autoptr(GFile) file = g_file_new_for_path (fn);
g_assert (file != NULL);
g_autofree gchar *checksum = _compute_checksum_for_file (file, &error);
g_assert_no_error (error);
g_assert (checksum != NULL);
/* write for debugging */
ret = g_output_stream_close (op, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
g_autoptr(GBytes) blob = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (op));
ret = g_file_set_contents ("/tmp/test-none.cab", g_bytes_get_data (blob, NULL), g_bytes_get_size (blob), &error);
g_assert_no_error (error);
g_assert (ret);
/* verify the checksum */
g_autofree gchar *csum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob);
g_assert_cmpstr (csum, ==, checksum);
}
static void
gcab_test_cabinet_load_func (void)
{
struct {
const gchar *fn;
guint32 size;
GCabCompression comptype;
} tests[] = {
{ "test-none.cab", 115, GCAB_COMPRESSION_NONE },
{ "test-mszip.cab", 119, GCAB_COMPRESSION_MSZIP },
{ "test-signed.cab", 139, GCAB_COMPRESSION_NONE },
{ NULL, 0, 0 }
};
for (guint i = 0; tests[i].fn != NULL; i++) {
GCabFolder *cabfolder_tmp;
GPtrArray *cabfolders;
gboolean ret;
g_autofree gchar *fn = NULL;
g_autoptr(GCabCabinet) cabinet = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file = NULL;
g_autoptr(GFile) file_tmpdir = NULL;
g_autoptr(GInputStream) in = NULL;
struct {
const gchar *fn;
const gchar *checksum;
} files[] = {
{ "test.sh", "82b4415cf30efc9b5877e366475d652f263c0ced" },
{ "test.txt", "decc67ff4a11acd93430cbb18c7bbddd00abf4fa" },
{ NULL, NULL }
};
/* read file */
g_print ("%s... ", tests[i].fn);
fn = gcab_test_get_filename (tests[i].fn);
g_assert (fn != NULL);
file = g_file_new_for_path (fn);
in = G_INPUT_STREAM (g_file_read (file, NULL, &error));
g_assert_no_error (error);
g_assert (in != NULL);
cabinet = gcab_cabinet_new ();
ret = gcab_cabinet_load (cabinet, in, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
/* check size */
g_assert_cmpint (gcab_cabinet_get_size (cabinet), ==, tests[i].size);
cabfolders = gcab_cabinet_get_folders (cabinet);
g_assert (cabfolders != NULL);
g_assert_cmpint (cabfolders->len, ==, 1);
cabfolder_tmp = g_ptr_array_index (cabfolders, 0);
g_assert_cmpint (gcab_folder_get_comptype (cabfolder_tmp), ==, tests[i].comptype);
g_autoptr(GSList) cabfiles = gcab_folder_get_files (cabfolder_tmp);
for (GSList *l = cabfiles; l != NULL; l = l->next) {
GCabFile *cabfile = GCAB_FILE (l->data);
g_assert_null (gcab_file_get_file (cabfile));
g_assert_null (gcab_file_get_bytes (cabfile));
g_assert_cmpint (gcab_file_get_attributes (cabfile), ==, GCAB_FILE_ATTRIBUTE_ARCH);
}
file_tmpdir = g_file_new_for_path ("/tmp");
ret = gcab_cabinet_extract_simple (cabinet, file_tmpdir, NULL, NULL, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
/* check files were decompressed okay */
for (guint j = 0; files[j].fn != NULL; j++) {
g_autofree gchar *fn_src = g_build_filename ("/tmp", files[j].fn, NULL);
g_autoptr(GFile) file_dst = g_file_new_for_path (fn_src);
g_autofree gchar *csum = _compute_checksum_for_file (file_dst, &error);
g_assert_no_error (error);
g_assert (csum != NULL);
g_assert_cmpstr (csum, ==, files[j].checksum);
}
/* extract again to a memory blob on each GCabFile */
ret = gcab_cabinet_extract_simple (cabinet, NULL, NULL, NULL, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
/* check each blob checksum */
for (guint j = 0; files[j].fn != NULL; j++) {
GCabFile *cabfile = gcab_folder_get_file_by_name (cabfolder_tmp, files[j].fn);
g_assert_nonnull (cabfile);
GBytes *blob = gcab_file_get_bytes (cabfile);
g_assert_nonnull (blob);
g_autofree gchar *csum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob);
g_assert_cmpstr (csum, ==, files[j].checksum);
}
}
}
static void
gcab_test_cabinet_error_cves_func (void)
{
const gchar *tests[] = {
"CVE-2014-9556.cab",
"CVE-2014-9732.cab",
"CVE-2015-4470.cab",
"CVE-2015-4471.cab",
NULL };
g_setenv ("GCAB_SKIP_CHECKSUM", "1", TRUE);
for (guint i = 0; tests[i] != NULL; i++) {
gboolean ret;
g_autofree gchar *fn = NULL;
g_autoptr(GCabCabinet) cabinet = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file = NULL;
g_autoptr(GFile) file_tmpdir = NULL;
g_autoptr(GInputStream) in = NULL;
/* read file */
g_print ("%s... ", tests[i]);
fn = gcab_test_get_filename (tests[i]);
g_assert (fn != NULL);
file = g_file_new_for_path (fn);
in = G_INPUT_STREAM (g_file_read (file, NULL, &error));
g_assert_no_error (error);
g_assert (in != NULL);
cabinet = gcab_cabinet_new ();
ret = gcab_cabinet_load (cabinet, in, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
file_tmpdir = g_file_new_for_path ("/tmp");
ret = gcab_cabinet_extract_simple (cabinet, file_tmpdir, NULL, NULL, NULL, &error);
g_assert (error != NULL);
g_assert (!ret);
}
g_unsetenv ("GCAB_SKIP_CHECKSUM");
}
static void
gcab_test_cabinet_signature_func (void)
{
const GByteArray *sig;
gboolean ret;
g_autofree gchar *fn = NULL;
g_autoptr(GCabCabinet) cabinet = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file = NULL;
g_autoptr(GInputStream) in = NULL;
/* load file */
fn = gcab_test_get_filename ("test-signed.cab");
g_assert (fn != NULL);
file = g_file_new_for_path (fn);
in = G_INPUT_STREAM (g_file_read (file, NULL, &error));
g_assert_no_error (error);
g_assert (in != NULL);
cabinet = gcab_cabinet_new ();
ret = gcab_cabinet_load (cabinet, in, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
/* get signature */
sig = gcab_cabinet_get_signature (cabinet, NULL, &error);
g_assert_no_error (error);
g_assert (sig != NULL);
g_assert_cmpint (sig->len, ==, 2040);
}
static void
gcab_test_cabinet_write_func (void)
{
struct {
const gchar *fn;
guint32 size;
GCabCompression comptype;
} tests[] = {
{ "test-none.cab", 115, GCAB_COMPRESSION_NONE },
{ "test-mszip.cab", 119, GCAB_COMPRESSION_MSZIP },
{ NULL, 0, 0 }
};
for (guint i = 0; tests[i].fn != NULL; i++) {
gboolean ret;
g_autofree gchar *checksum_in = NULL;
g_autofree gchar *checksum_out = NULL;
g_autofree gchar *fn_in = NULL;
g_autofree gchar *fn_out = NULL;
g_autoptr(GCabCabinet) cabinet = NULL;
g_autoptr(GCabFolder) cabfolder = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file_in = NULL;
g_autoptr(GFile) file_out = NULL;
g_autoptr(GOutputStream) op = NULL;
struct {
const gchar *fn;
const gchar *checksum;
} files[] = {
{ "test.sh", "82b4415cf30efc9b5877e366475d652f263c0ced" },
{ "test.txt", "decc67ff4a11acd93430cbb18c7bbddd00abf4fa" },
{ NULL, NULL }
};
/* create new cabinet with a single folder of the correct type */
cabinet = gcab_cabinet_new ();
cabfolder = gcab_folder_new (tests[i].comptype);
ret = gcab_cabinet_add_folder (cabinet, cabfolder, &error);
g_assert_no_error (error);
g_assert (ret);
/* add new files to a folder */
for (guint j = 0; files[j].fn != NULL; j++) {
g_autofree gchar *fn_src = g_build_filename ("src", files[j].fn, NULL);
g_autofree gchar *fn_tmp = gcab_test_get_filename (fn_src);
g_autoptr(GFile) file = g_file_new_for_path (fn_tmp);
g_autoptr(GCabFile) cabfile = gcab_file_new_with_file (files[j].fn, file);
g_autoptr(GDateTime) dt = NULL;
GTimeVal tv;
/* set the time so the checksums match */
dt = g_date_time_new_utc (2017, 9, 15, 0, 0, 0.f);
ret = g_date_time_to_timeval (dt, &tv);
g_assert (ret);
gcab_file_set_date (cabfile, &tv);
ret = gcab_folder_add_file (cabfolder, cabfile, FALSE, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
}
/* write the file */
g_print ("%s... ", tests[i].fn);
fn_out = g_build_filename ("/tmp", tests[i].fn, NULL);
file_out = g_file_new_for_path (fn_out);
op = G_OUTPUT_STREAM (g_file_replace (file_out, NULL, FALSE, 0, NULL, &error));
g_assert_no_error (error);
g_assert (op != NULL);
ret = gcab_cabinet_write_simple (cabinet, op, NULL, NULL, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
/* check size */
g_assert_cmpint (gcab_cabinet_get_size (cabinet), ==, tests[i].size);
/* compare checksums */
fn_in = gcab_test_get_filename (tests[i].fn);
g_assert (fn_in != NULL);
file_in = g_file_new_for_path (fn_in);
checksum_in = _compute_checksum_for_file (file_in, &error);
g_assert_no_error (error);
g_assert (checksum_in != NULL);
checksum_out = _compute_checksum_for_file (file_out, &error);
g_assert_no_error (error);
g_assert (checksum_out != NULL);
g_assert_cmpstr (checksum_in, ==, checksum_out);
}
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
/* only critical and error are fatal */
g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
/* tests go here */
g_test_add_func ("/GCab/file", gcab_test_file_func);
g_test_add_func ("/GCab/folder", gcab_test_folder_func);
g_test_add_func ("/GCab/folder{path}", gcab_test_folder_path_func);
g_test_add_func ("/GCab/cabinet", gcab_test_cabinet_func);
g_test_add_func ("/GCab/cabinet{error-not-seekable}", gcab_test_cabinet_error_not_seekable_func);
g_test_add_func ("/GCab/cabinet{error-not-loaded}", gcab_test_cabinet_error_not_loaded_func);
g_test_add_func ("/GCab/cabinet{error-no-folders}", gcab_test_cabinet_error_no_folders_func);
g_test_add_func ("/GCab/cabinet{error-cves}", gcab_test_cabinet_error_cves_func);
g_test_add_func ("/GCab/cabinet{load}", gcab_test_cabinet_load_func);
g_test_add_func ("/GCab/cabinet{write}", gcab_test_cabinet_write_func);
g_test_add_func ("/GCab/cabinet{blob}", gcab_test_cabinet_blob_func);
g_test_add_func ("/GCab/cabinet{signature}", gcab_test_cabinet_signature_func);
return g_test_run ();
}