+
+
+
+
+
+
+
+
+
+
+
+
+
+Telodendria | A Matrix Homeserver written in ANSI C.
+
+
+
Telodendria
+
+Telodendria: The terminal branches of an axon.
+
+
+Note:Telodendria is under heavy development.
+Please see the Project Status.
+
+
+Telodendria is a Matrix homeserver implementation written from
+scratch in ANSI C. It is designed to be lightweight and simple, yet
+functional. Telodendria differentiates itself from other Matrix
+homeserver implementations because it:
+
+
+Is written in C, a stable, low-level programming language with a long
+history, low build and runtime overhead, and wide compatibility.
+
+
+Is written with minimalism as a primary design goal. Whenever possible
+and practical, no third-party libraries are pulled in to the source
+code. Everything Telodendria needs is custom written. As a
+result, Telodendria depends only on a standard C compiler and
+library to be built, and only a web server with CGI capabilities to
+run.
+
+
+Uses a flat-file directory structure to store data instead of a
+database. This has a number of advantages:
+
+
It makes setup and maintenance much easier.
+
+It allows Telodendria to run on systems with fewer resources.
+
+
+It provides both runtime and data safety and stability. Since no
+database is running, there's fewer things that could go wrong because
+there's a lot less code running on the system.
+
+
+
+
+Runs as a CGI application. Telodendria is delivered as a single
+small, highly-optimized binary that can be dropped in a web server's
+web root to be executed. This allows it to consume very few resources
+and be very easy to set up.
+
+
+
+Telodendria is on Matrix! Check out the official Matrix rooms:
+
+
+
+
Room
+
Description
+
+
+
+#telodendria-releases:bancino.net
+
+
+Get notified of new releases.
+
+
+
+
+#telodendria-general:bancino.net
+
+
+General discussion and support for Telodendria.
+
+
+
+
+#telodendria-issues:bancino.net
+
+
+Report issues with Telodendria.
+
+
+
+
+#telodendria-patches:bancino.net
+
+
+Submit code patches to the Telodendria project.
+
+If your operating system has an official package or port of
+Telodendria, you should prefer to use that. If your operating
+system's package or port is too out of date for your tastes, please
+contact the package's maintainers to notify them, or offer to update
+the package yourself.
+
+
+If your operating system does not have an official package, see below
+for instructions on building from source and use them to create one.
+
+
OpenBSD
+
+Telodendria is available in the ports tree and as a binary
+package. You can install it with the following command:
+
+
+$ pkg_add telodendria
+
+
Building From Source
+
+Telodendria is designed to be light enough that it can be built
+from source on just about any operating system. It only has the
+following requirements, all of which should be already available to
+you on a sufficiently complete operating system:
+
+
+
+A standards-compliant C compiler with the C standard library. Because
+Telodendria is written in ANSI C, it should compile on just about
+any compiler, but the following compilers are known to work:
+
+
GCC
+
Clang
+
+Tiny C Compiler (Note: must edit make.sh and remove
+-Wl,-static -Wl,-gc-sections from LDFLAGS)
+
+
+Other compilers should work as well, but you may have to play with the
+flags in make.sh.
+
+
+POSIX base utilities, including find, stat,
+env, and compliant sh-like shell.
+
+
+
+$ ./make.sh
+
+
+If everything went well, that will produce
+telodendria.cgi, which you can then place under your web
+root and configure your web server to execute. You'll need to make sure
+/.well-known/matrix and /_matrix and all the
+paths under them actually execute telodendria.cgi. See the
+provided OpenBSD httpd.conf for reference. Even if you
+aren't using OpenBSD's httpd(8), you should find its
+configuration syntax simple enough to adequately demonstrate the proper
+configuration.
+
+
Configure Telodendria
+
+Once you get Telodendria built and hooked into your web server,
+you will have to write a configuration file for it. The configuration
+file is just JSON, and it should be called
+Telodendria.json.
+
+
Project Status
+
+Telodendria is a very ambitious project. There's a lot that needs
+to happen yet before it is even remotely usable. At the moment, there's
+nothing that even remotely resembles a Matrix homeserver here; we're still
+getting off the ground and building a foundation.
+
+
+Just because there's nothing here yet doesn't mean you should go away
+though! We desparately need help, so you are more than welcome to help
+out if you want things to go quicker. Please see the
+Contributing section for details on how you
+can get involved.
+
+
Phase 1: Getting Off The Ground
+
+
Name this project
+
Set up a CVS repository
+
Make CVS repository public
+
Write a coding style guide
+
Write a build script
+
Add a license
+
Add support and issue reporting guide
+
Add table of contents to this document
+
+
Phase 2: Building A Foundation
+
+
Implement an array
+
Implement a logging facility
+
Implement argument parsing (-c file -Vh)
+
Implement a hash map
+
Combine library code files
+
Implement configuration file parsing using the hash map
+
Implement a JSON library using the hash map and array
+
Figure out how to write unit tests for array/hashmap/etc
+
Implement a simple HTTP server
+
+Design the server architecture
+
+
Route requests
+
Handle requests
+
Data abstraction layer
+
Error generation
+
+
+
+
Phase 3: Welcome To Matrix
+
+
+Implement the Client-Server API
+
+
+Implement the Server-Server API
+
+
+Implement the other Matrix APIs
+
+
+
Phase 4: A Real Homeserver
+
+
+Create an OpenBSD package and get it submitted to ports
+
+
+Create a command line tool to manage Telodendria
+
+
Configuration file generation
+
User management
+
Room management
+
+
+
+Migrate from Synapse. I run a Synapse homeserver right now, so somehow
+I have to get all my data into the Telodendria format.
+
+
+
Documentation Status
+
+This documentation needs just a little work. Here's the things
+on my list for that:
+
+
+
Update Rationale section
+
Update Project description (no longer a CGI binary)
+I want a lightweight Matrix homeserver designed for OpenBSD. I want a
+homeserver that can be developed in vi(1) and compiled
+with a C compiler. I want it to function entirely on a base OpenBSD
+install without having to install any extra packages whatsoever. I've
+found that the existing homeserver implementations are way
+over-engineered and written in such a way that many programs and
+libraries have to be pulled in to use them. I also want to learn how
+Matrix works, and I want to understand the code I'm running on my
+server.
+
+
+So I wrote Telodendria.
+
+
+Telodendria is written entirely in portable ANSI C. It depends on no
+third-party C libraries other than the standard C library. The only
+thing you need to run it is a web server that supports executing CGI
+programs, and a directory that data can be written to. Everything
+Telodendria needs to run itself is compiled into a single static
+binary, and the source code can be built anywhere, right out of the
+box.
+
+
+Telodendria doesn't use a database like all the other homeservers.
+Instead, it operates more like email: it uses a flat-file data
+structure similar to maildir to store data. The advantage of this is
+that it saves server maintainers from also having to maintain a
+database. It greatly simplifies the process of getting a Matrix
+homeserver up and running, and it makes it highly portable. It also is
+extremely easy to back up and restore with base tools; just
+tar(1) up the directory, and you're good to go.
+
+
+Telodendria is developed and tested on OpenBSD, but you'll find that it
+should run under any web server that supports CGI. I chose to write
+Telodendria as a CGI program because anyone running an existing Matrix
+server is likely running a web server acting as a reverse proxy in
+front of it anyway, so why not just hook the homeserver directly into
+the web server? That's one less daemon to run, which means memory and
+CPU savings. CGI also allows Telodendria to remain single-threaded.
+Each request that comes in is handled as its own process, and
+operations are entirely isolated.
+
+
Project Goals
+
+The goals of this project are as follows:
+
+
+
+To be a production-ready Matrix server capable of handling a lot of
+users. Telodendria should have good performance in many diverse
+environments.
+
+
+To have as few external build and run dependencies as possible. It
+should be possible to compile Telodendria on any operating system out
+of the box, and have it be totally statically linked, ready to run
+under a chroot(8)-ed web server. You'll even notice that
+the documentation is written in HTML directly, not Markdown, to remove
+the dependency on a Markdown parser and renderer.
+
+
+To be written in clean, elegant, and well-documented code. The goal is
+to build a Matrix homeserver from the ground up, not just because I
+don't the way existing homeservers are implemented, but also so I can
+learn how Matrix really works, and maybe even teach others along the
+way.
+
+
+
Getting Support
+
+Telodendria is designed to be fairly straightforward, but that
+doesn't mean there won't be hiccups along the way. If you are struggling
+to get Telodendria up and running, you're more than welcome to
+reach out for support. Just join the
+#telodendria-general:bancino.net Matrix channel. Before
+you do though, make sure you're running the latest version of
+Telodendria and you've thoroughly read through all the
+relevant documentation.
+
+
Contributing
+
+Telodendria is an open source project. As such, it welcomes
+contributions. There are many ways you can contribute, and any way you
+can is greatly appreciated.
+
+
Reporting Issues
+
+If—after you've reached out to
+#telodendria-general:bancino.net—it has been
+determined that there is a problem with Telodendria, it should
+be reported to #telodendria-issues:bancino.net. There it
+can be discussed further. The issues channel serves as the official
+issue tracker of Telodendria; although issues may be copied
+into a TODO file in the CVS repository just so they
+don't get lost.
+
+
Developing
+
+The primary language used to write Telodendria code is ANSI C.
+Yes, that's the original C standard from 1989. The reason this standard
+is chosen, and the reason that it will not be changed, is because the
+original C is the most portable. Other languages you'll find in the
+Telodendria repository are shell scripts and HTML. If you have
+any experience at all with any of these languages, your contributions
+are valuable. Please follow the guidelines in this section to ensure
+the contribution workflow goes as smoothly as possible.
+
+
Getting The Code
+
+There are multiple ways to get the source code for Telodendria.
+You can download an official release tarball from
+here if you would like,
+but the preferred way is to check out the source code from CVS. This
+makes generating patches a lot easier. If you do not have CVS, consult
+your operating system's package repository to install it. CVS was the
+chosen version control system for this project primarily because it is
+built into OpenBSD.
+
+You should now have the latest Telodendria source code. Follow
+the Code Style as you make your changes.
+
+
Code Style
+
+Telodendria's code style is very unique. In general, these are
+the conventions used by the code base.
+
+
+
+All function, enumeration, structure, and header names are
+CamelCase. This is preferred to snake_case
+because it is more compact.
+
+
+enums and structs are always
+typedef-ed to their same name. The typedef
+occurs in the public API header, and the actual declaration occurs in
+the private implementation header.
+
+
+Indentation is done with spaces. This ensures that files look the same
+for everyone. It also makes line wrapping rules much easier because
+the indentations are the same size. Please configure your editor to
+make the Tab key insert spaces, and if it does automatic
+indentation, make sure it indents with spaces. If you cannot configure
+your editor to insert spaces, then you can try running the code files
+you were working on through expand. A unit of indentation
+is 4 spaces.
+
+
+Lines should not exceed 72 characters, including indentations. Some
+developers use vi(1) in an 80x24 terminal to write code.
+
+
+
+This guide may be subject to change. The source code is the absolute
+source of truth, so as long as you make your code look like the
+code surrounding it, you should be fine.
+
+
Submitting Patches
+
+Submitting patches is fairly easy to do if you've got the CVS sources
+checked out. Once you have made your changes, just run
+cvs diff:
+
+
+$ cvs diff -uNp > your-changes.patch
+
+
+Then, send the resulting patches to
+#telodendria-patches:bancino.net, where they will be
+promptly reviewed by the community.
+
+
License
+
+All of the code and documentation for Telodendria is licensed
+under the following terms and conditions:
+
+
+Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
Change Log
+
+At this time, Telodendria does not have any tagged releases because it
+is not yet functional as a Matrix homeserver. Please check out the Project Status to see where things are
+currently at.
+
+
+
diff --git a/contrib/httpd.conf b/contrib/httpd.conf
new file mode 100644
index 0000000..bc1297f
--- /dev/null
+++ b/contrib/httpd.conf
@@ -0,0 +1,9 @@
+#
+# httpd.conf: An OpenBSD httpd(8) configuration file for running
+# Telodendria. Note that this is a development configuration that
+# should be adapted using the httpd.conf(5) man page for production
+# use.
+#
+server "matrix" {
+ listen on localhost port http
+}
diff --git a/contrib/telodendria.conf b/contrib/telodendria.conf
new file mode 100644
index 0000000..8d32a41
--- /dev/null
+++ b/contrib/telodendria.conf
@@ -0,0 +1,13 @@
+# Telodendria configuration file
+
+log "/var/log/telodendria.log" {
+ level "message";
+ timestampFormat "none";
+ color "true";
+};
+
+threads "4";
+
+data-dir "/var/telodendria";
+
+federation "true";
\ No newline at end of file
diff --git a/make.sh b/make.sh
new file mode 100644
index 0000000..dc24dd2
--- /dev/null
+++ b/make.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env sh
+
+TELODENDRIA_VERSION="0.0.1"
+
+HEADERS="-D_POSIX_C_SOURCE=199506L -DTELODENDRIA_VERSION=\"$TELODENDRIA_VERSION\""
+INCLUDES="-Isrc/include"
+
+CC="${CC:-cc}"
+CFLAGS="-Wall -Werror -pedantic -std=c89 -O3 $HEADERS $INCLUDES"
+LDFLAGS="-static -flto -fdata-sections -ffunction-sections -s -Wl,-static -Wl,-gc-sections"
+PROG="telodendria"
+
+mod_time() {
+ if [ -n "$1" ] && [ -f "$1" ]; then
+ case "$(uname)" in
+ Linux)
+ stat -c %Y "$1"
+ ;;
+ *BSD)
+ stat -f %m "$1"
+ ;;
+ *)
+ echo "0"
+ ;;
+ esac
+ else
+ echo "0"
+ fi
+}
+
+mkdir -p build
+
+do_rebuild=0
+objs=""
+for src in $(find src -name '*.c'); do
+ obj=$(echo "$src" | sed -e 's/^src/build/' -e 's/\.c$/\.o/')
+ objs="$objs $obj"
+
+ if [ $(mod_time "$src") -gt $(mod_time "$obj") ]; then
+ echo "CC $obj"
+ obj_dir=$(dirname "$obj")
+ mkdir -p "$obj_dir"
+ if ! $CC $CFLAGS -c -o "$obj" "$src"; then
+ exit 1
+ fi
+ do_rebuild=1
+ fi
+done
+
+if [ $do_rebuild -eq 1 ] || [ ! -f "build/$PROG" ]; then
+ echo "LD build/$PROG"
+ $CC $LDFLAGS -o "build/$PROG" $objs
+else
+ echo "Up to date."
+fi
+
+ls -lh "build/$PROG"
diff --git a/src/Array.c b/src/Array.c
index a32c2ef..25e8db6 100644
--- a/src/Array.c
+++ b/src/Array.c
@@ -1,26 +1,3 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
#include
#ifndef ARRAY_BLOCK
@@ -28,18 +5,16 @@
#endif
#include
-#include
+#include
-struct Array
-{
- void **entries; /* An array of void pointers, to
- * store any data */
- size_t allocated; /* Elements allocated on the heap */
- size_t size; /* Elements actually filled */
+struct Array {
+ void **entries; /* An array of void pointers, to store any data */
+ size_t allocated; /* Elements allocated on the heap */
+ size_t size; /* Elements actually filled */
};
int
-ArrayAdd(Array * array, void *value)
+ArrayAdd(Array *array, void *value)
{
if (!array)
{
@@ -52,7 +27,7 @@ ArrayAdd(Array * array, void *value)
Array *
ArrayCreate(void)
{
- Array *array = Malloc(sizeof(Array));
+ Array *array = malloc(sizeof(Array));
if (!array)
{
@@ -61,11 +36,11 @@ ArrayCreate(void)
array->size = 0;
array->allocated = ARRAY_BLOCK;
- array->entries = Malloc(sizeof(void *) * ARRAY_BLOCK);
+ array->entries = malloc(sizeof(void *) * ARRAY_BLOCK);
if (!array->entries)
{
- Free(array);
+ free(array);
return NULL;
}
@@ -73,12 +48,12 @@ ArrayCreate(void)
}
void *
-ArrayDelete(Array * array, size_t index)
+ArrayDelete(Array *array, size_t index)
{
size_t i;
void *element;
- if (!array || array->size <= index)
+ if (!array)
{
return NULL;
}
@@ -96,17 +71,17 @@ ArrayDelete(Array * array, size_t index)
}
void
-ArrayFree(Array * array)
+ArrayFree(Array *array)
{
if (array)
{
- Free(array->entries);
- Free(array);
+ free(array->entries);
+ free(array);
}
}
void *
-ArrayGet(Array * array, size_t index)
+ArrayGet(Array *array, size_t index)
{
if (!array)
{
@@ -123,7 +98,7 @@ ArrayGet(Array * array, size_t index)
extern int
-ArrayInsert(Array * array, void *value, size_t index)
+ArrayInsert(Array *array, void *value, size_t index)
{
size_t i;
@@ -139,7 +114,7 @@ ArrayInsert(Array * array, void *value, size_t index)
tmp = array->entries;
- array->entries = Realloc(array->entries,
+ array->entries = realloc(array->entries,
sizeof(void *) * newSize);
if (!array->entries)
@@ -164,7 +139,7 @@ ArrayInsert(Array * array, void *value, size_t index)
}
size_t
-ArraySize(Array * array)
+ArraySize(Array *array)
{
if (!array)
{
@@ -175,7 +150,7 @@ ArraySize(Array * array)
}
int
-ArrayTrim(Array * array)
+ArrayTrim(Array *array)
{
void **tmp;
@@ -186,7 +161,7 @@ ArrayTrim(Array * array)
tmp = array->entries;
- array->entries = Realloc(array->entries,
+ array->entries = realloc(array->entries,
sizeof(void *) * array->size);
if (!array->entries)
@@ -197,53 +172,3 @@ ArrayTrim(Array * array)
return 1;
}
-
-static void
-ArraySwap(Array * array, size_t i, size_t j)
-{
- void *p = array->entries[i];
-
- array->entries[i] = array->entries[j];
- array->entries[j] = p;
-}
-
-static size_t
-ArrayPartition(Array * array, size_t low, size_t high, int (*compare) (void *, void *))
-{
- void *pivot = array->entries[high];
- size_t i = low - 1;
- size_t j;
-
- for (j = low; j <= high - 1; j++)
- {
- if (compare(array->entries[j], pivot) < 0)
- {
- i++;
- ArraySwap(array, i, j);
- }
- }
- ArraySwap(array, i + 1, high);
- return i + 1;
-}
-
-static void
-ArrayQuickSort(Array * array, size_t low, size_t high, int (*compare) (void *, void *))
-{
- if (low < high)
- {
- size_t pi = ArrayPartition(array, low, high, compare);
-
- ArrayQuickSort(array, low, pi - 1, compare);
- ArrayQuickSort(array, pi + 1, high, compare);
- }
-}
-
-void
-ArraySort(Array * array, int (*compare) (void *, void *))
-{
- if (!array)
- {
- return;
- }
- ArrayQuickSort(array, 0, array->size, compare);
-}
diff --git a/src/Base64.c b/src/Base64.c
index bf9ee5b..12b60aa 100644
--- a/src/Base64.c
+++ b/src/Base64.c
@@ -1,40 +1,17 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
#include
-#include
+#include
static const char Base64EncodeMap[] =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const int Base64DecodeMap[] = {
- 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58,
- 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5,
- 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
- 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
- 43, 44, 45, 46, 47, 48, 49, 50, 51
+ 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51
};
size_t
@@ -66,14 +43,10 @@ Base64DecodedSize(const char *base64, size_t len)
ret = len / 4 * 3;
- for (i = len; i > 0; i--)
- {
- if (base64[i] == '=')
- {
+ for (i = len; i > 0; i--) {
+ if (base64[i] == '=') {
ret--;
- }
- else
- {
+ } else {
break;
}
}
@@ -94,7 +67,7 @@ Base64Encode(const char *input, size_t len)
}
outLen = Base64EncodedSize(len);
- out = Malloc(outLen + 1);
+ out = malloc(outLen + 1);
if (!out)
{
return NULL;
@@ -113,17 +86,12 @@ Base64Encode(const char *input, size_t len)
if (i + 1 < len)
{
out[j + 2] = Base64EncodeMap[(v >> 6) & 0x3F];
- }
- else
- {
+ } else {
out[j + 2] = '=';
}
- if (i + 2 < len)
- {
+ if (i + 2 < len) {
out[j + 3] = Base64EncodeMap[v & 0x3F];
- }
- else
- {
+ } else {
out[j + 3] = '=';
}
}
@@ -135,11 +103,11 @@ static int
Base64IsValidChar(char c)
{
return (c >= '0' && c <= '9') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= 'a' && c <= 'z') ||
- (c == '+') ||
- (c == '/') ||
- (c == '=');
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c == '+') ||
+ (c == '/') ||
+ (c == '=');
}
char *
@@ -158,11 +126,11 @@ Base64Decode(const char *input, size_t len)
outLen = Base64DecodedSize(input, len);
if (len % 4)
{
- /* Invalid length; must have incorrect padding */
+ /* Invalid length; must have incorrect padding */
return NULL;
}
- /* Scan for invalid characters. */
+ /* Scan for invalid characters. */
for (i = 0; i < len; i++)
{
if (!Base64IsValidChar(input[i]))
@@ -171,7 +139,7 @@ Base64Decode(const char *input, size_t len)
}
}
- out = Malloc(outLen + 1);
+ out = malloc(outLen + 1);
if (!out)
{
return NULL;
@@ -179,8 +147,7 @@ Base64Decode(const char *input, size_t len)
out[outLen] = '\0';
- for (i = 0, j = 0; i < len; i += 4, j += 3)
- {
+ for (i = 0, j = 0; i < len; i += 4, j += 3) {
v = Base64DecodeMap[input[i] - 43];
v = (v << 6) | Base64DecodeMap[input[i + 1] - 43];
v = input[i + 2] == '=' ? v << 6 : (v << 6) | Base64DecodeMap[input[i + 2] - 43];
@@ -221,17 +188,17 @@ Base64Pad(char **base64Ptr, size_t length)
if (length % 4 == 0)
{
- return length; /* Success: no padding needed */
+ return length; /* Success: no padding needed */
}
newSize = length + (4 - (length % 4));
- tmp = Realloc(*base64Ptr, newSize + 100);;
+ tmp = realloc(*base64Ptr, newSize + 100);;
if (!tmp)
{
- return 0; /* Memory error */
+ return 0; /* Memory error */
}
- *base64Ptr = tmp;
+ *base64Ptr = tmp;
for (i = length; i < newSize; i++)
{
@@ -242,3 +209,4 @@ Base64Pad(char **base64Ptr, size_t length)
return newSize;
}
+
diff --git a/src/Config.c b/src/Config.c
index 8dabc94..57b06b2 100644
--- a/src/Config.c
+++ b/src/Config.c
@@ -1,30 +1,5 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
#include
-#include
-
#include
#include
#include
@@ -33,24 +8,20 @@
#define CONFIG_BUFFER_BLOCK 32
#endif
-struct ConfigDirective
-{
+struct ConfigDirective {
Array *values;
HashMap *children;
};
-struct ConfigParseResult
-{
- unsigned int ok:1;
- union
- {
+struct ConfigParseResult {
+ unsigned int ok : 1;
+ union {
size_t lineNumber;
HashMap *confMap;
} data;
};
-typedef enum ConfigToken
-{
+typedef enum ConfigToken {
TOKEN_UNKNOWN,
TOKEN_NAME,
TOKEN_MACRO_ASSIGNMENT,
@@ -62,14 +33,13 @@ typedef enum ConfigToken
TOKEN_EOF
} ConfigToken;
-typedef struct ConfigParserState
-{
- FILE *stream;
- unsigned int line;
+typedef struct ConfigParserState {
+ FILE *stream;
+ unsigned int line;
- char *token;
- size_t tokenSize;
- size_t tokenLen;
+ char *token;
+ size_t tokenSize;
+ size_t tokenLen;
ConfigToken tokenType;
HashMap *macroMap;
@@ -77,48 +47,54 @@ typedef struct ConfigParserState
} ConfigParserState;
unsigned int
-ConfigParseResultOk(ConfigParseResult * result)
+ConfigParseResultOk(ConfigParseResult *result)
{
return result ? result->ok : 0;
}
size_t
-ConfigParseResultLineNumber(ConfigParseResult * result)
+ConfigParseResultLineNumber(ConfigParseResult *result)
{
return result && !result->ok ? result->data.lineNumber : 0;
}
HashMap *
-ConfigParseResultGet(ConfigParseResult * result)
+ConfigParseResultGet(ConfigParseResult *result)
{
return result && result->ok ? result->data.confMap : NULL;
}
void
-ConfigParseResultFree(ConfigParseResult * result)
+ConfigParseResultFree(ConfigParseResult *result)
{
/*
* Note that if the parse was valid, the hash map
* needs to be freed separately.
*/
- Free(result);
+ free(result);
}
Array *
-ConfigValuesGet(ConfigDirective * directive)
+ConfigValuesGet(ConfigDirective *directive)
{
return directive ? directive->values : NULL;
}
HashMap *
-ConfigChildrenGet(ConfigDirective * directive)
+ConfigChildrenGet(ConfigDirective *directive)
{
return directive ? directive->children : NULL;
}
+/*
+ * Takes a void pointer because it is only used with
+ * HashMapIterate(), which requires a pointer to a function
+ * that takes a void pointer.
+ */
static void
-ConfigDirectiveFree(ConfigDirective * directive)
+ConfigDirectiveFree(void *ptr)
{
+ ConfigDirective *directive = ptr;
size_t i;
if (!directive)
@@ -128,36 +104,26 @@ ConfigDirectiveFree(ConfigDirective * directive)
for (i = 0; i < ArraySize(directive->values); i++)
{
- Free(ArrayGet(directive->values, i));
+ free(ArrayGet(directive->values, i));
}
ArrayFree(directive->values);
-
ConfigFree(directive->children);
- Free(directive);
+ free(directive);
}
void
-ConfigFree(HashMap * conf)
+ConfigFree(HashMap *conf)
{
- char *key;
- void *value;
-
- while (HashMapIterate(conf, &key, &value))
- {
- ConfigDirectiveFree((ConfigDirective *) value);
- Free(key);
- }
-
+ HashMapIterate(conf, ConfigDirectiveFree);
HashMapFree(conf);
}
static ConfigParserState *
ConfigParserStateCreate(FILE * stream)
{
- ConfigParserState *state = Malloc(sizeof(ConfigParserState));
-
+ ConfigParserState *state = malloc(sizeof(ConfigParserState));
if (!state)
{
return NULL;
@@ -167,7 +133,7 @@ ConfigParserStateCreate(FILE * stream)
if (!state->macroMap)
{
- Free(state);
+ free(state);
return NULL;
}
@@ -182,28 +148,19 @@ ConfigParserStateCreate(FILE * stream)
}
static void
-ConfigParserStateFree(ConfigParserState * state)
+ConfigParserStateFree(ConfigParserState *state)
{
- char *key;
- void *value;
-
if (!state)
{
return;
}
+ free(state->token);
- Free(state->token);
-
- while (HashMapIterate(state->macroMap, &key, &value))
- {
- Free(key);
- Free(value);
- }
-
+ HashMapIterate(state->macroMap, free);
HashMapFree(state->macroMap);
- Free(state);
+ free(state);
}
static int
@@ -213,10 +170,9 @@ ConfigIsNameChar(int c)
}
static char
-ConfigConsumeWhitespace(ConfigParserState * state)
+ConfigConsumeWhitespace(ConfigParserState *state)
{
int c;
-
while (isspace(c = fgetc(state->stream)))
{
if (c == '\n')
@@ -228,24 +184,22 @@ ConfigConsumeWhitespace(ConfigParserState * state)
}
static void
-ConfigConsumeLine(ConfigParserState * state)
+ConfigConsumeLine(ConfigParserState *state)
{
while (fgetc(state->stream) != '\n');
state->line++;
}
static void
-ConfigTokenSeek(ConfigParserState * state)
+ConfigTokenSeek(ConfigParserState *state)
{
int c;
/* If we already hit EOF, don't do anything */
- if (state->tokenType == TOKEN_EOF)
- {
+ if (state->tokenType == TOKEN_EOF) {
return;
}
- while ((c = ConfigConsumeWhitespace(state)) == '#')
- {
+ while ((c = ConfigConsumeWhitespace(state)) == '#') {
ConfigConsumeLine(state);
}
@@ -254,59 +208,47 @@ ConfigTokenSeek(ConfigParserState * state)
* token by looking at the next character
*/
- if (feof(state->stream))
- {
+ if (feof(state->stream)) {
state->tokenType = TOKEN_EOF;
return;
}
- if (ConfigIsNameChar(c))
- {
+ if (ConfigIsNameChar(c)) {
state->tokenLen = 0;
/* Read the key/macro into state->token */
- if (!state->token)
- {
+ if (!state->token) {
state->tokenSize = CONFIG_BUFFER_BLOCK;
- state->token = Malloc(CONFIG_BUFFER_BLOCK);
+ state->token = malloc(CONFIG_BUFFER_BLOCK);
}
state->token[state->tokenLen] = c;
state->tokenLen++;
- while (ConfigIsNameChar((c = fgetc(state->stream))))
- {
+ while (ConfigIsNameChar((c = fgetc(state->stream)))) {
state->token[state->tokenLen] = c;
state->tokenLen++;
- if (state->tokenLen >= state->tokenSize)
- {
+ if (state->tokenLen >= state->tokenSize) {
state->tokenSize += CONFIG_BUFFER_BLOCK;
- state->token = Realloc(state->token,
- state->tokenSize);
+ state->token = realloc(state->token,
+ state->tokenSize);
}
}
state->token[state->tokenLen] = '\0';
state->tokenLen++;
- if (!isspace(c))
- {
+ if (!isspace(c)) {
state->tokenType = TOKEN_UNKNOWN;
- }
- else
- {
+ } else {
state->tokenType = TOKEN_NAME;
- if (c == '\n')
- {
+ if (c == '\n') {
state->line++;
}
}
- }
- else
- {
- switch (c)
- {
+ } else {
+ switch (c) {
case '=':
state->tokenType = TOKEN_MACRO_ASSIGNMENT;
break;
@@ -315,29 +257,25 @@ ConfigTokenSeek(ConfigParserState * state)
state->tokenType = TOKEN_VALUE;
/* read the value into state->curtok */
- while ((c = fgetc(state->stream)) != '"')
- {
- if (c == '\n')
- {
+ while ((c = fgetc(state->stream)) != '"') {
+ if (c == '\n') {
state->line++;
}
/*
* End of the stream reached without finding
* a closing quote
*/
- if (feof(state->stream))
- {
+ if (feof(state->stream)) {
state->tokenType = TOKEN_EOF;
break;
}
state->token[state->tokenLen] = c;
state->tokenLen++;
- if (state->tokenLen >= state->tokenSize)
- {
+ if (state->tokenLen >= state->tokenSize) {
state->tokenSize += CONFIG_BUFFER_BLOCK;
- state->token = Realloc(state->token,
- state->tokenSize);
+ state->token = realloc(state->token,
+ state->tokenSize);
}
}
state->token[state->tokenLen] = '\0';
@@ -355,16 +293,14 @@ ConfigTokenSeek(ConfigParserState * state)
case '$':
state->tokenLen = 0;
/* read the macro name into state->curtok */
- while (ConfigIsNameChar(c = fgetc(state->stream)))
- {
+ while (ConfigIsNameChar(c = fgetc(state->stream))) {
state->token[state->tokenLen] = c;
state->tokenLen++;
- if (state->tokenLen >= state->tokenSize)
- {
+ if (state->tokenLen >= state->tokenSize) {
state->tokenSize += CONFIG_BUFFER_BLOCK;
- state->token = Realloc(state->token,
- state->tokenSize);
+ state->token = realloc(state->token,
+ state->tokenSize);
}
}
state->token[state->tokenLen] = '\0';
@@ -380,81 +316,67 @@ ConfigTokenSeek(ConfigParserState * state)
}
/* Resize curtok to only use the bytes it needs */
- if (state->tokenLen)
- {
+ if (state->tokenLen) {
state->tokenSize = state->tokenLen;
- state->token = Realloc(state->token, state->tokenSize);
+ state->token = realloc(state->token, state->tokenSize);
}
}
static int
-ConfigExpect(ConfigParserState * state, ConfigToken tokenType)
+ConfigExpect(ConfigParserState *state, ConfigToken tokenType)
{
return state->tokenType == tokenType;
}
static HashMap *
-ConfigParseBlock(ConfigParserState * state, int level)
+ConfigParseBlock(ConfigParserState *state, int level)
{
HashMap *block = HashMapCreate();
ConfigTokenSeek(state);
- while (ConfigExpect(state, TOKEN_NAME))
- {
- char *name = Malloc(state->tokenLen + 1);
-
+ while (ConfigExpect(state, TOKEN_NAME)) {
+ char *name = malloc(state->tokenLen + 1);
strcpy(name, state->token);
ConfigTokenSeek(state);
- if (ConfigExpect(state, TOKEN_VALUE) || ConfigExpect(state, TOKEN_MACRO))
- {
+ if (ConfigExpect(state, TOKEN_VALUE) || ConfigExpect(state, TOKEN_MACRO)) {
ConfigDirective *directive;
- directive = Malloc(sizeof(ConfigDirective));
+ directive = malloc(sizeof(ConfigDirective));
directive->children = NULL;
directive->values = ArrayCreate();
while (ConfigExpect(state, TOKEN_VALUE) ||
- ConfigExpect(state, TOKEN_MACRO))
- {
+ ConfigExpect(state, TOKEN_MACRO)) {
char *dval;
char *dvalCpy;
- if (ConfigExpect(state, TOKEN_VALUE))
- {
+ if (ConfigExpect(state, TOKEN_VALUE)) {
dval = state->token;
- }
- else if (ConfigExpect(state, TOKEN_MACRO))
- {
+ } else if (ConfigExpect(state, TOKEN_MACRO)) {
dval = HashMapGet(state->macroMap, state->token);
- if (!dval)
- {
+ if (!dval) {
goto error;
}
- }
- else
- {
- dval = NULL; /* Should never happen */
+ } else {
+ dval = NULL; /* Should never happen */
}
- /* dval is a pointer which is overwritten with the next
- * token. */
- dvalCpy = Malloc(strlen(dval) + 1);
+ /* dval is a pointer which is overwritten with the next token. */
+ dvalCpy = malloc(strlen(dval) + 1);
strcpy(dvalCpy, dval);
ArrayAdd(directive->values, dvalCpy);
ConfigTokenSeek(state);
}
- if (ConfigExpect(state, TOKEN_BLOCK_OPEN))
- {
+ if (ConfigExpect(state, TOKEN_BLOCK_OPEN)) {
/* token_seek(state); */
directive->children = ConfigParseBlock(state, level + 1);
- if (!directive->children)
- {
+ if (!directive->children) {
goto error;
}
}
@@ -469,49 +391,37 @@ ConfigParseBlock(ConfigParserState * state, int level)
* NULL is sent to ConfigDirectiveFree(), making it a no-op.
*/
ConfigDirectiveFree(HashMapSet(block, name, directive));
- }
- else if (ConfigExpect(state, TOKEN_MACRO_ASSIGNMENT))
- {
- ConfigTokenSeek(state);
- if (ConfigExpect(state, TOKEN_VALUE))
- {
- char *valueCopy = Malloc(strlen(state->token) + 1);
+ } else if (ConfigExpect(state, TOKEN_MACRO_ASSIGNMENT)) {
+ ConfigTokenSeek(state);
+ if (ConfigExpect(state, TOKEN_VALUE)) {
+ char * valueCopy = malloc(strlen(state->token) + 1);
strcpy(valueCopy, state->token);
- Free(HashMapSet(state->macroMap, name, valueCopy));
+ free(HashMapSet(state->macroMap, name, state->token));
ConfigTokenSeek(state);
- }
- else
- {
+ } else {
goto error;
}
- }
- else
- {
+ } else {
goto error;
}
- if (!ConfigExpect(state, TOKEN_SEMICOLON))
- {
+ if (!ConfigExpect(state, TOKEN_SEMICOLON)) {
goto error;
}
ConfigTokenSeek(state);
}
- if (ConfigExpect(state, level ? TOKEN_BLOCK_CLOSE : TOKEN_EOF))
- {
+ if (ConfigExpect(state, level ? TOKEN_BLOCK_CLOSE : TOKEN_EOF)) {
ConfigTokenSeek(state);
return block;
- }
- else
- {
+ } else {
goto error;
}
-error:
+ error:
/* Only free the very top level, because this will recurse */
- if (!level)
- {
+ if (!level) {
ConfigFree(block);
}
return NULL;
@@ -524,17 +434,14 @@ ConfigParse(FILE * stream)
HashMap *conf;
ConfigParserState *state;
- result = Malloc(sizeof(ConfigParseResult));
+ result = malloc(sizeof(ConfigParseResult));
state = ConfigParserStateCreate(stream);
conf = ConfigParseBlock(state, 0);
- if (!conf)
- {
+ if (!conf) {
result->ok = 0;
result->data.lineNumber = state->line;
- }
- else
- {
+ } else {
result->ok = 1;
result->data.confMap = conf;
}
@@ -542,3 +449,4 @@ ConfigParse(FILE * stream)
ConfigParserStateFree(state);
return result;
}
+
diff --git a/src/HashMap.c b/src/HashMap.c
index e09a5f7..351c4a2 100644
--- a/src/HashMap.c
+++ b/src/HashMap.c
@@ -1,61 +1,31 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
#include
-#include
-
+#include
#include
-#include
+#include
-typedef struct HashMapBucket
-{
- unsigned long hash;
- char *key;
+typedef struct HashMapBucket {
+ uint32_t hash;
void *value;
} HashMapBucket;
-struct HashMap
-{
+struct HashMap {
size_t count;
size_t capacity;
HashMapBucket **entries;
- unsigned long (*hashFunc) (const char *);
-
float maxLoad;
- size_t iterator;
};
-static unsigned long
+static uint32_t
HashMapHashKey(const char *key)
{
- unsigned long hash = 2166136261u;
+ uint32_t hash = 2166136261u;
size_t i = 0;
while (key[i])
{
- hash ^= (unsigned char) key[i];
+ hash ^= (uint8_t) key[i];
hash *= 16777619;
i++;
@@ -64,8 +34,9 @@ HashMapHashKey(const char *key)
return hash;
}
+
static int
-HashMapGrow(HashMap * map)
+HashMapGrow(HashMap *map)
{
size_t oldCapacity;
size_t i;
@@ -79,15 +50,12 @@ HashMapGrow(HashMap * map)
oldCapacity = map->capacity;
map->capacity *= 2;
- newEntries = Malloc(map->capacity * sizeof(HashMapBucket *));
+ newEntries = calloc(map->capacity, sizeof(HashMapBucket *));
if (!newEntries)
{
- map->capacity /= 2;
return 0;
}
- memset(&newEntries, 0, map->capacity * sizeof(HashMapBucket *));
-
for (i = 0; i < oldCapacity; i++)
{
/* If there is a value here, and it isn't a tombstone */
@@ -102,7 +70,7 @@ HashMapGrow(HashMap * map)
{
if (!newEntries[index]->hash)
{
- Free(newEntries[index]);
+ free(newEntries[index]);
newEntries[index] = map->entries[i];
break;
}
@@ -119,11 +87,11 @@ HashMapGrow(HashMap * map)
else
{
/* Either NULL or a tombstone */
- Free(map->entries[i]);
+ free(map->entries[i]);
}
}
- Free(map->entries);
+ free(map->entries);
map->entries = newEntries;
return 1;
}
@@ -131,8 +99,7 @@ HashMapGrow(HashMap * map)
HashMap *
HashMapCreate(void)
{
- HashMap *map = Malloc(sizeof(HashMap));
-
+ HashMap *map = malloc(sizeof(HashMap));
if (!map)
{
return NULL;
@@ -141,25 +108,21 @@ HashMapCreate(void)
map->maxLoad = 0.75;
map->count = 0;
map->capacity = 16;
- map->iterator = 0;
- map->hashFunc = HashMapHashKey;
- map->entries = Malloc(map->capacity * sizeof(HashMapBucket *));
+ map->entries = calloc(map->capacity, sizeof(HashMapBucket *));
if (!map->entries)
{
- Free(map);
+ free(map);
return NULL;
}
- memset(map->entries, 0, map->capacity * sizeof(HashMapBucket *));
-
return map;
}
void *
-HashMapDelete(HashMap * map, const char *key)
+HashMapDelete(HashMap *map, const char *key)
{
- unsigned long hash;
+ uint32_t hash;
size_t index;
if (!map || !key)
@@ -167,7 +130,7 @@ HashMapDelete(HashMap * map, const char *key)
return NULL;
}
- hash = map->hashFunc(key);
+ hash = HashMapHashKey(key);
index = hash % map->capacity;
for (;;)
@@ -192,7 +155,7 @@ HashMapDelete(HashMap * map, const char *key)
}
void
-HashMapFree(HashMap * map)
+HashMapFree(HashMap *map)
{
if (map)
{
@@ -202,18 +165,18 @@ HashMapFree(HashMap * map)
{
if (map->entries[i])
{
- Free(map->entries[i]);
+ free(map->entries[i]);
}
}
- Free(map->entries);
- Free(map);
}
+
+ free(map);
}
void *
-HashMapGet(HashMap * map, const char *key)
+HashMapGet(HashMap *map, const char *key)
{
- unsigned long hash;
+ uint32_t hash;
size_t index;
if (!map || !key)
@@ -221,7 +184,7 @@ HashMapGet(HashMap * map, const char *key)
return NULL;
}
- hash = map->hashFunc(key);
+ hash = HashMapHashKey(key);
index = hash % map->capacity;
for (;;)
@@ -244,44 +207,31 @@ HashMapGet(HashMap * map, const char *key)
return NULL;
}
-int
-HashMapIterate(HashMap * map, char **key, void **value)
+void
+HashMapIterate(HashMap *map, void (*iteratorFunc)(void *))
{
+ size_t i;
+
if (!map)
{
- return 0;
+ return;
}
- if (map->iterator >= map->capacity)
+ for (i = 0; i < map->capacity; i++)
{
- map->iterator = 0;
- *key = NULL;
- *value = NULL;
- return 0;
- }
-
- while (map->iterator < map->capacity)
- {
- HashMapBucket *bucket = map->entries[map->iterator];
-
- map->iterator++;
+ HashMapBucket *bucket = map->entries[i];
if (bucket)
{
- *key = bucket->key;
- *value = bucket->value;
- return 1;
+ iteratorFunc(bucket->value);
}
}
-
- map->iterator = 0;
- return 0;
}
void
-HashMapMaxLoadSet(HashMap * map, float load)
+HashMapMaxLoadSet(HashMap *map, float load)
{
- if (!map || (load > 1.0 || load <= 0))
+ if (!map)
{
return;
}
@@ -289,21 +239,11 @@ HashMapMaxLoadSet(HashMap * map, float load)
map->maxLoad = load;
}
-void
-HashMapFunctionSet(HashMap * map, unsigned long (*hashFunc) (const char *))
-{
- if (!map || !hashFunc)
- {
- return;
- }
-
- map->hashFunc = hashFunc;
-}
void *
-HashMapSet(HashMap * map, char *key, void *value)
+HashMapSet(HashMap *map, const char *key, void *value)
{
- unsigned long hash;
+ uint32_t hash;
size_t index;
if (!map || !key || !value)
@@ -316,7 +256,7 @@ HashMapSet(HashMap * map, char *key, void *value)
HashMapGrow(map);
}
- hash = map->hashFunc(key);
+ hash = HashMapHashKey(key);
index = hash % map->capacity;
for (;;)
@@ -325,14 +265,13 @@ HashMapSet(HashMap * map, char *key, void *value)
if (!bucket)
{
- bucket = Malloc(sizeof(HashMapBucket));
+ bucket = malloc(sizeof(HashMapBucket));
if (!bucket)
{
break;
}
bucket->hash = hash;
- bucket->key = key;
bucket->value = value;
map->entries[index] = bucket;
map->count++;
@@ -342,7 +281,6 @@ HashMapSet(HashMap * map, char *key, void *value)
if (!bucket->hash)
{
bucket->hash = hash;
- bucket->key = key;
bucket->value = value;
break;
}
@@ -350,7 +288,6 @@ HashMapSet(HashMap * map, char *key, void *value)
if (bucket->hash == hash)
{
void *oldValue = bucket->value;
-
bucket->value = value;
return oldValue;
}
@@ -361,16 +298,3 @@ HashMapSet(HashMap * map, char *key, void *value)
return NULL;
}
-void
-HashMapIterateFree(char *key, void *value)
-{
- if (key)
- {
- Free(key);
- }
-
- if (value)
- {
- Free(value);
- }
-}
diff --git a/src/Log.c b/src/Log.c
index 7b62d28..c33d29a 100644
--- a/src/Log.c
+++ b/src/Log.c
@@ -1,210 +1,25 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
#include
-#include
-
-#include
+#include
#include
#include
#include
#include
-#include
#define LOG_TSBUFFER 64
-struct LogConfig
-{
- int level;
+struct LogConfig {
+ LogLevel level;
size_t indent;
FILE *out;
int flags;
char *tsFmt;
-
- pthread_mutex_t lock;
};
-LogConfig *
-LogConfigCreate(void)
-{
- LogConfig *config;
-
- config = Malloc(sizeof(LogConfig));
-
- if (!config)
- {
- return NULL;
- }
-
- memset(config, 0, sizeof(LogConfig));
-
- LogConfigLevelSet(config, LOG_MESSAGE);
- LogConfigIndentSet(config, 0);
- LogConfigOutputSet(config, NULL); /* Will set to stdout */
- LogConfigFlagSet(config, LOG_FLAG_COLOR);
- LogConfigTimeStampFormatSet(config, "%y-%m-%d %H:%M:%S");
-
- return config;
-}
-
void
-LogConfigFlagClear(LogConfig * config, int flags)
+Log(LogConfig *config, LogLevel level, const char *msg, ...)
{
- if (!config)
- {
- return;
- }
-
- config->flags &= ~flags;
-}
-
-static int
-LogConfigFlagGet(LogConfig * config, int flags)
-{
- if (!config)
- {
- return 0;
- }
-
- return config->flags & flags;
-}
-
-void
-LogConfigFlagSet(LogConfig * config, int flags)
-{
- if (!config)
- {
- return;
- }
-
- config->flags |= flags;
-}
-
-void
-LogConfigFree(LogConfig * config)
-{
- if (!config)
- {
- return;
- }
-
- fclose(config->out);
- Free(config);
-}
-
-void
-LogConfigIndent(LogConfig * config)
-{
- if (config)
- {
- config->indent += 2;
- }
-}
-
-void
-LogConfigIndentSet(LogConfig * config, size_t indent)
-{
- if (!config)
- {
- return;
- }
-
- config->indent = indent;
-}
-
-int
-LogConfigLevelGet(LogConfig * config)
-{
- if (!config)
- {
- return -1;
- }
-
- return config->level;
-}
-
-void
-LogConfigLevelSet(LogConfig * config, int level)
-{
- if (!config)
- {
- return;
- }
-
- switch (level)
- {
- case LOG_ERROR:
- case LOG_WARNING:
- case LOG_MESSAGE:
- case LOG_DEBUG:
- config->level = level;
- default:
- break;
- }
-}
-
-void
-LogConfigOutputSet(LogConfig * config, FILE * out)
-{
- if (!config)
- {
- return;
- }
-
- if (out)
- {
- config->out = out;
- }
- else
- {
- config->out = stdout;
- }
-
-}
-
-void
-LogConfigTimeStampFormatSet(LogConfig * config, char *tsFmt)
-{
- if (config)
- {
- config->tsFmt = tsFmt;
- }
-}
-
-void
-LogConfigUnindent(LogConfig * config)
-{
- if (config && config->indent >= 2)
- {
- config->indent -= 2;
- }
-}
-
-void
-Log(LogConfig * config, int level, const char *msg,...)
-{
- size_t i;
+ int i;
int doColor;
char indicator;
va_list argp;
@@ -226,32 +41,20 @@ Log(LogConfig * config, int level, const char *msg,...)
return;
}
- pthread_mutex_lock(&config->lock);
-
- if (LogConfigFlagGet(config, LOG_FLAG_SYSLOG))
+ for (i = 0; i < config->indent; i++)
{
- /* No further print logic is needed; syslog will handle it all
- * for us. */
- va_start(argp, msg);
- vsyslog(level, msg, argp);
- va_end(argp);
- pthread_mutex_unlock(&config->lock);
- return;
+ fputc(' ', config->out);
}
doColor = LogConfigFlagGet(config, LOG_FLAG_COLOR)
- && isatty(fileno(config->out));
+ && isatty(fileno(config->out));
if (doColor)
{
char *ansi;
-
switch (level)
{
- case LOG_EMERG:
- case LOG_ALERT:
- case LOG_CRIT:
- case LOG_ERR:
+ case LOG_ERROR:
/* Bold Red */
ansi = "\033[1;31m";
break;
@@ -259,11 +62,11 @@ Log(LogConfig * config, int level, const char *msg,...)
/* Bold Yellow */
ansi = "\033[1;33m";
break;
- case LOG_NOTICE:
+ case LOG_TASK:
/* Bold Magenta */
ansi = "\033[1;35m";
break;
- case LOG_INFO:
+ case LOG_MESSAGE:
/* Bold Green */
ansi = "\033[1;32m";
break;
@@ -293,7 +96,7 @@ Log(LogConfig * config, int level, const char *msg,...)
if (tsLength)
{
fputs(tsBuffer, config->out);
- if (!isspace((unsigned char) tsBuffer[tsLength - 1]))
+ if (!isspace(tsBuffer[tsLength - 1]))
{
fputc(' ', config->out);
}
@@ -302,15 +105,6 @@ Log(LogConfig * config, int level, const char *msg,...)
switch (level)
{
- case LOG_EMERG:
- indicator = '#';
- break;
- case LOG_ALERT:
- indicator = '@';
- break;
- case LOG_CRIT:
- indicator = 'X';
- break;
case LOG_ERROR:
indicator = 'x';
break;
@@ -327,7 +121,7 @@ Log(LogConfig * config, int level, const char *msg,...)
indicator = '*';
break;
default:
- indicator = '?';
+ indicator = ' ';
break;
}
@@ -340,23 +134,177 @@ Log(LogConfig * config, int level, const char *msg,...)
}
fputc(' ', config->out);
- for (i = 0; i < config->indent; i++)
- {
- fputc(' ', config->out);
- }
va_start(argp, msg);
vfprintf(config->out, msg, argp);
fputc('\n', config->out);
va_end(argp);
- /* If we are debugging, there might be something that's going to
- * segfault the program coming up, so flush the output stream
- * immediately. */
+ /* If we are debugging, there might be something that's
+ * going to segfault the program coming up, so flush the
+ * output stream immediately.
+ */
if (config->level == LOG_DEBUG)
{
fflush(config->out);
}
-
- pthread_mutex_unlock(&config->lock);
+}
+
+LogConfig *
+LogConfigCreate(void)
+{
+ LogConfig *config;
+
+ config = calloc(1, sizeof(LogConfig));
+
+ if (!config)
+ {
+ return NULL;
+ }
+
+ LogConfigLevelSet(config, LOG_MESSAGE);
+ LogConfigIndentSet(config, 0);
+ LogConfigOutputSet(config, NULL); /* Will set to stdout */
+ LogConfigFlagSet(config, LOG_FLAG_COLOR);
+ LogConfigTimeStampFormatSet(config, "%y-%m-%d %H:%M:%S");
+
+ return config;
+}
+
+void
+LogConfigFlagClear(LogConfig *config, int flags)
+{
+ if (!config)
+ {
+ return;
+ }
+
+ config->flags &= ~flags;
+}
+
+int
+LogConfigFlagGet(LogConfig *config, int flags)
+{
+ if (!config)
+ {
+ return 0;
+ }
+
+ return config->flags & flags;
+}
+
+void
+LogConfigFlagSet(LogConfig *config, int flags)
+{
+ if (!config)
+ {
+ return;
+ }
+
+ config->flags |= flags;
+}
+
+void
+LogConfigFree(LogConfig *config)
+{
+ free(config);
+}
+
+void
+LogConfigIndent(LogConfig *config)
+{
+ if (config)
+ {
+ config->indent += 2;
+ }
+}
+
+size_t
+LogConfigIndentGet(LogConfig *config)
+{
+ if (!config)
+ {
+ return 0;
+ }
+
+ return config->indent;
+}
+
+void
+LogConfigIndentSet(LogConfig *config, size_t indent)
+{
+ if (!config)
+ {
+ return;
+ }
+
+ config->indent = indent;
+}
+
+LogLevel
+LogConfigLevelGet(LogConfig *config)
+{
+ if (!config)
+ {
+ return -1;
+ }
+
+ return config->level;
+}
+
+void
+LogConfigLevelSet(LogConfig *config, LogLevel level)
+{
+ if (!config)
+ {
+ return;
+ }
+
+ switch (level)
+ {
+ case LOG_ERROR:
+ case LOG_WARNING:
+ case LOG_MESSAGE:
+ case LOG_DEBUG:
+ config->level = level;
+ default:
+ break;
+ }
+}
+
+void
+LogConfigOutputSet(LogConfig *config, FILE *out)
+{
+ if (!config)
+ {
+ return;
+ }
+
+ if (out)
+ {
+ config->out = out;
+ }
+ else
+ {
+ config->out = stdout;
+ }
+
+}
+
+void
+LogConfigTimeStampFormatSet(LogConfig *config, char *tsFmt)
+{
+ if (config)
+ {
+ config->tsFmt = tsFmt;
+ }
+}
+
+void
+LogConfigUnindent(LogConfig *config)
+{
+ if (config && config->indent >= 2)
+ {
+ config->indent -= 2;
+ }
}
diff --git a/src/Telodendria.c b/src/Telodendria.c
index d0ff212..f5290bb 100644
--- a/src/Telodendria.c
+++ b/src/Telodendria.c
@@ -1,108 +1,21 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
#include
#include
#include
#include
-#include
-#include
-#include
-#include
-
-#include
-#include
#include
#include
#include
-#include
-#include
-#include
-
-static void
-TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
-{
- LogConfig *lc = (LogConfig *) args;
- char *action;
-
- switch (a)
- {
- case MEMORY_ALLOCATE:
- action = "Allocated";
- break;
- case MEMORY_REALLOCATE:
- action = "Re-allocated";
- break;
- case MEMORY_FREE:
- action = "Freed";
- break;
- case MEMORY_BAD_POINTER:
- action = "Bad pointer to";
- break;
- default:
- action = "Unknown operation on";
- break;
- }
-
- Log(lc, LOG_DEBUG, "%s:%d: %s %lu bytes of memory at %p.",
- MemoryInfoGetFile(i), MemoryInfoGetLine(i),
- action, MemoryInfoGetSize(i),
- MemoryInfoGetPointer(i));
-}
-
-static void
-TelodendriaMemoryIterator(MemoryInfo * i, void *args)
-{
- LogConfig *lc = (LogConfig *) args;
-
- /* We haven't freed the logger memory yet */
- if (MemoryInfoGetPointer(i) != lc)
- {
- Log(lc, LOG_WARNING, "%s:%d: %lu bytes of memory at %p leaked.",
- MemoryInfoGetFile(i), MemoryInfoGetLine(i),
- MemoryInfoGetSize(i), MemoryInfoGetPointer(i));
- }
-}
-
-static HttpServer *httpServer = NULL;
-
-static void
-TelodendriaSignalHandler(int signalNo)
-{
- (void) signalNo;
- HttpServerStop(httpServer);
-}
+#include
typedef enum ArgFlag
{
ARG_VERSION = (1 << 0),
- ARG_CONFIGTEST = (1 << 1),
- ARG_VERBOSE = (1 << 2)
+ ARG_USAGE = (1 << 1)
} ArgFlag;
static void
-TelodendriaPrintHeader(LogConfig * lc)
+TelodendriaPrintHeader(LogConfig *lc)
{
Log(lc, LOG_MESSAGE,
" _____ _ _ _ _");
@@ -113,18 +26,26 @@ TelodendriaPrintHeader(LogConfig * lc)
Log(lc, LOG_MESSAGE,
" | | __/ | (_) | (_| | __/ | | | (_| | | | | (_| |");
Log(lc, LOG_MESSAGE,
- " |_|\\___|_|\\___/ \\__,_|\\___|_| |_|\\__,_|_| |_|\\__,_|");
+ " |_|\\___|_|\\___/ \\__,_|\\___|_| |_|\\__,_|_| |_|\\__,_|");
Log(lc, LOG_MESSAGE, "Telodendria v" TELODENDRIA_VERSION);
Log(lc, LOG_MESSAGE, "");
Log(lc, LOG_MESSAGE,
"Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>");
Log(lc, LOG_MESSAGE,
- "Documentation/Support: https://telodendria.io");
+ "Documentation/Support: https://bancino.net/pub/Telodendria");
Log(lc, LOG_MESSAGE, "");
}
-int
-main(int argc, char **argv)
+static void
+TelodendriaPrintUsage(LogConfig *lc)
+{
+ Log(lc, LOG_MESSAGE, "Usage:");
+ Log(lc, LOG_MESSAGE, " -c Configuration file ('-' for stdin).");
+ Log(lc, LOG_MESSAGE, " -V Print the header, then exit.");
+ Log(lc, LOG_MESSAGE, " -h Print this usage, then exit.");
+}
+
+int main(int argc, char **argv)
{
LogConfig *lc;
int exit = EXIT_SUCCESS;
@@ -132,69 +53,38 @@ main(int argc, char **argv)
/* Arg parsing */
int opt;
int flags = 0;
- char *configArg = "/etc/telodendria.conf";
+ char *configArg = NULL;
/* Config file */
FILE *configFile = NULL;
ConfigParseResult *configParseResult = NULL;
HashMap *config = NULL;
- /* Program configuration */
- TelodendriaConfig *tConfig = NULL;
-
- /* User validation */
- struct passwd *userInfo;
- struct group *groupInfo;
-
- /* Signal handling */
- struct sigaction sigAction;
-
- MatrixHttpHandlerArgs matrixArgs;
-
- memset(&matrixArgs, 0, sizeof(matrixArgs));
-
lc = LogConfigCreate();
+ /* TODO: Remove */
+ LogConfigLevelSet(lc, LOG_DEBUG);
+
if (!lc)
{
printf("Fatal error: unable to allocate memory for logger.\n");
return EXIT_FAILURE;
}
- MemoryHook(TelodendriaMemoryHook, lc);
-
TelodendriaPrintHeader(lc);
-#ifdef __OpenBSD__
- Log(lc, LOG_DEBUG, "Attempting pledge...");
-
- if (pledge("stdio rpath wpath cpath inet dns getpw id unveil", NULL) != 0)
- {
- Log(lc, LOG_ERROR, "Pledge failed: %s", strerror(errno));
- exit = EXIT_FAILURE;
- goto finish;
- }
-#endif
-
- while ((opt = getopt(argc, argv, "f:Vvn")) != -1)
+ while ((opt = getopt(argc, argv, "c:Vh")) != -1)
{
switch (opt)
{
- case 'f':
+ case 'c':
configArg = optarg;
break;
case 'V':
flags |= ARG_VERSION;
break;
- case 'v':
- flags |= ARG_VERBOSE;
- break;
- case 'n':
- flags |= ARG_CONFIGTEST;
- break;
- case '?':
- exit = EXIT_FAILURE;
- goto finish;
+ case 'h':
+ flags |= ARG_USAGE;
default:
break;
}
@@ -205,21 +95,26 @@ main(int argc, char **argv)
goto finish;
}
+ if (flags & ARG_USAGE)
+ {
+ TelodendriaPrintUsage(lc);
+ goto finish;
+ }
+
+ if (!configArg)
+ {
+ Log(lc, LOG_ERROR, "No configuration file specified.");
+ TelodendriaPrintUsage(lc);
+ exit = EXIT_FAILURE;
+ goto finish;
+ }
+
if (strcmp(configArg, "-") == 0)
{
- configFile = stdin;
+ configFile = stdout;
}
else
{
- fclose(stdin);
-#ifdef __OpenBSD__
- if (unveil(configArg, "r") != 0)
- {
- Log(lc, LOG_ERROR, "Unable to unveil() configuration file '%s' for reading.", configArg);
- exit = EXIT_FAILURE;
- goto finish;
- }
-#endif
configFile = fopen(configArg, "r");
if (!configFile)
{
@@ -229,9 +124,11 @@ main(int argc, char **argv)
}
}
- Log(lc, LOG_TASK, "Processing configuration file '%s'.", configArg);
+ Log(lc, LOG_MESSAGE, "Using configuration file '%s'.", configArg);
+ /* Read config here */
configParseResult = ConfigParse(configFile);
+
if (!ConfigParseResultOk(configParseResult))
{
Log(lc, LOG_ERROR, "Syntax error on line %d.",
@@ -243,257 +140,18 @@ main(int argc, char **argv)
config = ConfigParseResultGet(configParseResult);
ConfigParseResultFree(configParseResult);
+ Log(lc, LOG_DEBUG, "Closing configuration file.");
fclose(configFile);
- tConfig = TelodendriaConfigParse(config, lc);
- if (!tConfig)
- {
- exit = EXIT_FAILURE;
- goto finish;
- }
-
- ConfigFree(config);
-
- if (flags & ARG_CONFIGTEST)
- {
- Log(lc, LOG_MESSAGE, "Configuration is OK.");
- goto finish;
- }
-
-#ifdef __OpenBSD__
- if (unveil(tConfig->dataDir, "rwc") != 0)
- {
- Log(lc, LOG_ERROR, "Unveil of data directory failed: %s", strerror(errno));
- exit = EXIT_FAILURE;
- goto finish;
- }
-
- unveil(NULL, NULL); /* Done with unveil(), so disable it */
-#endif
-
- LogConfigTimeStampFormatSet(lc, tConfig->logTimestamp);
-
- if (tConfig->flags & TELODENDRIA_LOG_COLOR)
- {
- LogConfigFlagSet(lc, LOG_FLAG_COLOR);
- }
- else
- {
- LogConfigFlagClear(lc, LOG_FLAG_COLOR);
- }
-
- LogConfigLevelSet(lc, flags & ARG_VERBOSE ? LOG_DEBUG : tConfig->logLevel);
-
- if (chdir(tConfig->dataDir) != 0)
- {
- Log(lc, LOG_ERROR, "Unable to change into data directory: %s.", strerror(errno));
- exit = EXIT_FAILURE;
- goto finish;
- }
- else
- {
- Log(lc, LOG_DEBUG, "Changed working directory to: %s", tConfig->dataDir);
- }
-
-
- if (tConfig->flags & TELODENDRIA_LOG_FILE)
- {
- FILE *logFile = fopen("telodendria.log", "a");
-
- if (!logFile)
- {
- Log(lc, LOG_ERROR, "Unable to open log file for appending.");
- exit = EXIT_FAILURE;
- goto finish;
- }
-
- Log(lc, LOG_MESSAGE, "Logging to the log file. Check there for all future messages.");
- LogConfigOutputSet(lc, logFile);
- }
- else if (tConfig->flags & TELODENDRIA_LOG_STDOUT)
- {
- Log(lc, LOG_DEBUG, "Already logging to standard output.");
- }
- else if (tConfig->flags & TELODENDRIA_LOG_SYSLOG)
- {
- Log(lc, LOG_MESSAGE, "Logging to the syslog. Check there for all future messages.");
- LogConfigFlagSet(lc, LOG_FLAG_SYSLOG);
-
- openlog("telodendria", LOG_PID | LOG_NDELAY, LOG_DAEMON);
- /* Always log everything, because the Log API will control what
- * messages get passed to the syslog */
- setlogmask(LOG_UPTO(LOG_DEBUG));
- }
- else
- {
- Log(lc, LOG_ERROR, "Unknown logging method in flags: '%d'", tConfig->flags);
- Log(lc, LOG_ERROR, "This is a programmer error; please report it.");
- exit = EXIT_FAILURE;
- goto finish;
- }
-
- Log(lc, LOG_DEBUG, "Configuration:");
- LogConfigIndent(lc);
- Log(lc, LOG_DEBUG, "Listen On: %d", tConfig->listenPort);
- Log(lc, LOG_DEBUG, "Server Name: %s", tConfig->serverName);
- Log(lc, LOG_DEBUG, "Base URL: %s", tConfig->baseUrl);
- Log(lc, LOG_DEBUG, "Identity Server: %s", tConfig->identityServer);
- Log(lc, LOG_DEBUG, "Run As: %s:%s", tConfig->uid, tConfig->gid);
- Log(lc, LOG_DEBUG, "Data Directory: %s", tConfig->dataDir);
- Log(lc, LOG_DEBUG, "Threads: %d", tConfig->threads);
- Log(lc, LOG_DEBUG, "Max Connections: %d", tConfig->maxConnections);
- Log(lc, LOG_DEBUG, "Max Cache: %ld", tConfig->maxCache);
- Log(lc, LOG_DEBUG, "Flags: %x", tConfig->flags);
- LogConfigUnindent(lc);
-
- Log(lc, LOG_DEBUG, "Running as uid:gid: %d:%d.", getuid(), getgid());
-
- userInfo = getpwnam(tConfig->uid);
- groupInfo = getgrnam(tConfig->gid);
-
- if (!userInfo || !groupInfo)
- {
- Log(lc, LOG_ERROR, "Unable to locate the user/group specified in the configuration.");
- exit = EXIT_FAILURE;
- goto finish;
- }
- else
- {
- Log(lc, LOG_DEBUG, "Found user/group information using getpwnam() and getgrnam().");
- }
-
- /* Arguments to pass into the HTTP handler */
- matrixArgs.lc = lc;
- matrixArgs.config = tConfig;
-
- /* Bind the socket before possibly dropping permissions */
- httpServer = HttpServerCreate(tConfig->listenPort, tConfig->threads, tConfig->maxConnections,
- MatrixHttpHandler, &matrixArgs);
- if (!httpServer)
- {
- Log(lc, LOG_ERROR, "Unable to create HTTP server on port %d: %s",
- tConfig->listenPort, strerror(errno));
- exit = EXIT_FAILURE;
- goto finish;
- }
-
- if (getuid() == 0)
- {
-#ifndef __OpenBSD__
- if (chroot(".") == 0)
- {
- Log(lc, LOG_DEBUG, "Changed the root directory to: %s.", tConfig->dataDir);
- }
- else
- {
- Log(lc, LOG_WARNING, "Unable to chroot into directory: %s.", tConfig->dataDir);
- }
-#else
- Log(lc, LOG_DEBUG, "Not attempting chroot() after pledge() and unveil().");
-#endif
-
- if (setgid(groupInfo->gr_gid) != 0 || setuid(userInfo->pw_uid) != 0)
- {
- Log(lc, LOG_WARNING, "Unable to set process uid/gid.");
- }
- else
- {
- Log(lc, LOG_DEBUG, "Set uid/gid to %s:%s.", tConfig->uid, tConfig->gid);
- }
- }
- else
- {
- Log(lc, LOG_DEBUG, "Not changing root directory, because we are not root.");
-
- if (getuid() != userInfo->pw_uid || getgid() != groupInfo->gr_gid)
- {
- Log(lc, LOG_WARNING, "Not running as the uid/gid specified in the configuration.");
- }
- else
- {
- Log(lc, LOG_DEBUG, "Running as the uid/gid specified in the configuration.");
- }
- }
-
- /* These config values are no longer needed; don't hold them in
- * memory anymore */
- Free(tConfig->dataDir);
- Free(tConfig->uid);
- Free(tConfig->gid);
-
- tConfig->dataDir = NULL;
- tConfig->uid = NULL;
- tConfig->gid = NULL;
-
- matrixArgs.db = DbOpen(".", tConfig->maxCache);
-
- if (!tConfig->maxCache)
- {
- Log(lc, LOG_WARNING, "Max-cache is set to zero; caching is disabled.");
- }
-
- if (!matrixArgs.db)
- {
- Log(lc, LOG_ERROR, "Unable to open data directory as a database.");
- exit = EXIT_FAILURE;
- goto finish;
- }
-
- Log(lc, LOG_TASK, "Starting server...");
-
- if (!HttpServerStart(httpServer))
- {
- Log(lc, LOG_ERROR, "Unable to start HTTP server.");
- exit = EXIT_FAILURE;
- goto finish;
- }
-
- Log(lc, LOG_MESSAGE, "Listening on port: %d", tConfig->listenPort);
-
- sigAction.sa_handler = TelodendriaSignalHandler;
- sigfillset(&sigAction.sa_mask);
- sigAction.sa_flags = SA_RESTART;
-
- if (sigaction(SIGINT, &sigAction, NULL) < 0)
- {
- Log(lc, LOG_ERROR, "Unable to install signal handler.");
- exit = EXIT_FAILURE;
- goto finish;
- }
-
- /* Block this thread until the server is terminated by a signal
- * handler */
- HttpServerJoin(httpServer);
+ /* Configure log file */
finish:
- Log(lc, LOG_TASK, "Shutting down...");
- if (httpServer)
+ if (config)
{
- HttpServerFree(httpServer);
- Log(lc, LOG_DEBUG, "Freed HTTP Server.");
+ Log(lc, LOG_DEBUG, "Freeing configuration structure.");
+ ConfigFree(config);
}
-
- /*
- * If we're not logging to standard output, then we can close it. Otherwise,
- * if we are logging to stdout, LogConfigFree() will close it for us.
- */
- if (!tConfig || !(tConfig->flags & TELODENDRIA_LOG_STDOUT))
- {
- fclose(stdout);
- }
-
- TelodendriaConfigFree(tConfig);
- DbClose(matrixArgs.db);
-
- Log(lc, LOG_DEBUG, "");
- MemoryIterate(TelodendriaMemoryIterator, lc);
- Log(lc, LOG_DEBUG, "");
-
- Log(lc, LOG_DEBUG, "Exiting with code '%d'.", exit);
+ Log(lc, LOG_DEBUG, "Freeing log configuration and exiting with code '%d'.", exit);
LogConfigFree(lc);
-
- MemoryFreeAll();
-
- fclose(stderr);
return exit;
}
diff --git a/src/include/Array.h b/src/include/Array.h
index d699574..f377a18 100644
--- a/src/include/Array.h
+++ b/src/include/Array.h
@@ -1,27 +1,3 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
#ifndef TELODENDRIA_ARRAY_H
#define TELODENDRIA_ARRAY_H
@@ -30,30 +6,27 @@
typedef struct Array Array;
extern Array *
- ArrayCreate(void);
+ArrayCreate(void);
extern size_t
- ArraySize(Array *);
+ArraySize(Array *array);
extern void *
- ArrayGet(Array *, size_t);
+ArrayGet(Array *array, size_t index);
extern int
- ArrayInsert(Array *, void *, size_t);
+ArrayInsert(Array *, void *value, size_t index);
extern int
- ArrayAdd(Array *, void *);
+ArrayAdd(Array *array, void *value);
extern void *
- ArrayDelete(Array *, size_t);
+ArrayDelete(Array *array, size_t index);
extern void
- ArraySort(Array *, int (*) (void *, void *));
-
-extern void
- ArrayFree(Array *);
+ArrayFree(Array *array);
extern int
- ArrayTrim(Array *);
+ArrayTrim(Array *array);
-#endif /* TELODENDRIA_ARRAY_H */
+#endif
diff --git a/src/include/Base64.h b/src/include/Base64.h
index 428506e..d45d014 100644
--- a/src/include/Base64.h
+++ b/src/include/Base64.h
@@ -1,48 +1,25 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
#ifndef TELODENDRIA_BASE64_H
#define TELODENDRIA_BASE64_H
#include
extern size_t
- Base64EncodedSize(size_t);
+Base64EncodedSize(size_t inputSize);
extern size_t
- Base64DecodedSize(const char *, size_t);
+Base64DecodedSize(const char *base64, size_t len);
extern char *
- Base64Encode(const char *, size_t);
+Base64Encode(const char *input, size_t len);
extern char *
- Base64Decode(const char *, size_t);
+Base64Decode(const char *input, size_t len);
extern void
- Base64Unpad(char *, size_t);
+Base64Unpad(char *base64, size_t length);
extern int
- Base64Pad(char **, size_t);
+Base64Pad(char **base64Ptr, size_t length);
+
+#endif
-#endif /* TELODENDRIA_BASE64_H */
diff --git a/src/include/Config.h b/src/include/Config.h
index 314f439..0b445b1 100644
--- a/src/include/Config.h
+++ b/src/include/Config.h
@@ -1,42 +1,3 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-/*
- * Config.h: A heavily-modified version of Conifer2, a configuration
- * file format specification and C parsing library written by Jordan
- * Bancino. This library differs from Conifer2 in that the function
- * naming convention has been updated to be consistent with Telodendria,
- * and the underlying data structures have been overhauled to use the
- * data structure libraries provided by Telodendria.
- *
- * Conifer2 was originally a learning project. It was very thoroughly
- * debugged, however, and the configuration syntax was elegant,
- * certainly more elegant than using JSON for a configuration file,
- * so it was chosen to be the format for Telodendria's configuration
- * file. The original Conifer2 project is now dead; Conifer2 lives on
- * only as Telodendria's Config parsing library.
- */
#ifndef TELODENDRIA_CONFIG_H
#define TELODENDRIA_CONFIG_H
@@ -45,164 +6,32 @@
#include
#include
-/*
- * A configuration directive is a single key that may have at least one
- * value, and any number of children.
- */
typedef struct ConfigDirective ConfigDirective;
-/*
- * The parser returns a parse result object. This stores whether or
- * not the parse was successful, and then also additional information
- * about the parse, such as the line number on which parsing failed,
- * or the collection of directives if the parsing succeeded.
- *
- * There are a number of ConfigParseResult methods that can be used
- * to query the result of parsing.
- */
typedef struct ConfigParseResult ConfigParseResult;
-/*
- * Parse a configuration file, and generate the structures needed to
- * make it easy to read.
- *
- * Params:
- *
- * (FILE *) The input stream to read from.
- *
- * Return: A ConfigParseResult, which can be used to check whether or
- * not the parsing was successful. If the parsing was sucessful, then
- * this object contains the root directive, which can be used to
- * retrieve configuration values out of. If the parsing failed, then
- * this object contains the line number at which the parsing was
- * aborted.
- */
extern ConfigParseResult *
- ConfigParse(FILE *);
+ConfigParse(FILE *stream);
-/*
- * Get whether or not a parse result indicates that parsing was
- * successful or not. This function should be used to determine what
- * to do next. If the parsing failed, your program should terminate
- * with an error, otherwise, you can proceed to parse the configuration
- * file.
- *
- * Params:
- *
- * (ConfigParseResult *) The output of ConfigParse() to check.
- *
- * Return: 0 if the configuration file is malformed, or otherwise
- * could not be parsed. Any non-zero return value indicates that the
- * configuration file was successfully parsed.
- */
extern unsigned int
- ConfigParseResultOk(ConfigParseResult *);
+ConfigParseResultOk(ConfigParseResult *result);
-/*
- * If, and only if, the configuration file parsing failed, then this
- * function can be used to get the line number it failed at. Typically,
- * this will be reported to the user and then the program will be
- * terminated.
- *
- * Params:
- *
- * (ConfigParseResult *) The output of ConfigParse() to get the
- * line number from.
- *
- * Return: The line number on which the configuration file parser
- * choked, or 0 if the parsing was actually successful.
- */
extern size_t
- ConfigParseResultLineNumber(ConfigParseResult *);
+ConfigParseResultLineNumber(ConfigParseResult *result);
-/*
- * Convert a ConfigParseResult into a HashMap containing the entire
- * configuration file, if, and only if, the parsing was successful.
- *
- * Params:
- *
- * (ConfigParseResult *) The output of ConfigParse() to get the
- * actual configuration data from.
- *
- * Return: A HashMap containing all the configuration data, or NULL
- * if the parsing was not successful. This HashMap is a map of string
- * keys to ConfigDirective objects. Use the standard HashMap methods
- * to get ConfigDirectives, and then use the ConfigDirective functions
- * to get information out of them.
- */
extern HashMap *
- ConfigParseResultGet(ConfigParseResult *);
+ConfigParseResultGet(ConfigParseResult *result);
-/*
- * Free the memory being used by the given ConfigParseResult. Note that
- * it is safe to free the ConfigParseResult immediately after you have
- * retrieved either the line number or the configuration data from it.
- * Freeing the parse result does not free the configuration data.
- *
- * Params:
- *
- * (ConfigParseResult *) The output of ConfigParse() to free. This
- * object will be invalidated, but pointers to
- * the actual configuration data will still be
- * valid.
- */
extern void
- ConfigParseResultFree(ConfigParseResult *);
+ConfigParseResultFree(ConfigParseResult *result);
-/*
- * Get an array of values associated with the given configuration
- * directive. Directives can have any number of values, which are
- * made accessible via the Array API.
- *
- * Params:
- *
- * (ConfigDirective *) The configuration directive to get the values
- * for.
- *
- * Return: An array that contains at least 1 value. Configuration files
- * cannot have value-less directives. If the passed directive is NULL,
- * or there is an error allocating memory for an array, then NULL is
- * returned.
- */
extern Array *
- ConfigValuesGet(ConfigDirective *);
+ConfigValuesGet(ConfigDirective *directive);
-/*
- * Get a map of children associated with the given configuration
- * directive. Configuration files can recurse with no practical limit,
- * so directives can have any number of child directives.
- *
- * Params:
- *
- * (ConfigDirective *) The configuratio ndirective to get the
- * children of.
- *
- * Return: A HashMap containing child directives, or NULL if the passed
- * directive is NULL or has no children.
- */
extern HashMap *
- ConfigChildrenGet(ConfigDirective *);
+ConfigChildrenGet(ConfigDirective *directive);
-/*
- * Free all the memory associated with the given configuration hash
- * map. Note: this will free *everything*. All Arrays, HashMaps,
- * ConfigDirectives, and even strings will be invalidated. As such,
- * this should be done after you either copy the values you want, or
- * are done using them. It is highly recommended to use this function
- * near the end of your program's execution during cleanup, otherwise
- * copy any values you need into your own buffers.
- *
- * Note that this should only be run on the root configuration object,
- * not any children. Running on children will produce undefined
- * behavior. This function is recursive; it will get all the children
- * under it.
- *
- * Params:
- *
- * (HashMap *) The configuration data to free.
- *
- */
extern void
- ConfigFree(HashMap *);
+ConfigFree(HashMap *conf);
-#endif /* TELODENDRIA_CONFIG_H */
+#endif /* TELODENDRIA_CONFIG_H */
diff --git a/src/include/HashMap.h b/src/include/HashMap.h
index c75c962..659f3a3 100644
--- a/src/include/HashMap.h
+++ b/src/include/HashMap.h
@@ -1,54 +1,85 @@
/*
+ * HashMap.h: The public interface for Telodendria's hash map
+ * implementation. This hash map is designed to be simple, well
+ * documented, and generally readable and understandable, yet also
+ * performant enough to be useful, because it is used extensively in
+ * Telodendria.
+ *
+ * Fundamentally, this is an entirely generic map implementation. It
+ * can be used for many general purposes, but it is designed to only
+ * implement the features that Telodendria needs to function well.
+ *
* Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-
#ifndef TELODENDRIA_HASHMAP_H
#define TELODENDRIA_HASHMAP_H
+#include
+
+/*
+ * HashMap: The primary data structure used by this hash map algorithm.
+ * All data necessary for the algorithm to function is stored inside
+ * it. This is an opaque structure; use the methods defined by this
+ * interface to manipulate it.
+ */
typedef struct HashMap HashMap;
+/*
+ * HashMapCreate: Create a new HashMap object.
+ *
+ * Returns: A HashMap object that is ready to be used by the rest of
+ * the HashMap functions, or NULL if memory could not be allocated on
+ * the heap.
+ */
extern HashMap *
- HashMapCreate(void);
+HashMapCreate(void);
extern void
- HashMapMaxLoadSet(HashMap *, float);
+HashMapMaxLoadSet(HashMap *map, float load);
+
+/*
+ * HashMapSet: Set the given key in the HashMap to the given value. Note
+ * that the value is not copied into the HashMap's own memory space;
+ * only the pointer is stored. It is the caller's job to ensure that the
+ * value's memory remains valid for the life of the HashMap.
+ *
+ * Returns: The previous value at the given key, or NULL if the key did
+ * not previously exist or any of the parameters provided are NULL. All
+ * keys must have values; you can't set a key to NULL. To delete a key,
+ * use HashMapDelete.
+ */
+extern void *
+HashMapSet(HashMap * map, const char *key, void *value);
+
+/*
+ * HashMapGet: Get the value for the given key.
+ *
+ * Returns: The value at the given key, or NULL if the key does not
+ * exist, no map was provided, or no key was provided.
+ */
+extern void *
+HashMapGet(HashMap * map, const char *key);
+
+/*
+ * HashMapDelete: Delete the value for the given key.
+ *
+ * Returns: The value at the given key, or NULL if the key does not
+ * exist or the map or key was not provided.
+ */
+extern void *
+HashMapDelete(HashMap *map, const char *key);
extern void
- HashMapFunctionSet(HashMap *, unsigned long (*) (const char *));
-
-extern void *
- HashMapSet(HashMap *, char *, void *);
-
-extern void *
- HashMapGet(HashMap *, const char *);
-
-extern void *
- HashMapDelete(HashMap *, const char *);
-
-extern int
- HashMapIterate(HashMap *, char **, void **);
+HashMapIterate(HashMap *map, void (*iteratorFunc)(void *));
+/*
+ * HashMapFree: Free the hash map, returning its memory to the operating
+ * system. Note that this function does not free the values stored in
+ * the map since this hash map implementation has no way of knowing
+ * what actually is stored in it. You should use HashMapIterate to
+ * free the values using your own algorithm.
+ */
extern void
- HashMapFree(HashMap *);
+HashMapFree(HashMap *map);
-#endif /* TELODENDRIA_HASHMAP_H */
+#endif /* TELODENDRIA_HASHMAP_H */
diff --git a/src/include/Http.h b/src/include/Http.h
index 18f8cce..c27f589 100644
--- a/src/include/Http.h
+++ b/src/include/Http.h
@@ -1,36 +1,7 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
#ifndef TELODENDRIA_HTTP_H
#define TELODENDRIA_HTTP_H
-#include
-
-#include
-
-typedef enum HttpRequestMethod
-{
- HTTP_METHOD_UNKNOWN,
+typedef enum HttpRequestMethod {
HTTP_GET,
HTTP_HEAD,
HTTP_POST,
@@ -42,13 +13,12 @@ typedef enum HttpRequestMethod
HTTP_PATCH
} HttpRequestMethod;
-typedef enum HttpStatus
-{
+typedef enum HttpStatus {
/* Informational responses */
HTTP_CONTINUE = 100,
HTTP_SWITCHING_PROTOCOLS = 101,
HTTP_EARLY_HINTS = 103,
-
+
/* Successful responses */
HTTP_OK = 200,
HTTP_CREATED = 201,
@@ -57,7 +27,7 @@ typedef enum HttpStatus
HTTP_NO_CONTENT = 204,
HTTP_RESET_CONTENT = 205,
HTTP_PARTIAL_CONTENT = 206,
-
+
/* Redirection messages */
HTTP_MULTIPLE_CHOICES = 300,
HTTP_MOVED_PERMANENTLY = 301,
@@ -66,7 +36,7 @@ typedef enum HttpStatus
HTTP_NOT_MODIFIED = 304,
HTTP_TEMPORARY_REDIRECT = 307,
HTTP_PERMANENT_REDIRECT = 308,
-
+
/* Client error messages */
HTTP_BAD_REQUEST = 400,
HTTP_UNAUTHORIZED = 401,
@@ -91,7 +61,7 @@ typedef enum HttpStatus
HTTP_TOO_MANY_REQUESTS = 429,
HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
-
+
/* Server error responses */
HTTP_INTERNAL_SERVER_ERROR = 500,
HTTP_NOT_IMPLEMENTED = 501,
@@ -104,25 +74,23 @@ typedef enum HttpStatus
HTTP_NETWORK_AUTH_REQUIRED = 511
} HttpStatus;
-extern const char *
- HttpStatusToString(const HttpStatus);
+struct HttpRequest {
+ HttpRequestMethod method;
+};
+
+struct HttpResponse {
+ HttpStatus status;
+};
+
+extern char *
+HttpGetStatusString(const HttpStatus httpStatus);
extern HttpRequestMethod
- HttpRequestMethodFromString(const char *);
+HttpRequestMethodFromString(const char *requestMethod);
-extern const char *
- HttpRequestMethodToString(const HttpRequestMethod);
+typedef struct HttpRequest HttpRequest;
+typedef struct HttpResponse HttpResponse;
-extern char *
- HttpUrlEncode(char *);
-
-extern char *
- HttpUrlDecode(char *);
-
-extern HashMap *
- HttpParamDecode(char *);
-
-extern char *
- HttpParamEncode(HashMap *);
+typedef void (*HttpHandler)(HttpRequest *, HttpResponse *);
#endif
diff --git a/src/include/Json.h b/src/include/Json.h
index 5e5005f..ead2453 100644
--- a/src/include/Json.h
+++ b/src/include/Json.h
@@ -1,271 +1,69 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-/*
- * Json.h: A fully-featured JSON API for C using Arrays and HashMaps.
- * This API builds on the foundations of Arrays and HashMaps, because
- * that's all a JSON object really is. It provides a JsonValue, which
- * is used to encapsulate arbitrary values while being able to identify
- * them in the future, so that JSON can be effectively handled.
- *
- * This implementation is just to get the job done in parsing and
- * generating JSON. It is extremely strict; it will fail on syntax
- * errors. This is fine for Matrix, because we can just return
- * M_BAD_JSON anything in here fails.
- *
- * One thing to note about this implementation is that it focuses
- * primarily on serialization and deserialization to and from streams.
- * What this means is that it does not provide facilities for handling
- * JSON strings; it only writes JSON to output streams, and reading
- * them from input streams. Of course, you could use the POSIX
- * fmemopen() and open_memstream() functions if you really want to deal
- * with JSON strings, but JSON is intended to be an exchange format.
- * Data should be converted to JSON when it is leaving, and converted
- * from JSON when it is coming in. Ideally, most of the program would
- * have no idea what JSON actually is.
- */
#ifndef TELODENDRIA_JSON_H
#define TELODENDRIA_JSON_H
#include
#include
-#include
-#include
-
-/*
- * All the possible JSON types. This enumeration is used to identify
- * the type of the value stored in a JsonValue.
- */
-typedef enum JsonType
-{
- JSON_NULL, /* Maps to nothing. */
- JSON_OBJECT, /* Maps to a HashMap of JsonValues */
- JSON_ARRAY, /* Maps to an Array of JsonValues */
- JSON_STRING, /* Maps to a C string */
- JSON_INTEGER, /* Maps to a C long */
- JSON_FLOAT, /* Maps to a C double */
- JSON_BOOLEAN /* Maps to a C 1 or 0 */
+typedef enum JsonType {
+ JSON_OBJECT,
+ JSON_ARRAY,
+ JSON_STRING,
+ JSON_INTEGER,
+ JSON_FLOAT,
+ JSON_BOOLEAN,
+ JSON_NULL
} JsonType;
-/*
- * A JsonValue encapsulates all the possible values that can be stored
- * in a JSON object as a single type, so as to provide a consistent
- * API for accessing and setting them. It is an opaque structure that
- * can be managed entirely by the functions defined in this API.
- *
- * Note that in the case of objects, arrays, and strings, this structure
- * only stores pointers to allocated data, it doesn't store the data
- * itself. JsonValues only store integers, floats, booleans, and NULL
- * in their memory. Anything else must be freed separately.
- */
-typedef struct JsonValue JsonValue;
+typedef struct JsonValue {
+ JsonType type;
+ union as {
+ HashMap *object;
+ Array *array;
+ char *string;
+ int64_t integer;
+ double floating;
+ int boolean : 1;
+ };
+} JsonValue;
+
-/*
- * Get the type of a JsonValue.
- *
- * Params:
- *
- * (JsonValue *) The value to get the type of.
- *
- * Return: A JsonType that tells what the provided value is, or
- * JSON_NULL if the passed value is NULL. Note that even a fully
- * valid JsonValue may still be of type JSON_NULL, so this function
- * should not be used to check whether or not the JSON value is valid.
- */
extern JsonType
- JsonValueType(JsonValue *);
+JsonValueType(JsonValue *value);
-/*
- * Wrap a HashMap into a JsonValue that represents a JSON object. Note
- * that the HashMap should contain only JsonValues. Any other contents
- * are not supported and will lead to undefined behavior.
- *
- * Params:
- *
- * (HashMap *) The hash map of JsonValues to wrap in a JsonValue.
- *
- * Return: A JsonValue that holds a pointer to the given object, or
- * NULL if there was an error allocating memory.
- */
extern JsonValue *
- JsonValueObject(HashMap *);
+JsonValueObject(HashMap *object);
-/*
- * Get a HashMap from a JsonValue that represents a JSON object.
- *
- * Params:
- *
- * (JsonValue *) The value to extract the object from.
- *
- * Return: A HashMap of JsonValues, or NULL if no value was provided,
- * or the value is not of type JSON_OBJECT.
- */
extern HashMap *
- JsonValueAsObject(JsonValue *);
-
-/*
- * The following methods very closely resemble the ones above, and
- * behave pretty much the exact same. To save on time and effort,
- * I'm choosing not to explicitly document all of these. If something
- * is unclear about how these functions work, consult the source code,
- * and then feel free to write the documentation yourself.
- *
- * Otherwise, reach out to the official Matrix room, and someone will
- * be able to help you.
- */
+JsonValueAsObject(JsonValue *value);
extern JsonValue *
- JsonValueArray(Array * array);
+JsonValueArray(Array *array);
extern Array *
- JsonValueAsArray(JsonValue * value);
+JsonValueAsArray(JsonValue *value);
extern JsonValue *
- JsonValueString(char *string);
+JsonValueString(char *string);
extern JsonValue *
- JsonValueInteger(long integer);
+JsonValueInteger(int64_t integer);
extern JsonValue *
- JsonValueFloat(double floating);
+JsonValueFloat(double floating);
extern JsonValue *
- JsonValueBoolean(int boolean);
+JsonValueBoolean(int boolean);
-/*
- * Create a JsonValue that represents a JSON null. Because Arrays and
- * HashMaps should not contain NULL values, I thought it appropriate
- * to provide support for JSON nulls. Yes, a small amount of memory is
- * allocated just to point to a NULL, but this keeps all the APIs
- * clean.
- *
- * Return: A JsonValue that represents a JSON null, or NULL if memory
- * could not be allocated.
- */
extern JsonValue *
- JsonValueNull(void);
+JsonValueNull(void);
-/*
- * Free the memory being used by a JSON value. Note that this will
- * recursively free all Arrays, HashMaps, and other JsonValues that
- * are reachable from this one. It will invoke free() on strings as
- * well, so make sure passed string pointers point to strings on the
- * heap, not the stack. This will be the case for all strings returned
- * by JsonDecode(), which is why this assumption is made. However, if
- * you are manually creating JsonObjects and stitching them together,
- * you'll have to manually free them as well. Calling this on a
- * JsonValue that contains a pointer to a stack string is undefined.
- *
- * Params:
- *
- * (JsonValue *) The JsonValue to recursively free.
- */
-extern void
- JsonValueFree(JsonValue *);
+extern void *
+JsonValueFree(JsonValue *value);
-/*
- * Recursively free a HashMap of JsonValues. This iterates over all
- * the JsonValues in a HashMap and frees them using JsonValueFree(),
- * which will in turn call JsonFree() on values of type JSON_OBJECT.
- *
- * Params:
- *
- * (HashMap *) The hash map of JsonValues to recursively free.
- */
-extern void
- JsonFree(HashMap *);
+extern char *
+JsonEncode(HashMap *object);
-/*
- * Encode the given string in such a way that it can be embedded in a
- * JSON stream. This entails:
- *
- * - Escaping quotes, backslashes, and other special characters using
- * their backslash escape
- * - Encoding bytes that are not UTF-8 using \u escapes.
- * - Wrapping the entire string in double quotes.
- *
- * This function is provided via the public API so it is accessible to
- * custom JSON encoders, such as the CanonicalJson API. This will
- * typically be used for encoding JSON keys; for values, just use
- * JsonEncodeValue().
- *
- * Params:
- *
- * (const char *) The C string to serialize as a JSON string.
- * (FILE *) The output stream to write the encoded string to.
- */
-extern void
- JsonEncodeString(const char *, FILE *);
-
-/*
- * Serialize a JsonValue as it would appear in JSON output. This is
- * a recursive function that will also encode all child values
- * reachable from the given JsonValue.
- *
- * This is exposed via the public API so that custom JSON encoders
- * such as CanonicalJson can take advantage of it. Normal users that
- * are writing custom encoders should just use JsonEncode() to encode
- * an entire object.
- *
- * Params:
- *
- * (JsonValue *) The value to encode.
- * (FILE *) The output stream to write the given value to.
- */
-extern void
- JsonEncodeValue(JsonValue * value, FILE * out);
-
-/*
- * Encode a HashMap of JsonValues into a fully-valid, minimized JSON
- * object. This function is recursive; it will serialize everything
- * accessible from the passed object into JSON.
- *
- * Params:
- *
- * (HashMap *) The HashMap of JsonValues to encode and write to the
- * output stream.
- * (FILE *) The output stream to write the given HashMap to.
- *
- * Return: Whether or not the operation was successful. This function
- * will fail if either the passed HashMap or file stream are NULL. In
- * all other cases, this function succeeds.
- */
-extern int
- JsonEncode(HashMap *, FILE *);
-
-/*
- * Decode the given input stream into a HashMap of JsonValues.
- *
- * Params:
- *
- * (FILE *) The input stream to parse JSON from.
- *
- * Return: A HashMap of JsonValues, or NULL if there was an error
- * parsing the JSON.
- */
extern HashMap *
- JsonDecode(FILE *);
+JsonDecode(char *string);
-#endif /* TELODENDRIA_JSON_H */
+#endif
diff --git a/src/include/Log.h b/src/include/Log.h
index 46464a0..3011692 100644
--- a/src/include/Log.h
+++ b/src/include/Log.h
@@ -1,197 +1,63 @@
-/*
- * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-/*
- * Log.h: A heavily-modified version of Shlog, a simple C logging
- * facility that allows for colorful output, timestamps, and custom
- * log levels. This library differs from Shlog in that the naming
- * conventions have been updated to be consistent with Telodendria.
- *
- * Shlog was originally a learning project. It worked well, however,
- * and produced elegant logging output, so it was chosen to be the
- * main logging mechanism of Telodendria. The original Shlog project
- * is now dead; Shlog lives on now only as Telodendria's logging
- * mechanism.
- *
- * In the name of simplicity and portability, I opted to use an
- * in-house logging system instead of syslog(), or other system logging
- * mechanisms. However, this API could easily be patched to allow
- * logging via other mechanisms that support the same features.
- */
#ifndef TELODENDRIA_LOG_H
#define TELODENDRIA_LOG_H
#include
#include
-#include
-/*
- * I used to define all my own constants, but now I use
- * those defined in syslog.h. Instead of replacing all the
- * references, I just map the old names to the new ones. If
- * you're ever bored one day, you can remove these, and then
- * go fix all the compiler errors that arise. Should be pretty
- * easy, just mind numbing.
- */
-#define LOG_ERROR LOG_ERR
-#define LOG_TASK LOG_NOTICE
-#define LOG_MESSAGE LOG_INFO
+typedef enum LogLevel {
+ LOG_ERROR,
+ LOG_WARNING,
+ LOG_TASK,
+ LOG_MESSAGE,
+ LOG_DEBUG
+} LogLevel;
-/*
- * The possible flags that can be applied to alter the behavior of
- * the logger.
- */
-typedef enum LogFlag
-{
- LOG_FLAG_COLOR = (1 << 0), /* Enable color output on TTYs */
- LOG_FLAG_SYSLOG = (1 << 1) /* Log to the syslog instead of a
- * file */
+typedef enum LogFlag {
+ LOG_FLAG_COLOR = (1 << 0)
} LogFlag;
-/*
- * The log configurations structure in which all settings exist.
- * It's not super elegant to pass around a pointer to the logging
- * configuration, but this really is the best way without having a
- * global variable. It allows multiple loggers to exist if necessary,
- * and makes things more thread safe.
- */
typedef struct LogConfig LogConfig;
-/*
- * Create a new log configuration on the heap. This will be passed to
- * every Log() call after it is configured.
- *
- * Return: A pointer to a new LogConfig that can be configured and used
- * for logging. It should have sane defaults; in other words, you should
- * be able to immediately start logging with it.
- */
extern LogConfig *
- LogConfigCreate(void);
-
-/*
- * Free a log configuration. Future attempts to log with the passed
- * configuration will fail in an undefined way, such as by hanging the
- * process or segfaulting.
- *
- * Params:
- *
- * (LogConfig *) The configuration to free. All memory associated with
- * configuring the logging mechanism will be
- * invalidated.
- */
-extern void
- LogConfigFree(LogConfig *);
-
-/*
- * Set the current log level on the specified log configuration. This
- * indicates that only messages at or above this level should be
- * logged; all other messages are ignored by the Log() function.
- *
- * Params:
- *
- * (LogConfig *) The log configuration to set the log level on.
- * (int) The log level to set.
- */
-extern void
- LogConfigLevelSet(LogConfig *, int);
-
-/*
- * Indent the log output by two spaces. This can be helpful in
- * generating stack traces, or otherwise producing hierarchical output.
- * After calling this function, all future log messages using this
- * configuration will be indented.
- *
- * Params:
- *
- * (LogConfig *) The log configuration to indent.
- */
-extern void
- LogConfigIndent(LogConfig *);
-
-/*
- * Decrease the log output indent by two spaces. This can be helpful in
- * generating stack traces, or otherwise producing hierarchical output.
- * After calling this function, all future log messages using this
- * configuration will be unindented, unless there was no indentation
- * to begin with; in that case, this function will do nothing.
- *
- * Params:
- *
- * (LogConfig *) The log configuration to unindent.
- */
-extern void
- LogConfigUnindent(LogConfig *);
-
-/*
-* Set the log output indent to an arbitrary amount. This can be helpful
-* in generating stack traces, or otherwise producing hierarchical
-* output. After calling this function, all future log messages using
-* this configuration will be indented by the given amount.
-*
-* Params:
-*
-* (LogConfig *) The log configuration to apply the indent to.
-*/
-extern void
- LogConfigIndentSet(LogConfig *, size_t);
+LogConfigCreate(void);
extern void
- LogConfigOutputSet(LogConfig *, FILE *);
+LogConfigFree(LogConfig *config);
extern void
- LogConfigFlagSet(LogConfig *, int);
+LogConfigLevelSet(LogConfig *config, LogLevel level);
+
+extern LogLevel
+LogConfigLevelGet(LogConfig *config);
extern void
- LogConfigFlagClear(LogConfig *, int);
+LogConfigIndentSet(LogConfig *config, size_t indent);
+
+extern size_t
+LogConfigIndentGet(LogConfig *config);
extern void
- LogConfigTimeStampFormatSet(LogConfig *, char *);
+LogConfigIndent(LogConfig *config);
-/*
- * Actually log a message to a console, file, or other output device,
- * using the given log configuration. This function is thread-safe; it
- * locks a mutex before writing a message, and then unlocks it after
- * the message was written. It should therefore work well in
- * multithreaded environments, and with multiple different log
- * configurations, as each one has its own mutex.
- *
- * This function only logs messages if they are above the currently
- * configured log level. In this way, it is easy to turn some messages
- * on and off.
- *
- * This function is a printf() style function; it takes a format
- * string and any number of parameters to format.
- *
- * Params:
- *
- * (LogConfig *) The logging configuration.
- * (int) The level of the message to log.
- * (const char *) The format string, or a plain message string.
- * (...) Any items to map into the format string, printf()
- * style.
- */
extern void
- Log(LogConfig *, int, const char *,...);
+LogConfigUnindent(LogConfig *config);
+
+extern void
+LogConfigOutputSet(LogConfig *config, FILE *out);
+
+extern void
+LogConfigFlagSet(LogConfig *config, int flags);
+
+extern void
+LogConfigFlagClear(LogConfig *config, int flags);
+
+extern int
+LogConfigFlagGet(LogConfig *config, int flags);
+
+extern void
+LogConfigTimeStampFormatSet(LogConfig *config, char *tsFmt);
+
+extern void
+Log(LogConfig *config, LogLevel level, const char *msg, ...);
#endif