diff --git a/Makefile b/Makefile index fadf89d..f2dd960 100644 --- a/Makefile +++ b/Makefile @@ -6,15 +6,20 @@ LIB_FILENAME ?= libvieter.a BUILD_DIR ?= build SRC_DIR ?= src TEST_DIR ?= test -INC_DIRS ?= include +3RDPARTY_DIR ?= thirdparty +INCLUDE_DIR ?= include +INC_DIRS ?= $(INCLUDE_DIR) $(3RDPARTY_DIR)/include LIB := $(BUILD_DIR)/$(LIB_FILENAME) SRCS != find '$(SRC_DIR)' -iname '*.c' SRCS_H != find $(INC_DIRS) '$(SRC_DIR)' -iname '*.h' SRCS_TEST != find '$(TEST_DIR)' -iname '*.c' +SRCS_3RDPARTY != find '$(3RDPARTY_DIR)/src' -iname '*.c' -OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) +$(info ${SRCS}) + +OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) $(SRCS_3RDPARTY:%=$(BUILD_DIR)/%.o) OBJS_TEST := $(SRCS_TEST:%=$(BUILD_DIR)/%.o) DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) @@ -22,6 +27,7 @@ BINS_TEST := $(OBJS_TEST:%.c.o=%) TARGETS_TEST := $(BINS_TEST:%=test-%) TARGETS_MEM_TEST := $(BINS_TEST:%=test-mem-%) +LIBFLAGS := -larchive INC_FLAGS := $(addprefix -I,$(INC_DIRS)) # -MMD: generate a .d file for every source file. This file can be imported by @@ -50,6 +56,9 @@ $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c mkdir -p $(dir $@) $(CC) $(VIETERCFLAGS) -c $< -o $@ +$(BUILD_DIR)/$(3RDPARTY_DIR)/src/%.c.o: $(3RDPARTY_DIR)/src/%.c + mkdir -p $(dir $@) + $(CC) $(VIETERCFLAGS) -c $< -o $@ # =====TESTING===== .PHONY: test @@ -71,7 +80,7 @@ build-test: $(BINS_TEST) $(BINS_TEST): %: %.c.o $(LIB) $(CC) \ - $^ -o $@ + $^ $(LIBFLAGS) -o $@ # Along with the include directory, each test includes $(TEST_DIR) (which # contains the acutest.h header file), and the src directory of the module it's diff --git a/README.md b/README.md index abc250a..408f760 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ See the [source code](src) for the list of modules. Everything is handled by the provided Makefile. To compile the static library, simply run `make`. +### Required libraries + +Libvieter requires libarchive. + ### Project structure Each module has its own subdirectory inside `src`, e.g. `src/cron`. This diff --git a/include/vieter_dynarray.h b/include/vieter_dynarray.h new file mode 100644 index 0000000..2a6305e --- /dev/null +++ b/include/vieter_dynarray.h @@ -0,0 +1,34 @@ +#ifndef VIETER_DYNARRAY +#define VIETER_DYNARRAY + +#include +#include + +typedef struct vieter_dynarray { + char **array; + size_t capacity; + size_t size; +} vieter_dynarray; + +/* + * Allocate a dynamic array. + */ +vieter_dynarray *vieter_dynarray_init(size_t initial_capacity); + +/* + * Initialise array (if it's not already initialised) and insert a string. + */ +void vieter_dynarray_add(vieter_dynarray *da, const char *s); + +/* + * Deallocate dynamic array. + */ +void vieter_dynarray_free(vieter_dynarray *da); + +/* + * Convert a vieter_dynarray into an array by freeing all its surrounding + * components and returning the underlying array pointer. + */ +char **vieter_dynarray_convert(vieter_dynarray *da); + +#endif diff --git a/include/vieter_package.h b/include/vieter_package.h new file mode 100644 index 0000000..fb220c8 --- /dev/null +++ b/include/vieter_package.h @@ -0,0 +1,33 @@ +#ifndef VIETER_PACKAGE +#define VIETER_PACKAGE + +typedef struct vieter_package vieter_package; + +typedef enum vieter_package_error { + vieter_package_ok = 0, + vieter_package_unarchive_error = 1, + vieter_package_stat_error = 2 +} vieter_package_error; + +/* + * Allocate an empty package + */ +vieter_package *vieter_package_init(); + +/* + * Parse package file into something usable by libvieter. + */ +vieter_package_error vieter_package_read_archive(vieter_package *pkg, + const char *pkg_path); + +/* + * Deallocate a package. + */ +void vieter_package_free(vieter_package **ptp); + +/* + * Create string that will become the package's desc file. + */ +char *vieter_package_to_description(vieter_package *pkg); + +#endif diff --git a/src/dynarray/vieter_dynarray.c b/src/dynarray/vieter_dynarray.c new file mode 100644 index 0000000..5c4026b --- /dev/null +++ b/src/dynarray/vieter_dynarray.c @@ -0,0 +1,58 @@ +#include "vieter_dynarray.h" + +vieter_dynarray *vieter_dynarray_init(size_t initial_capacity) { + vieter_dynarray *da = malloc(sizeof(vieter_dynarray)); + da->size = 0; + da->capacity = initial_capacity; + + return da; +} + +void vieter_dynarray_add(vieter_dynarray *da, const char *s) { + // An empty vieter_dynarray does not have an allocated internal array + // yet + if (da->size == 0) { + da->array = malloc(sizeof(char *) * da->capacity); + + // Initialise all char*'s to 0 so array[i] == NULL can be used to see if + // field is empty + memset(da->array, 0, sizeof(char *) * da->capacity); + } + // Double array size if it's full + else if (da->size == da->capacity) { + // if the realloc fails, access to memory in da->array is lost + da->array = realloc(da->array, sizeof(char *) * da->capacity * 2); + da->capacity *= 2; + + // Same as the previous memset, but only for newly allocated pointers + memset(da->array + da->size, 0, sizeof(char *) * da->capacity / 2); + } + + da->array[da->size] = strdup(s); + da->size++; +} + +void vieter_dynarray_free(vieter_dynarray *da) { + if (da == NULL) { + return; + } + + if (da->array != NULL) { + for (size_t i = 0; i < da->size; i++) { + free(da->array[i]); + } + + free(da->array); + } + + free(da); +} + +char **vieter_dynarray_convert(vieter_dynarray *da) { + char **array = da->array; + + da->array = NULL; + vieter_dynarray_free(da); + + return array; +} diff --git a/src/package/README.md b/src/package/README.md new file mode 100644 index 0000000..b2bcbd7 --- /dev/null +++ b/src/package/README.md @@ -0,0 +1,5 @@ +# package + +This module handles both parsing the published Arch tarballs & the contents of +their `.PKGINFO` files, as well as generating the contents of the database +archives' `desc` & `files` files. diff --git a/src/package/vieter_package.c b/src/package/vieter_package.c new file mode 100644 index 0000000..6aca847 --- /dev/null +++ b/src/package/vieter_package.c @@ -0,0 +1,263 @@ +#include +#include +#include +#include + +#include "sha256.h" +#include "vieter_package_internal.h" + +#define ADD_STRING(section, field) \ + if (pkg_info->field != 0) { \ + size_to_be_written = \ + snprintf(aux, small_buff_size, section, pkg_info->field); \ + if (size_to_be_written > small_buff_size) { \ + aux = realloc(aux, size_to_be_written + 1); \ + small_buff_size = size_to_be_written + 1; \ + snprintf(aux, small_buff_size, section, pkg_info->field); \ + } \ + if (buff_size < strlen(description) + small_buff_size + 1) { \ + description = realloc(description, buff_size * 2); \ + buff_size *= 2; \ + } \ + strcat(description, aux); \ + } + +#define ADD_ARRAY(section, field) \ + i = 0; \ + if (pkg_info->field != NULL) { \ + ADD_STRING(section, field->array[i]); \ + i++; \ + while (pkg_info->field->array[i] != NULL) { \ + ADD_STRING("\n%s", field->array[i]); \ + i++; \ + } \ + } + +static char *ignored_names[5] = {".BUILDINFO", ".INSTALL", ".MTREE", ".PKGINFO", + ".CHANGELOG"}; +static size_t ignored_words_len = sizeof(ignored_names) / sizeof(char *); + +vieter_package *vieter_package_init() { + return calloc(sizeof(vieter_package_info), 1); +} + +vieter_package_error vieter_package_read_archive(vieter_package *pkg, + const char *pkg_path) { + struct archive *a = archive_read_new(); + struct archive_entry *entry; + + // These three are the most commonly used compression methods + archive_read_support_filter_zstd(a); + archive_read_support_filter_gzip(a); + archive_read_support_filter_xz(a); + + // Contents should always be a tarball + archive_read_support_format_tar(a); + + // TODO where does this 10240 come from? + int r = archive_read_open_filename(a, pkg_path, 10240); + + // Exit early if we weren't able to successfully open the archive for reading + if (r != ARCHIVE_OK) { + return vieter_package_unarchive_error; + } + + int compression_code = archive_filter_code(a, 0); + const char *path_name; + + vieter_package_info *pkg_info = NULL; + vieter_dynarray *files = vieter_dynarray_init(16); + vieter_dynarray_add(files, "%FILES%"); + + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { + path_name = archive_entry_pathname(entry); + + bool ignore = false; + + for (size_t i = 0; i < ignored_words_len; i++) { + if (strcmp(path_name, ignored_names[i]) == 0) { + ignore = true; + break; + } + } + + if (!ignore) { + vieter_dynarray_add(files, path_name); + } + + if (strcmp(path_name, ".PKGINFO") == 0) { + // Read data of file into memory buffer + int size = archive_entry_size(entry); + char *buf = malloc(size + 1); + archive_read_data(a, buf, size); + buf[size] = '\0'; + + // Parse package vieter_package_info string into a struct + pkg_info = vieter_package_info_init(); + vieter_package_info_parse(pkg_info, buf); + + free(buf); + } else { + archive_read_data_skip(a); + } + } + + // Get size of file + struct stat stats; + + if (stat(pkg_path, &stats) != 0) { + // errno is set if stat() fails; the calling function should check + // the value of errno in case vieter_package_stat_error is returned + return vieter_package_stat_error; + } + + pkg_info->csize = stats.st_size; + + archive_read_free(a); + + // Create final return value + pkg->path = strdup(pkg_path); + pkg->info = pkg_info; + pkg->files = files; + pkg->compression = compression_code; + + return vieter_package_ok; +} + +void vieter_package_sha256sum(vieter_package *pkg, char *res) { + FILE *f = fopen(pkg->path, "r"); + // Try to read 100KiB at a time + unsigned char *in = malloc(102400); + // Actual number of bytes read + size_t read_size; + + SHA256_CTX *ctx = malloc(sizeof(SHA256_CTX)); + sha256_init(ctx); + while ((read_size = fread(in, 1, 102400, f)) != 0) { + sha256_update(ctx, in, read_size); + } + unsigned char hash[SHA256_BLOCK_SIZE]; + + sha256_final(ctx, hash); + + fclose(f); + free(in); + free(ctx); + + // We need to convert the bytes in the hash to get a string representation of + // its hex values i.e. turn 1001 1111 into the string "9f" Each byte of the + // hash is going to turn into two bytes in the final string so we are going to + // convert each half byte into a char + unsigned int half_byte = 0; + int j = 0; + + // We advance 2 bytes in the string for every one byte of the hash + for (int i = 0; i < SHA256_BLOCK_SIZE; i++) { + // We transform the first half byte into the second character to keep + // each byte from becoming reversed in the final string + half_byte = hash[i] & 0b1111; + if (half_byte < 10) { + res[j + 1] = half_byte + 48; + } else { + res[j + 1] = half_byte + 87; + } + hash[i] = hash[i] >> 4; + half_byte = hash[i] & 0b1111; + if (half_byte < 10) { + res[j] = half_byte + 48; + } else { + res[j] = half_byte + 87; + } + + j += 2; + } + res[j] = '\0'; +} + +char *vieter_package_to_description(vieter_package *pkg) { + vieter_package_info *pkg_info = pkg->info; + + size_t buff_size = 1024; + int small_buff_size = 128; + int size_to_be_written; + char *aux = malloc(sizeof(char) * small_buff_size); + char *description = malloc(sizeof(char) * buff_size); + // Helper variable for ADD_ARRAY macro + int i; + + // special case for FILENAME + char *ext = NULL; + switch (pkg->compression) { + case 0: + ext = ".tar"; + break; + case 1: + ext = ".tar.gz"; + break; + case 6: + ext = ".tar.xz"; + break; + case 14: + ext = ".tar.zst"; + break; + } + + size_to_be_written = + snprintf(aux, small_buff_size, "%%FILENAME%%\n%s-%s-%s.pkg%s", + pkg_info->name, pkg_info->version, pkg_info->arch, ext); + + // We neither want to let an arbritrarily long input to overflow the buffer + // nor to truncate perfectly valid inputs + if (size_to_be_written > small_buff_size) { + aux = realloc(aux, size_to_be_written + 1); + small_buff_size = size_to_be_written + 1; + snprintf(aux, small_buff_size, "%%FILENAME%%\n%s-%s-%s.pkg.tar.zst", + pkg_info->name, pkg_info->version, pkg_info->arch); + } + strcpy(description, aux); + + ADD_STRING("\n\n%%NAME%%\n%s", name); + ADD_STRING("\n\n%%BASE%%\n%s", base); + ADD_STRING("\n\n%%VERSION%%\n%s", version); + ADD_STRING("\n\n%%DESC%%\n%s", description); + ADD_ARRAY("\n\n%%GROUPS%%\n%s", groups); + ADD_STRING("\n\n%%CSIZE%%\n%ld", csize); + ADD_STRING("\n\n%%ISIZE%%\n%ld", size); + + char checksum[SHA256_BLOCK_SIZE * 2 + 1]; + vieter_package_sha256sum(pkg, checksum); + + snprintf(aux, small_buff_size, "\n\n%%SHA256SUM%%\n%s", checksum); + if (buff_size < strlen(description) + small_buff_size + 1) { + description = realloc(description, buff_size * 2); + buff_size *= 2; + } + strcat(description, aux); + + ADD_STRING("\n\n%%URL%%\n%s", url); + ADD_ARRAY("\n\n%%LICENSE%%\n%s", licenses); + ADD_STRING("\n\n%%ARCH%%\n%s", arch); + ADD_STRING("\n\n%%BUILDDATE%%\n%ld", build_date); + ADD_STRING("\n\n%%PACKAGER%%\n%s", packager); + ADD_ARRAY("\n\n%%REPLACES%%\n%s", replaces); + ADD_ARRAY("\n\n%%CONFLICTS%%\n%s", conflicts); + ADD_ARRAY("\n\n%%PROVIDES%%\n%s", provides); + ADD_ARRAY("\n\n%%DEPENDS%%\n%s", depends); + ADD_ARRAY("\n\n%%OPTDEPENDS%%\n%s", optdepends); + ADD_ARRAY("\n\n%%MAKEDEPENDS%%\n%s", makedepends); + ADD_ARRAY("\n\n%%CHECKDEPENDS%%\n%s", checkdepends); + + strcat(description, "\n\n"); + + free(aux); + + return description; +} + +void vieter_package_free(vieter_package **ptp) { + FREE_STRING((*ptp)->path); + vieter_package_info_free((*ptp)->info); + vieter_dynarray_free((*ptp)->files); + free(*ptp); + *ptp = NULL; +} diff --git a/src/package/vieter_package_info.c b/src/package/vieter_package_info.c new file mode 100644 index 0000000..4805206 --- /dev/null +++ b/src/package/vieter_package_info.c @@ -0,0 +1,86 @@ +#include + +#include "vieter_package_info.h" + +#define PKG_INFO_STRING(key_ptr, field) \ + if ((value_ptr = strstr(value_ptr, key_ptr)) != NULL) { \ + value_ptr += strlen(key_ptr); \ + tail_ptr = strchr(value_ptr, '\n'); \ + tail_ptr[0] = '\0'; \ + pkg_info->field = strdup(value_ptr); \ + tail_ptr[0] = '\n'; \ + } \ + value_ptr = tail_ptr; + +#define PKG_INFO_INT(key_ptr, field) \ + value_ptr = strstr(value_ptr, key_ptr) + strlen(key_ptr); \ + tail_ptr = strchr(value_ptr, '\n'); \ + tail_ptr[0] = '\0'; \ + pkg_info->field = atoi(value_ptr); \ + tail_ptr[0] = '\n'; \ + value_ptr = tail_ptr; + +#define PKG_INFO_ARRAY(key_ptr, field) \ + while ((value_ptr = strstr(value_ptr, key_ptr)) != NULL) { \ + value_ptr = value_ptr + strlen(key_ptr); \ + tail_ptr = strchr(value_ptr, '\n'); \ + tail_ptr[0] = '\0'; \ + if (pkg_info->field == NULL) { \ + pkg_info->field = vieter_dynarray_init(4); \ + } \ + vieter_dynarray_add(pkg_info->field, value_ptr); \ + tail_ptr[0] = '\n'; \ + value_ptr = tail_ptr; \ + } \ + value_ptr = tail_ptr; + +vieter_package_info *vieter_package_info_init() { + return calloc(1, sizeof(vieter_package_info)); +} + +void vieter_package_info_free(vieter_package_info *pkg_info) { + FREE_STRING(pkg_info->name); + FREE_STRING(pkg_info->base); + FREE_STRING(pkg_info->version); + FREE_STRING(pkg_info->description); + FREE_STRING(pkg_info->url); + FREE_STRING(pkg_info->arch); + FREE_STRING(pkg_info->packager); + FREE_STRING(pkg_info->pgpsig); + + vieter_dynarray_free(pkg_info->groups); + vieter_dynarray_free(pkg_info->licenses); + vieter_dynarray_free(pkg_info->replaces); + vieter_dynarray_free(pkg_info->depends); + vieter_dynarray_free(pkg_info->conflicts); + vieter_dynarray_free(pkg_info->provides); + vieter_dynarray_free(pkg_info->optdepends); + vieter_dynarray_free(pkg_info->makedepends); + vieter_dynarray_free(pkg_info->checkdepends); + + free(pkg_info); +} + +void vieter_package_info_parse(vieter_package_info *pkg_info, + char *pkg_info_str) { + char *value_ptr = pkg_info_str, *tail_ptr = NULL; + + PKG_INFO_STRING("\npkgname = ", name); + PKG_INFO_STRING("\npkgbase = ", base); + PKG_INFO_STRING("\npkgver = ", version); + PKG_INFO_STRING("\npkgdesc = ", description); + PKG_INFO_STRING("\nurl = ", url); + PKG_INFO_INT("\nbuilddate = ", build_date); + PKG_INFO_STRING("\npackager = ", packager); + PKG_INFO_INT("\nsize = ", size); + PKG_INFO_STRING("\narch = ", arch); + PKG_INFO_ARRAY("\nlicense = ", licenses); + PKG_INFO_ARRAY("\nreplaces = ", replaces); + PKG_INFO_ARRAY("\ngroup = ", groups); + PKG_INFO_ARRAY("\nconflict = ", conflicts); + PKG_INFO_ARRAY("\nprovides = ", provides); + PKG_INFO_ARRAY("\ndepend = ", depends); + PKG_INFO_ARRAY("\noptdepend = ", optdepends); + PKG_INFO_ARRAY("\nmakedepend = ", makedepends); + PKG_INFO_ARRAY("\ncheckdepend = ", checkdepends); +} diff --git a/src/package/vieter_package_info.h b/src/package/vieter_package_info.h new file mode 100644 index 0000000..2c69d92 --- /dev/null +++ b/src/package/vieter_package_info.h @@ -0,0 +1,52 @@ +#ifndef VIETER_PACKAGE_INFO +#define VIETER_PACKAGE_INFO + +#define FREE_STRING(sp) if (sp != NULL) free(sp) + +#include + +#include "vieter_package.h" +#include "vieter_dynarray.h" + + +typedef struct vieter_package_info { + char *name; + char *base; + char *version; + char *description; + int64_t size; + int64_t csize; + char *url; + char *arch; + int64_t build_date; + char *packager; + char *pgpsig; + int64_t pgpsigsize; + + vieter_dynarray *groups; + vieter_dynarray *licenses; + vieter_dynarray *replaces; + vieter_dynarray *depends; + vieter_dynarray *conflicts; + vieter_dynarray *provides; + vieter_dynarray *optdepends; + vieter_dynarray *makedepends; + vieter_dynarray *checkdepends; +} vieter_package_info; + +/* + * Allocate and initialise a pkg_info pointer to hold .PKGINFO. + */ +vieter_package_info *vieter_package_info_init(); + +/* + * Parse .PKGINFO file into something usable by libvieter. + */ +void vieter_package_info_parse(vieter_package_info *pkg_info, char *pkg_info_str); + +/* + * Deallocate a pkg_info pointer. + */ +void vieter_package_info_free(vieter_package_info *pkg_info); + +#endif diff --git a/src/package/vieter_package_internal.h b/src/package/vieter_package_internal.h new file mode 100644 index 0000000..4c4eef6 --- /dev/null +++ b/src/package/vieter_package_internal.h @@ -0,0 +1,10 @@ +#include "vieter_package.h" +#include "vieter_package_info.h" +#include "vieter_dynarray.h" + +struct vieter_package { + char *path; + vieter_package_info *info; + vieter_dynarray *files; + int compression; +}; diff --git a/test/package/.PKGINFO b/test/package/.PKGINFO new file mode 100644 index 0000000..7113061 --- /dev/null +++ b/test/package/.PKGINFO @@ -0,0 +1,22 @@ +# Generated by makepkg 6.0.2 +# using fakeroot version 1.30.1 +pkgname = xcursor-dmz +pkgbase = xcursor-dmz +pkgver = 0.4.5-2 +pkgdesc = Style neutral, scalable cursor theme +url = https://packages.debian.org/sid/dmz-cursor-theme +builddate = 1673751613 +packager = Unknown Packager +size = 3469584 +arch = any +license = MIT +replaces = test1 +group = x11 +conflict = test2 +conflict = test3 +provides = test4 +depend = test5 +depend = test6 +optdepend = test7 +makedepend = xorg-xcursorgen +checkdepend = test8 diff --git a/test/package/desc b/test/package/desc new file mode 100644 index 0000000..d583af1 --- /dev/null +++ b/test/package/desc @@ -0,0 +1,45 @@ +%FILENAME% +xcursor-dmz-0.4.5-2-any.pkg.tar.zst + +%NAME% +xcursor-dmz + +%BASE% +xcursor-dmz + +%VERSION% +0.4.5-2 + +%DESC% +Style neutral, scalable cursor theme + +%GROUPS% +x11 + +%CSIZE% +328282 + +%ISIZE% +3469584 + +%SHA256SUM% +4f4bce9e975334ed7775ff4ddf4d2e82e411d599802f6179a122f89149f53bfb + +%URL% +https://packages.debian.org/sid/dmz-cursor-theme + +%LICENSE% +MIT + +%ARCH% +any + +%BUILDDATE% +1673751613 + +%PACKAGER% +Unknown Packager + +%MAKEDEPENDS% +xorg-xcursorgen + diff --git a/test/package/files b/test/package/files new file mode 100644 index 0000000..aab44b1 --- /dev/null +++ b/test/package/files @@ -0,0 +1,243 @@ +%FILES% +usr/ +usr/share/ +usr/share/icons/ +usr/share/icons/DMZ-Black/ +usr/share/icons/DMZ-Black/cursor.theme +usr/share/icons/DMZ-Black/cursors/ +usr/share/icons/DMZ-Black/cursors/00008160000006810000408080010102 +usr/share/icons/DMZ-Black/cursors/028006030e0e7ebffc7f7070c0600140 +usr/share/icons/DMZ-Black/cursors/03b6e0fcb3499374a867c041f52298f0 +usr/share/icons/DMZ-Black/cursors/08e8e1c95fe2fc01f976f1e063a24ccd +usr/share/icons/DMZ-Black/cursors/1081e37283d90000800003c07f3ef6bf +usr/share/icons/DMZ-Black/cursors/14fef782d02440884392942c11205230 +usr/share/icons/DMZ-Black/cursors/2870a09082c103050810ffdffffe0204 +usr/share/icons/DMZ-Black/cursors/3085a0e285430894940527032f8b26df +usr/share/icons/DMZ-Black/cursors/3ecb610c1bf2410f44200f48c40d3599 +usr/share/icons/DMZ-Black/cursors/4498f0e0c1937ffe01fd06f973665830 +usr/share/icons/DMZ-Black/cursors/5c6cd98b3f3ebcb1f9c7f1c204630408 +usr/share/icons/DMZ-Black/cursors/6407b0e94181790501fd1e167b474872 +usr/share/icons/DMZ-Black/cursors/640fb0e74195791501fd1ed57b41487f +usr/share/icons/DMZ-Black/cursors/9081237383d90e509aa00f00170e968f +usr/share/icons/DMZ-Black/cursors/9d800788f1b08800ae810202380a0822 +usr/share/icons/DMZ-Black/cursors/X_cursor +usr/share/icons/DMZ-Black/cursors/alias +usr/share/icons/DMZ-Black/cursors/arrow +usr/share/icons/DMZ-Black/cursors/bd_double_arrow +usr/share/icons/DMZ-Black/cursors/bottom_left_corner +usr/share/icons/DMZ-Black/cursors/bottom_right_corner +usr/share/icons/DMZ-Black/cursors/bottom_side +usr/share/icons/DMZ-Black/cursors/bottom_tee +usr/share/icons/DMZ-Black/cursors/c7088f0f3e6c8088236ef8e1e3e70000 +usr/share/icons/DMZ-Black/cursors/circle +usr/share/icons/DMZ-Black/cursors/col-resize +usr/share/icons/DMZ-Black/cursors/color-picker +usr/share/icons/DMZ-Black/cursors/copy +usr/share/icons/DMZ-Black/cursors/cross +usr/share/icons/DMZ-Black/cursors/cross_reverse +usr/share/icons/DMZ-Black/cursors/crossed_circle +usr/share/icons/DMZ-Black/cursors/crosshair +usr/share/icons/DMZ-Black/cursors/d9ce0ab605698f320427677b458ad60b +usr/share/icons/DMZ-Black/cursors/default +usr/share/icons/DMZ-Black/cursors/diamond_cross +usr/share/icons/DMZ-Black/cursors/dnd-ask +usr/share/icons/DMZ-Black/cursors/dnd-copy +usr/share/icons/DMZ-Black/cursors/dnd-link +usr/share/icons/DMZ-Black/cursors/dnd-move +usr/share/icons/DMZ-Black/cursors/dnd-none +usr/share/icons/DMZ-Black/cursors/dot_box_mask +usr/share/icons/DMZ-Black/cursors/dotbox +usr/share/icons/DMZ-Black/cursors/double_arrow +usr/share/icons/DMZ-Black/cursors/draft_large +usr/share/icons/DMZ-Black/cursors/draft_small +usr/share/icons/DMZ-Black/cursors/draped_box +usr/share/icons/DMZ-Black/cursors/e-resize +usr/share/icons/DMZ-Black/cursors/e29285e634086352946a0e7090d73106 +usr/share/icons/DMZ-Black/cursors/ew-resize +usr/share/icons/DMZ-Black/cursors/fcf1c3c7cd4491d801f1e1c78f100000 +usr/share/icons/DMZ-Black/cursors/fd_double_arrow +usr/share/icons/DMZ-Black/cursors/fleur +usr/share/icons/DMZ-Black/cursors/grab +usr/share/icons/DMZ-Black/cursors/grabbing +usr/share/icons/DMZ-Black/cursors/h_double_arrow +usr/share/icons/DMZ-Black/cursors/hand +usr/share/icons/DMZ-Black/cursors/hand1 +usr/share/icons/DMZ-Black/cursors/hand2 +usr/share/icons/DMZ-Black/cursors/help +usr/share/icons/DMZ-Black/cursors/icon +usr/share/icons/DMZ-Black/cursors/left_ptr +usr/share/icons/DMZ-Black/cursors/left_ptr_help +usr/share/icons/DMZ-Black/cursors/left_ptr_watch +usr/share/icons/DMZ-Black/cursors/left_side +usr/share/icons/DMZ-Black/cursors/left_tee +usr/share/icons/DMZ-Black/cursors/link +usr/share/icons/DMZ-Black/cursors/ll_angle +usr/share/icons/DMZ-Black/cursors/lr_angle +usr/share/icons/DMZ-Black/cursors/move +usr/share/icons/DMZ-Black/cursors/n-resize +usr/share/icons/DMZ-Black/cursors/ne-resize +usr/share/icons/DMZ-Black/cursors/nesw-resize +usr/share/icons/DMZ-Black/cursors/not-allowed +usr/share/icons/DMZ-Black/cursors/ns-resize +usr/share/icons/DMZ-Black/cursors/nw-resize +usr/share/icons/DMZ-Black/cursors/nwse-resize +usr/share/icons/DMZ-Black/cursors/openhand +usr/share/icons/DMZ-Black/cursors/pencil +usr/share/icons/DMZ-Black/cursors/pirate +usr/share/icons/DMZ-Black/cursors/plus +usr/share/icons/DMZ-Black/cursors/pointer +usr/share/icons/DMZ-Black/cursors/progress +usr/share/icons/DMZ-Black/cursors/question_arrow +usr/share/icons/DMZ-Black/cursors/right_ptr +usr/share/icons/DMZ-Black/cursors/right_side +usr/share/icons/DMZ-Black/cursors/right_tee +usr/share/icons/DMZ-Black/cursors/row-resize +usr/share/icons/DMZ-Black/cursors/s-resize +usr/share/icons/DMZ-Black/cursors/sb_down_arrow +usr/share/icons/DMZ-Black/cursors/sb_h_double_arrow +usr/share/icons/DMZ-Black/cursors/sb_left_arrow +usr/share/icons/DMZ-Black/cursors/sb_right_arrow +usr/share/icons/DMZ-Black/cursors/sb_up_arrow +usr/share/icons/DMZ-Black/cursors/sb_v_double_arrow +usr/share/icons/DMZ-Black/cursors/se-resize +usr/share/icons/DMZ-Black/cursors/size_bdiag +usr/share/icons/DMZ-Black/cursors/size_fdiag +usr/share/icons/DMZ-Black/cursors/size_hor +usr/share/icons/DMZ-Black/cursors/size_ver +usr/share/icons/DMZ-Black/cursors/sw-resize +usr/share/icons/DMZ-Black/cursors/target +usr/share/icons/DMZ-Black/cursors/tcross +usr/share/icons/DMZ-Black/cursors/text +usr/share/icons/DMZ-Black/cursors/top_left_arrow +usr/share/icons/DMZ-Black/cursors/top_left_corner +usr/share/icons/DMZ-Black/cursors/top_right_corner +usr/share/icons/DMZ-Black/cursors/top_side +usr/share/icons/DMZ-Black/cursors/top_tee +usr/share/icons/DMZ-Black/cursors/ul_angle +usr/share/icons/DMZ-Black/cursors/ur_angle +usr/share/icons/DMZ-Black/cursors/v_double_arrow +usr/share/icons/DMZ-Black/cursors/w-resize +usr/share/icons/DMZ-Black/cursors/wait +usr/share/icons/DMZ-Black/cursors/watch +usr/share/icons/DMZ-Black/cursors/xterm +usr/share/icons/DMZ-White/ +usr/share/icons/DMZ-White/cursor.theme +usr/share/icons/DMZ-White/cursors/ +usr/share/icons/DMZ-White/cursors/00008160000006810000408080010102 +usr/share/icons/DMZ-White/cursors/028006030e0e7ebffc7f7070c0600140 +usr/share/icons/DMZ-White/cursors/03b6e0fcb3499374a867c041f52298f0 +usr/share/icons/DMZ-White/cursors/08e8e1c95fe2fc01f976f1e063a24ccd +usr/share/icons/DMZ-White/cursors/1081e37283d90000800003c07f3ef6bf +usr/share/icons/DMZ-White/cursors/14fef782d02440884392942c11205230 +usr/share/icons/DMZ-White/cursors/2870a09082c103050810ffdffffe0204 +usr/share/icons/DMZ-White/cursors/3085a0e285430894940527032f8b26df +usr/share/icons/DMZ-White/cursors/3ecb610c1bf2410f44200f48c40d3599 +usr/share/icons/DMZ-White/cursors/4498f0e0c1937ffe01fd06f973665830 +usr/share/icons/DMZ-White/cursors/5c6cd98b3f3ebcb1f9c7f1c204630408 +usr/share/icons/DMZ-White/cursors/6407b0e94181790501fd1e167b474872 +usr/share/icons/DMZ-White/cursors/640fb0e74195791501fd1ed57b41487f +usr/share/icons/DMZ-White/cursors/9081237383d90e509aa00f00170e968f +usr/share/icons/DMZ-White/cursors/9d800788f1b08800ae810202380a0822 +usr/share/icons/DMZ-White/cursors/X_cursor +usr/share/icons/DMZ-White/cursors/alias +usr/share/icons/DMZ-White/cursors/arrow +usr/share/icons/DMZ-White/cursors/bd_double_arrow +usr/share/icons/DMZ-White/cursors/bottom_left_corner +usr/share/icons/DMZ-White/cursors/bottom_right_corner +usr/share/icons/DMZ-White/cursors/bottom_side +usr/share/icons/DMZ-White/cursors/bottom_tee +usr/share/icons/DMZ-White/cursors/c7088f0f3e6c8088236ef8e1e3e70000 +usr/share/icons/DMZ-White/cursors/circle +usr/share/icons/DMZ-White/cursors/col-resize +usr/share/icons/DMZ-White/cursors/color-picker +usr/share/icons/DMZ-White/cursors/copy +usr/share/icons/DMZ-White/cursors/cross +usr/share/icons/DMZ-White/cursors/cross_reverse +usr/share/icons/DMZ-White/cursors/crossed_circle +usr/share/icons/DMZ-White/cursors/crosshair +usr/share/icons/DMZ-White/cursors/d9ce0ab605698f320427677b458ad60b +usr/share/icons/DMZ-White/cursors/default +usr/share/icons/DMZ-White/cursors/diamond_cross +usr/share/icons/DMZ-White/cursors/dnd-ask +usr/share/icons/DMZ-White/cursors/dnd-copy +usr/share/icons/DMZ-White/cursors/dnd-link +usr/share/icons/DMZ-White/cursors/dnd-move +usr/share/icons/DMZ-White/cursors/dnd-none +usr/share/icons/DMZ-White/cursors/dot_box_mask +usr/share/icons/DMZ-White/cursors/dotbox +usr/share/icons/DMZ-White/cursors/double_arrow +usr/share/icons/DMZ-White/cursors/draft_large +usr/share/icons/DMZ-White/cursors/draft_small +usr/share/icons/DMZ-White/cursors/draped_box +usr/share/icons/DMZ-White/cursors/e-resize +usr/share/icons/DMZ-White/cursors/e29285e634086352946a0e7090d73106 +usr/share/icons/DMZ-White/cursors/ew-resize +usr/share/icons/DMZ-White/cursors/fcf1c3c7cd4491d801f1e1c78f100000 +usr/share/icons/DMZ-White/cursors/fd_double_arrow +usr/share/icons/DMZ-White/cursors/fleur +usr/share/icons/DMZ-White/cursors/grab +usr/share/icons/DMZ-White/cursors/grabbing +usr/share/icons/DMZ-White/cursors/h_double_arrow +usr/share/icons/DMZ-White/cursors/hand +usr/share/icons/DMZ-White/cursors/hand1 +usr/share/icons/DMZ-White/cursors/hand2 +usr/share/icons/DMZ-White/cursors/help +usr/share/icons/DMZ-White/cursors/icon +usr/share/icons/DMZ-White/cursors/left_ptr +usr/share/icons/DMZ-White/cursors/left_ptr_help +usr/share/icons/DMZ-White/cursors/left_ptr_watch +usr/share/icons/DMZ-White/cursors/left_side +usr/share/icons/DMZ-White/cursors/left_tee +usr/share/icons/DMZ-White/cursors/link +usr/share/icons/DMZ-White/cursors/ll_angle +usr/share/icons/DMZ-White/cursors/lr_angle +usr/share/icons/DMZ-White/cursors/move +usr/share/icons/DMZ-White/cursors/n-resize +usr/share/icons/DMZ-White/cursors/ne-resize +usr/share/icons/DMZ-White/cursors/nesw-resize +usr/share/icons/DMZ-White/cursors/not-allowed +usr/share/icons/DMZ-White/cursors/ns-resize +usr/share/icons/DMZ-White/cursors/nw-resize +usr/share/icons/DMZ-White/cursors/nwse-resize +usr/share/icons/DMZ-White/cursors/openhand +usr/share/icons/DMZ-White/cursors/pencil +usr/share/icons/DMZ-White/cursors/pirate +usr/share/icons/DMZ-White/cursors/plus +usr/share/icons/DMZ-White/cursors/pointer +usr/share/icons/DMZ-White/cursors/progress +usr/share/icons/DMZ-White/cursors/question_arrow +usr/share/icons/DMZ-White/cursors/right_ptr +usr/share/icons/DMZ-White/cursors/right_side +usr/share/icons/DMZ-White/cursors/right_tee +usr/share/icons/DMZ-White/cursors/row-resize +usr/share/icons/DMZ-White/cursors/s-resize +usr/share/icons/DMZ-White/cursors/sb_down_arrow +usr/share/icons/DMZ-White/cursors/sb_h_double_arrow +usr/share/icons/DMZ-White/cursors/sb_left_arrow +usr/share/icons/DMZ-White/cursors/sb_right_arrow +usr/share/icons/DMZ-White/cursors/sb_up_arrow +usr/share/icons/DMZ-White/cursors/sb_v_double_arrow +usr/share/icons/DMZ-White/cursors/se-resize +usr/share/icons/DMZ-White/cursors/size_bdiag +usr/share/icons/DMZ-White/cursors/size_fdiag +usr/share/icons/DMZ-White/cursors/size_hor +usr/share/icons/DMZ-White/cursors/size_ver +usr/share/icons/DMZ-White/cursors/sw-resize +usr/share/icons/DMZ-White/cursors/target +usr/share/icons/DMZ-White/cursors/tcross +usr/share/icons/DMZ-White/cursors/text +usr/share/icons/DMZ-White/cursors/top_left_arrow +usr/share/icons/DMZ-White/cursors/top_left_corner +usr/share/icons/DMZ-White/cursors/top_right_corner +usr/share/icons/DMZ-White/cursors/top_side +usr/share/icons/DMZ-White/cursors/top_tee +usr/share/icons/DMZ-White/cursors/ul_angle +usr/share/icons/DMZ-White/cursors/ur_angle +usr/share/icons/DMZ-White/cursors/v_double_arrow +usr/share/icons/DMZ-White/cursors/w-resize +usr/share/icons/DMZ-White/cursors/wait +usr/share/icons/DMZ-White/cursors/watch +usr/share/icons/DMZ-White/cursors/xterm +usr/share/licenses/ +usr/share/licenses/xcursor-dmz/ +usr/share/licenses/xcursor-dmz/LICENSE diff --git a/test/package/test_package.c b/test/package/test_package.c new file mode 100644 index 0000000..bcefdf6 --- /dev/null +++ b/test/package/test_package.c @@ -0,0 +1,97 @@ +#include "acutest.h" +#include "vieter_package_internal.h" + +void test_info_parse() { + FILE *f = fopen("./test/package/.PKGINFO", "r"); + TEST_ASSERT_(f != NULL, "could not find test .PKGINFO file in ./test/package "); + fseek(f, 0L, SEEK_END); + size_t size = ftell(f); + fflush(stdout); + rewind(f); + char *pkg_info_str = malloc(size + 1); + fread(pkg_info_str, 1, size, f); + pkg_info_str[size] = '\0'; + fclose(f); + + vieter_package_info *pkg_info = vieter_package_info_init(); + vieter_package_info_parse(pkg_info, pkg_info_str); + + TEST_CHECK(!strcmp(pkg_info->name, "xcursor-dmz")); + TEST_CHECK(!strcmp(pkg_info->base, "xcursor-dmz")); + TEST_CHECK(!strcmp(pkg_info->version, "0.4.5-2")); + TEST_CHECK(!strcmp(pkg_info->description, "Style neutral, scalable cursor theme")); + TEST_CHECK(!strcmp(pkg_info->url, "https://packages.debian.org/sid/dmz-cursor-theme")); + TEST_CHECK(pkg_info->build_date == 1673751613); + TEST_CHECK(!strcmp(pkg_info->packager, "Unknown Packager")); + TEST_CHECK(pkg_info->size == 3469584); + TEST_CHECK(!strcmp(pkg_info->arch, "any")); + + TEST_CHECK(!strcmp(pkg_info->licenses->array[0], "MIT")); + TEST_CHECK(!strcmp(pkg_info->replaces->array[0], "test1")); + TEST_CHECK(!strcmp(pkg_info->groups->array[0], "x11")); + TEST_CHECK(!strcmp(pkg_info->conflicts->array[0], "test2")); + TEST_CHECK(!strcmp(pkg_info->conflicts->array[1], "test3")); + TEST_CHECK(!strcmp(pkg_info->provides->array[0], "test4")); + TEST_CHECK(!strcmp(pkg_info->depends->array[0], "test5")); + TEST_CHECK(!strcmp(pkg_info->depends->array[1], "test6")); + TEST_CHECK(!strcmp(pkg_info->optdepends->array[0], "test7")); + TEST_CHECK(!strcmp(pkg_info->makedepends->array[0], "xorg-xcursorgen")); + TEST_CHECK(!strcmp(pkg_info->checkdepends->array[0], "test8")); + + free(pkg_info_str); + vieter_package_info_free(pkg_info); +} + +void test_pkg_read_archive_files() { + vieter_package *pkg = vieter_package_init(); + vieter_package_error e = vieter_package_read_archive(pkg, "./test/package/xcursor-dmz-0.4.5-2-any.pkg.tar.zst"); + TEST_ASSERT_(e == vieter_package_ok, "failure parsing pkg archive"); + + FILE *f = fopen("./test/package/files", "r"); + TEST_ASSERT_(f != NULL, "could not find test files file in ./test/package"); + char buff[128]; + size_t i = 0; + + while ((fgets(buff, 128, f)) != NULL || i < pkg->files->size) { + if (buff[strlen(buff) - 1] == '\n') { + buff[strlen(buff) - 1] = '\0'; + } + + TEST_CHECK_(!strcmp(pkg->files->array[i], buff), "%s != %s", pkg->files->array[i], buff); + i++; + } + TEST_CHECK(pkg->compression = 14); + + vieter_package_free(&pkg); +} + +void test_pkg_read_archive_desc() { + vieter_package *pkg = vieter_package_init(); + vieter_package_error e = vieter_package_read_archive(pkg, "./test/package/xcursor-dmz-0.4.5-2-any.pkg.tar.zst"); + TEST_ASSERT_(e == vieter_package_ok, "failure parsing pkg archive"); + + char *description = vieter_package_to_description(pkg); + + FILE *f = fopen("./test/package/desc", "r"); + TEST_ASSERT_(f != NULL, "could not find test desc file in ./test/package"); + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + rewind(f); + char *desc = malloc(size + 1); + fread(desc, 1, size, f); + desc[size] = '\0'; + fclose(f); + + TEST_CHECK(!strcmp(description, desc)); + + vieter_package_free(&pkg); + free(description); + free(desc); +} + +TEST_LIST = { + {".PKGINFO parse", test_info_parse}, + {"files array creation", test_pkg_read_archive_files}, + {"desc file creation", test_pkg_read_archive_desc}, + {NULL, NULL} +}; diff --git a/test/package/xcursor-dmz-0.4.5-2-any.pkg.tar.zst b/test/package/xcursor-dmz-0.4.5-2-any.pkg.tar.zst new file mode 100644 index 0000000..a878047 Binary files /dev/null and b/test/package/xcursor-dmz-0.4.5-2-any.pkg.tar.zst differ diff --git a/thirdparty/include/sha256.h b/thirdparty/include/sha256.h new file mode 100644 index 0000000..7123a30 --- /dev/null +++ b/thirdparty/include/sha256.h @@ -0,0 +1,34 @@ +/********************************************************************* +* Filename: sha256.h +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Defines the API for the corresponding SHA1 implementation. +*********************************************************************/ + +#ifndef SHA256_H +#define SHA256_H + +/*************************** HEADER FILES ***************************/ +#include + +/****************************** MACROS ******************************/ +#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest + +/**************************** DATA TYPES ****************************/ +typedef unsigned char BYTE; // 8-bit byte +typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines + +typedef struct { + BYTE data[64]; + WORD datalen; + unsigned long long bitlen; + WORD state[8]; +} SHA256_CTX; + +/*********************** FUNCTION DECLARATIONS **********************/ +void sha256_init(SHA256_CTX *ctx); +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); +void sha256_final(SHA256_CTX *ctx, BYTE hash[]); + +#endif // SHA256_H diff --git a/thirdparty/src/sha256.c b/thirdparty/src/sha256.c new file mode 100644 index 0000000..eb9c5c0 --- /dev/null +++ b/thirdparty/src/sha256.c @@ -0,0 +1,158 @@ +/********************************************************************* +* Filename: sha256.c +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Implementation of the SHA-256 hashing algorithm. + SHA-256 is one of the three algorithms in the SHA2 + specification. The others, SHA-384 and SHA-512, are not + offered in this implementation. + Algorithm specification can be found here: + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf + This implementation uses little endian byte order. +*********************************************************************/ + +/*************************** HEADER FILES ***************************/ +#include +#include +#include "sha256.h" + +/****************************** MACROS ******************************/ +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) + +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +/**************************** VARIABLES *****************************/ +static const WORD k[64] = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) +{ + WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + for ( ; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void sha256_init(SHA256_CTX *ctx) +{ + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) +{ + WORD i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + sha256_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void sha256_final(SHA256_CTX *ctx, BYTE hash[]) +{ + WORD i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) + ctx->data[i++] = 0x00; + } + else { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + sha256_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = ctx->bitlen; + ctx->data[62] = ctx->bitlen >> 8; + ctx->data[61] = ctx->bitlen >> 16; + ctx->data[60] = ctx->bitlen >> 24; + ctx->data[59] = ctx->bitlen >> 32; + ctx->data[58] = ctx->bitlen >> 40; + ctx->data[57] = ctx->bitlen >> 48; + ctx->data[56] = ctx->bitlen >> 56; + sha256_transform(ctx, ctx->data); + + // Since this implementation uses little endian byte ordering and SHA uses big endian, + // reverse all the bytes when copying the final state to the output hash. + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +}