forked from Telodendria/Telodendria
Finish converting all existing documentation. Next up is writing new docs.
This commit is contained in:
parent
71fa96d10d
commit
b70c3f0bed
16 changed files with 1035 additions and 242 deletions
3
TODO.txt
3
TODO.txt
|
@ -32,10 +32,11 @@ Milestone: v0.3.0
|
||||||
[ ] http-debug-server
|
[ ] http-debug-server
|
||||||
[ ] tp
|
[ ] tp
|
||||||
[ ] send-patch
|
[ ] send-patch
|
||||||
[ ] Log
|
[x] Log
|
||||||
[ ] TelodendriaConfig -> Config
|
[ ] TelodendriaConfig -> Config
|
||||||
[x] HashMap
|
[x] HashMap
|
||||||
[ ] HttpRouter
|
[ ] HttpRouter
|
||||||
|
[x] Html
|
||||||
[ ] Str
|
[ ] Str
|
||||||
[ ] HeaderParser
|
[ ] HeaderParser
|
||||||
[ ] hdoc
|
[ ] hdoc
|
||||||
|
|
|
@ -568,7 +568,6 @@ HeaderParse(Stream * stream, HeaderExpr * expr)
|
||||||
{
|
{
|
||||||
/* Looks like we have an array. Slurp all the
|
/* Looks like we have an array. Slurp all the
|
||||||
* dimensions */
|
* dimensions */
|
||||||
int block = 1;
|
|
||||||
int i = wordLen;
|
int i = wordLen;
|
||||||
|
|
||||||
expr->data.global.name[i] = '[';
|
expr->data.global.name[i] = '[';
|
||||||
|
|
50
src/Util.c
50
src/Util.c
|
@ -168,56 +168,6 @@ UtilSleepMillis(long ms)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
|
||||||
UtilParseBytes(char *str)
|
|
||||||
{
|
|
||||||
size_t bytes = 0;
|
|
||||||
|
|
||||||
while (*str)
|
|
||||||
{
|
|
||||||
if (isdigit((unsigned char) *str))
|
|
||||||
{
|
|
||||||
bytes *= 10;
|
|
||||||
bytes += *str - '0';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (*str)
|
|
||||||
{
|
|
||||||
case 'K':
|
|
||||||
bytes *= 1024;
|
|
||||||
break;
|
|
||||||
case 'M':
|
|
||||||
bytes *= pow(1024, 2);
|
|
||||||
break;
|
|
||||||
case 'G':
|
|
||||||
bytes *= pow(1024, 3);
|
|
||||||
break;
|
|
||||||
case 'k':
|
|
||||||
bytes *= 1000;
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
bytes *= pow(1000, 2);
|
|
||||||
break;
|
|
||||||
case 'g':
|
|
||||||
bytes *= pow(1000, 3);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*(str + 1))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
UtilGetDelim(char **linePtr, size_t * n, int delim, Stream * stream)
|
UtilGetDelim(char **linePtr, size_t * n, int delim, Stream * stream)
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,10 +25,37 @@
|
||||||
#ifndef TELODENDRIA_CONFIG_H
|
#ifndef TELODENDRIA_CONFIG_H
|
||||||
#define TELODENDRIA_CONFIG_H
|
#define TELODENDRIA_CONFIG_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm Config
|
||||||
|
* @Nd Parse the Telodendria configuration into a structure.
|
||||||
|
* @Dd April 28 2023
|
||||||
|
* @Xr Db Json HttpServer Log
|
||||||
|
*
|
||||||
|
* .Nm
|
||||||
|
* validates and maintains the Telodendria server's configuration data.
|
||||||
|
* This API builds on the database and JSON APIs to add parsing
|
||||||
|
* specific to Telodendria. It converts a configuration in its raw
|
||||||
|
* form into a structure that is much easier and more convenient to
|
||||||
|
* work with.
|
||||||
|
* .Pp
|
||||||
|
* Since very early on in Telodendria's development, the configuration
|
||||||
|
* file has existed inside of the database. This API also offers
|
||||||
|
* convenience methods for extracting the configuration out of the
|
||||||
|
* database.
|
||||||
|
* .Pp
|
||||||
|
* This documentation does not describe the actual format of the
|
||||||
|
* configuration file; for that, consult
|
||||||
|
* .Xr telodendria-config 7 .
|
||||||
|
*/
|
||||||
|
|
||||||
#include <HashMap.h>
|
#include <HashMap.h>
|
||||||
#include <Array.h>
|
#include <Array.h>
|
||||||
#include <Db.h>
|
#include <Db.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bit flags that can be set in the flags field of the configuration
|
||||||
|
* structure.
|
||||||
|
*/
|
||||||
typedef enum ConfigFlag
|
typedef enum ConfigFlag
|
||||||
{
|
{
|
||||||
CONFIG_FEDERATION = (1 << 0),
|
CONFIG_FEDERATION = (1 << 0),
|
||||||
|
@ -39,11 +66,29 @@ typedef enum ConfigFlag
|
||||||
CONFIG_LOG_SYSLOG = (1 << 5)
|
CONFIG_LOG_SYSLOG = (1 << 5)
|
||||||
} ConfigFlag;
|
} ConfigFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration structure is not opaque like many of the other
|
||||||
|
* structures present in the other public APIs. This is intentional;
|
||||||
|
* defining functions for all of the fields would simply add too much
|
||||||
|
* unnecessary overhead.
|
||||||
|
*/
|
||||||
typedef struct Config
|
typedef struct Config
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* These are used internally and should not be touched outside of
|
||||||
|
* the functions defined in this API.
|
||||||
|
*/
|
||||||
Db *db;
|
Db *db;
|
||||||
DbRef *ref;
|
DbRef *ref;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Whether or not the parsing was successful. If this boolean
|
||||||
|
* value is 0, then read the error message and assume that all
|
||||||
|
* other fields are invalid.
|
||||||
|
*/
|
||||||
|
int ok;
|
||||||
|
char *err;
|
||||||
|
|
||||||
char *serverName;
|
char *serverName;
|
||||||
char *baseUrl;
|
char *baseUrl;
|
||||||
char *identityServer;
|
char *identityServer;
|
||||||
|
@ -58,28 +103,61 @@ typedef struct Config
|
||||||
char *logTimestamp;
|
char *logTimestamp;
|
||||||
int logLevel;
|
int logLevel;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An array of HttpServerConfig structures. Consult the HttpServer
|
||||||
|
* API.
|
||||||
|
*/
|
||||||
Array *servers;
|
Array *servers;
|
||||||
|
|
||||||
int ok;
|
|
||||||
char *err;
|
|
||||||
} Config;
|
} Config;
|
||||||
|
|
||||||
Config *
|
/**
|
||||||
ConfigParse(HashMap *);
|
* Parse a JSON object, extracting the necessary values, validating
|
||||||
|
* them, and adding them to the configuration structure for use by the
|
||||||
|
* caller. All values are copied, so the JSON object can be safely
|
||||||
|
* freed after this function returns.
|
||||||
|
* .Pp
|
||||||
|
* If an error occurs, this function will not return NULL, but it will
|
||||||
|
* set the ok flag to 0. The caller should always check the ok flag,
|
||||||
|
* and if there is an error, it should display the error to the user.
|
||||||
|
*/
|
||||||
|
Config * ConfigParse(HashMap *);
|
||||||
|
|
||||||
void
|
/**
|
||||||
ConfigFree(Config *);
|
* Free all the values inside of the given configuration structure,
|
||||||
|
* as well as the structure itself, such that it is completely invalid
|
||||||
|
* when this function returns.
|
||||||
|
*/
|
||||||
|
void ConfigFree(Config *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
ConfigExists(Db *);
|
* Check whether or not the configuration exists in the database,
|
||||||
|
* returning a boolean value to indicate the status.
|
||||||
|
*/
|
||||||
|
extern int ConfigExists(Db *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
ConfigCreateDefault(Db *);
|
* Create a sane default configuration in the specified database.
|
||||||
|
* This function returns a boolean value indicating whether or not it
|
||||||
|
* was successful.
|
||||||
|
*/
|
||||||
|
extern int ConfigCreateDefault(Db *);
|
||||||
|
|
||||||
extern Config *
|
/**
|
||||||
ConfigLock(Db *);
|
* Lock the configuration in the database using
|
||||||
|
* .Fn DbLock ,
|
||||||
|
* and then parse the object using
|
||||||
|
* .Fn ConfigParse .
|
||||||
|
* The return value of this function is the same as
|
||||||
|
* .Fn ConfigParse .
|
||||||
|
*/
|
||||||
|
extern Config * ConfigLock(Db *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
ConfigUnlock(Config *);
|
* Unlock the specified configuration, returning it back to the
|
||||||
|
* database. This function also invalidates all memory associated with
|
||||||
|
* this config object, so values that should be retained after this is
|
||||||
|
* called should be duplicated as necessary.
|
||||||
|
*/
|
||||||
|
extern int ConfigUnlock(Config *);
|
||||||
|
|
||||||
#endif /* TELODENDRIA_CONFIG_H */
|
#endif /* TELODENDRIA_CONFIG_H */
|
||||||
|
|
|
@ -24,6 +24,37 @@
|
||||||
#ifndef TELODENDRIA_HTML_H
|
#ifndef TELODENDRIA_HTML_H
|
||||||
#define TELODENDRIA_HTML_H
|
#define TELODENDRIA_HTML_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm Html
|
||||||
|
* @Nd Utility functions for generating static HTML pages.
|
||||||
|
* @Dd April 27 2023
|
||||||
|
*
|
||||||
|
* .Nm
|
||||||
|
* provides some simple macros and functions for generating HTML
|
||||||
|
* pages. These are very specific to Telodendria, as they automatically
|
||||||
|
* apply the color scheme and make assumptions about the stylesheets
|
||||||
|
* and scripts included.
|
||||||
|
* .Pp
|
||||||
|
* The following macros are available:
|
||||||
|
* .Bl -tag -width Ds
|
||||||
|
* .It HtmlBeginJs(stream)
|
||||||
|
* Begin JavaScript output. This sets up the opening script tags, and
|
||||||
|
* licenses the following JavaScript under the MIT license.
|
||||||
|
* .It HtmlEndJs(stream)
|
||||||
|
* End JavaScript output.
|
||||||
|
* .It HtmlBeginStyle(stream)
|
||||||
|
* Begin CSS output. This sets up the opening syle tags.
|
||||||
|
* .It HtmlEndStyle(stream)
|
||||||
|
* End CSS output.
|
||||||
|
* .It HtmlBeginForm(stream, id)
|
||||||
|
* Begin a new form with the specified ID. This sets up the opening
|
||||||
|
* form tags, which includes placing the form in a div with class
|
||||||
|
* 'form'.
|
||||||
|
* .It HtmlEndForm(stream)
|
||||||
|
* End HTML form output.
|
||||||
|
* .El
|
||||||
|
*/
|
||||||
|
|
||||||
#include <Stream.h>
|
#include <Stream.h>
|
||||||
|
|
||||||
#define HtmlBeginJs(stream) StreamPuts(stream, \
|
#define HtmlBeginJs(stream) StreamPuts(stream, \
|
||||||
|
@ -46,10 +77,18 @@
|
||||||
"<p id=\"error-msg\"></p>" \
|
"<p id=\"error-msg\"></p>" \
|
||||||
"</div>");
|
"</div>");
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
HtmlBegin(Stream *, char *);
|
* Initialize an HTML page by writing the head and the beginning of the
|
||||||
|
* body. After this function is called, the page's main HTML can be
|
||||||
|
* written. This function takes the name of the page it is beginning.
|
||||||
|
* The name is placed in the title tags, and is used as the page
|
||||||
|
* header.
|
||||||
|
*/
|
||||||
|
extern void HtmlBegin(Stream *, char *);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
HtmlEnd(Stream *);
|
* Finish an HTML page by writing any necessary closing tags.
|
||||||
|
*/
|
||||||
|
extern void HtmlEnd(Stream *);
|
||||||
|
|
||||||
#endif /* TELODENDRIA_HTML_H */
|
#endif /* TELODENDRIA_HTML_H */
|
||||||
|
|
|
@ -109,25 +109,88 @@ extern void LogConfigUnindent(LogConfig *);
|
||||||
*/
|
*/
|
||||||
extern void LogConfigIndentSet(LogConfig *, size_t);
|
extern void LogConfigIndentSet(LogConfig *, size_t);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
LogConfigOutputSet(LogConfig *, Stream *);
|
* Set the file stream that logging output should be written to. This
|
||||||
|
* defaults to standard output, but it can be set to standard error,
|
||||||
|
* or any other arbitrary stream. Passing a NULL value for the stream
|
||||||
|
* pointer sets the log output to the standard output. Note that the
|
||||||
|
* output stream is only used if
|
||||||
|
* .Va LOG_FLAG_SYSLOG
|
||||||
|
* is not set.
|
||||||
|
*/
|
||||||
|
extern void LogConfigOutputSet(LogConfig *, Stream *);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
LogConfigFlagSet(LogConfig *, int);
|
* Set a number of boolean options on a log configuration. This
|
||||||
|
* function uses bitwise operators, so multiple options can be set with
|
||||||
|
* a single function call using bitwise OR operators. The flags are
|
||||||
|
* defined as preprocessor macros, and are as follows:
|
||||||
|
* .Bl -tag -width Ds
|
||||||
|
* .It LOG_FLAG_COLOR
|
||||||
|
* When set, enable color-coded output on TTYs. Note that colors are
|
||||||
|
* implemented as ANSI escape sequences, and are not written to file
|
||||||
|
* streams that are not actually connected to a TTY, to prevent those
|
||||||
|
* sequences from being written to a file.
|
||||||
|
* .Xr isatty 3
|
||||||
|
* is checked before writing any terminal sequences.
|
||||||
|
* .It LOG_FLAG_SYSLOG
|
||||||
|
* When set, log output to the syslog using
|
||||||
|
* .Xr syslog 3 ,
|
||||||
|
* instead of logging to the file set by
|
||||||
|
* .Fn LogConfigOutputSet .
|
||||||
|
* This flag always overrides the stream set by that function,
|
||||||
|
* regardless of when it was set, even if it was set after this flag
|
||||||
|
* was set.
|
||||||
|
* .El
|
||||||
|
*/
|
||||||
|
extern void LogConfigFlagSet(LogConfig *, int);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
LogConfigFlagClear(LogConfig *, int);
|
* Clear a boolean flag from the specified log format. See above for
|
||||||
|
* the list of flags.
|
||||||
|
*/
|
||||||
|
extern void LogConfigFlagClear(LogConfig *, int);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
LogConfigTimeStampFormatSet(LogConfig *, char *);
|
* Set a custom timestamp to be prepended to each message if the
|
||||||
|
* output is not going to the system log. Consult your system's
|
||||||
|
* documentation for
|
||||||
|
* .Xr strftime 3 .
|
||||||
|
* A value of NULL disables the timestamp output before messages.
|
||||||
|
*/
|
||||||
|
extern void LogConfigTimeStampFormatSet(LogConfig *, char *);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
Logv(LogConfig *, int, const char *, va_list);
|
* This function does the actual logging of messages using a
|
||||||
|
* specified configuration. It takes the configuration, the log
|
||||||
|
* level, a format string, and then a list of arguments, all in that
|
||||||
|
* order. This function only logs messages if their level is above
|
||||||
|
* or equal to the currently configured log level, making it easy to
|
||||||
|
* turn some messages on or off.
|
||||||
|
* .Pp
|
||||||
|
* This function has the same usage as
|
||||||
|
* .Xr vprintf 3 .
|
||||||
|
* Consult that page for the list of format specifiers and their
|
||||||
|
* arguments. This function is typically not used directly, see the
|
||||||
|
* other log functions for the most common use cases.
|
||||||
|
*/
|
||||||
|
extern void Logv(LogConfig *, int, const char *, va_list);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
LogTo(LogConfig *, int, const char *,...);
|
* Log a message using
|
||||||
|
* .Fn Logv .
|
||||||
|
* with the specified configuration. This function has the same usage
|
||||||
|
* as
|
||||||
|
* .Xr printf 3 .
|
||||||
|
*/
|
||||||
|
extern void LogTo(LogConfig *, int, const char *, ...);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
Log(int, const char *,...);
|
* Log a message to the global log using
|
||||||
|
* .Fn Logv .
|
||||||
|
* This function has the same usage as
|
||||||
|
* .Xr printf 3 .
|
||||||
|
*/
|
||||||
|
extern void Log(int, const char *, ...);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,6 +24,18 @@
|
||||||
#ifndef TELODENDRIA_MATRIX_H
|
#ifndef TELODENDRIA_MATRIX_H
|
||||||
#define TELODENDRIA_MATRIX_H
|
#define TELODENDRIA_MATRIX_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm Matrix
|
||||||
|
* @Nd Functions for writing Matrix API Endpoints.
|
||||||
|
* @Dd March 6 2023
|
||||||
|
* @Xr HttpServer Log Config Db
|
||||||
|
*
|
||||||
|
* .Nm
|
||||||
|
* provides some helper functions that bind to the HttpServer API and
|
||||||
|
* add basic Matrix functionality, turning an HTTP server into a
|
||||||
|
* Matrix homeserver.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <HttpServer.h>
|
#include <HttpServer.h>
|
||||||
#include <HttpRouter.h>
|
#include <HttpRouter.h>
|
||||||
#include <Log.h>
|
#include <Log.h>
|
||||||
|
@ -32,6 +44,12 @@
|
||||||
#include <Config.h>
|
#include <Config.h>
|
||||||
#include <Db.h>
|
#include <Db.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The valid errors that can be used with
|
||||||
|
* .Fn MatrixErrorCreate .
|
||||||
|
* These values exactly follow the errors defined in the Matrix
|
||||||
|
* specification.
|
||||||
|
*/
|
||||||
typedef enum MatrixError
|
typedef enum MatrixError
|
||||||
{
|
{
|
||||||
M_FORBIDDEN,
|
M_FORBIDDEN,
|
||||||
|
@ -68,25 +86,70 @@ typedef enum MatrixError
|
||||||
M_CANNOT_LEAVE_SERVER_NOTICE_ROOM
|
M_CANNOT_LEAVE_SERVER_NOTICE_ROOM
|
||||||
} MatrixError;
|
} MatrixError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The arguments that should be passed through the void pointer to the
|
||||||
|
* .Fn MatrixHttpHandler
|
||||||
|
* function. This structure should be populated once, and then never
|
||||||
|
* modified again for the duration of the HTTP server.
|
||||||
|
*/
|
||||||
typedef struct MatrixHttpHandlerArgs
|
typedef struct MatrixHttpHandlerArgs
|
||||||
{
|
{
|
||||||
Db *db;
|
Db *db;
|
||||||
HttpRouter *router;
|
HttpRouter *router;
|
||||||
} MatrixHttpHandlerArgs;
|
} MatrixHttpHandlerArgs;
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
MatrixHttpHandler(HttpServerContext *, void *);
|
* The HTTP handler function that handles all Matrix homeserver
|
||||||
|
* functionality. It should be passed into
|
||||||
|
* .Fn HttpServerCreate ,
|
||||||
|
* and it expects that a pointer to a MatrixHttpHandlerArgs
|
||||||
|
* will be provided, because that is what the void pointer is
|
||||||
|
* cast to.
|
||||||
|
*/
|
||||||
|
extern void MatrixHttpHandler(HttpServerContext *, void *);
|
||||||
|
|
||||||
extern HashMap *
|
/**
|
||||||
MatrixErrorCreate(MatrixError);
|
* A convenience function that constructs an error payload, including
|
||||||
|
* the error code and message, given just a MatrixError.
|
||||||
|
*/
|
||||||
|
extern HashMap * MatrixErrorCreate(MatrixError);
|
||||||
|
|
||||||
extern HashMap *
|
/**
|
||||||
MatrixGetAccessToken(HttpServerContext *, char **);
|
* Read the request headers and parameters, and attempt to obtain an
|
||||||
|
* access token from them. The Matrix specification says that an access
|
||||||
|
* token can either be provided via the Authorization header, or in a
|
||||||
|
* .Sy GET
|
||||||
|
* parameter. This function checks both, and stores the access token it
|
||||||
|
* finds in the passed character pointer.
|
||||||
|
* .Pp
|
||||||
|
* The specification does not say whether the header or parameter
|
||||||
|
* should be preferred if both are provided. This function prefers the
|
||||||
|
* header.
|
||||||
|
* .Pp
|
||||||
|
* If this function returns a non-NULL value, then the return value
|
||||||
|
* should be immediately passed along to the client and no further
|
||||||
|
* logic should be performed.
|
||||||
|
*/
|
||||||
|
extern HashMap * MatrixGetAccessToken(HttpServerContext *, char **);
|
||||||
|
|
||||||
extern HashMap *
|
/**
|
||||||
MatrixRateLimit(HttpServerContext *, Db *);
|
* Determine whether or not the request should be rate limited. It is
|
||||||
|
* expected that this function will be called before most, if not all
|
||||||
|
* of the caller's logic.
|
||||||
|
* .Pp
|
||||||
|
* If this function returns a non-NULL value, then the return value
|
||||||
|
* should be immediately passed along to the client and no further
|
||||||
|
* logic should be performed.
|
||||||
|
*/
|
||||||
|
extern HashMap * MatrixRateLimit(HttpServerContext *, Db *);
|
||||||
|
|
||||||
extern HashMap *
|
/**
|
||||||
MatrixClientWellKnown(char *, char *);
|
* Build a ``well-known'' JSON object, which contains information
|
||||||
|
* about the homeserver base URL and identity server, both of which
|
||||||
|
* should be provided by the caller in that order. This object can be
|
||||||
|
* sent to a client as-is, or it can be added as a value nested inside
|
||||||
|
* of a more complex response. Both occur in the Matrix specification.
|
||||||
|
*/
|
||||||
|
extern HashMap * MatrixClientWellKnown(char *, char *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,8 +24,63 @@
|
||||||
#ifndef TELODENDRIA_MEMORY_H
|
#ifndef TELODENDRIA_MEMORY_H
|
||||||
#define TELODENDRIA_MEMORY_H
|
#define TELODENDRIA_MEMORY_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm Memory
|
||||||
|
* @Nd Smart memory management.
|
||||||
|
* @Dd January 9 2023
|
||||||
|
*
|
||||||
|
* .Nm
|
||||||
|
* is an API that allows for smart memory management and profiling. It
|
||||||
|
* wraps the standard library functions
|
||||||
|
* .Xr malloc 3 ,
|
||||||
|
* .Xr realloc 3 ,
|
||||||
|
* and
|
||||||
|
* .Xr free 3 ,
|
||||||
|
* and offers identical semantics, while providing functionality that
|
||||||
|
* the standard library doesn't have, such as getting statistics on the
|
||||||
|
* total memory allocated on the heap, and getting the size of a block
|
||||||
|
* given a pointer. Additionally, thanks to preprocessor macros, the
|
||||||
|
* exact file and line number at which an allocation, re-allocation, or
|
||||||
|
* free occured can be obtained given a pointer. Finally, all the
|
||||||
|
* blocks allocated on the heap can be iterated and evaluated, and a
|
||||||
|
* callback function can be executed every time a memory operation
|
||||||
|
* occurs.
|
||||||
|
* .Pp
|
||||||
|
* In the future, this API could include a garbage collector that
|
||||||
|
* automatically frees memory it detects as being no longer in use.
|
||||||
|
* However, this feature does not yet exist.
|
||||||
|
* .Pp
|
||||||
|
* A number of macros are available, which make the
|
||||||
|
* .Nm
|
||||||
|
* API much easier to use. They are as follows:
|
||||||
|
* .Bl -bullet -offset indent
|
||||||
|
* .It
|
||||||
|
* .Fn Malloc "x"
|
||||||
|
* .It
|
||||||
|
* .Fn Realloc "x" "y"
|
||||||
|
* .It
|
||||||
|
* .Fn Free "x"
|
||||||
|
* .El
|
||||||
|
* .Pp
|
||||||
|
* These macros expand to
|
||||||
|
* .Fn MemoryAllocate ,
|
||||||
|
* .Fn MemoryReallocate ,
|
||||||
|
* and
|
||||||
|
* .Fn MemoryFree
|
||||||
|
* with the second and third parameters set to __FILE__ and __LINE__.
|
||||||
|
* This allows
|
||||||
|
* .Nm
|
||||||
|
* to be used exactly how the standard library functions would be
|
||||||
|
* used. In fact, the functions to which these macros expand are not
|
||||||
|
* intended to be used directly; for the best results, use these
|
||||||
|
* macros.
|
||||||
|
*/
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These values are passed into the memory hook function to indicate
|
||||||
|
* the action that just happened.
|
||||||
|
*/
|
||||||
typedef enum MemoryAction
|
typedef enum MemoryAction
|
||||||
{
|
{
|
||||||
MEMORY_ALLOCATE,
|
MEMORY_ALLOCATE,
|
||||||
|
@ -38,44 +93,132 @@ typedef enum MemoryAction
|
||||||
#define Realloc(x, s) MemoryReallocate(x, s, __FILE__, __LINE__)
|
#define Realloc(x, s) MemoryReallocate(x, s, __FILE__, __LINE__)
|
||||||
#define Free(x) MemoryFree(x, __FILE__, __LINE__)
|
#define Free(x) MemoryFree(x, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The memory information is opaque, but can be accessed using the
|
||||||
|
* functions defined by this API.
|
||||||
|
*/
|
||||||
typedef struct MemoryInfo MemoryInfo;
|
typedef struct MemoryInfo MemoryInfo;
|
||||||
|
|
||||||
extern void *
|
/**
|
||||||
MemoryAllocate(size_t, const char *, int);
|
* Allocate the specified number of bytes on the heap. This function
|
||||||
|
* has the same semantics as
|
||||||
|
* .Xr malloc 3 ,
|
||||||
|
* except that it takes the file name and line number at which the
|
||||||
|
* allocation occurred.
|
||||||
|
*/
|
||||||
|
extern void * MemoryAllocate(size_t, const char *, int);
|
||||||
|
|
||||||
extern void *
|
/**
|
||||||
MemoryReallocate(void *, size_t, const char *, int);
|
* Change the size of the object pointed to by the given pointer
|
||||||
|
* to the given number of bytes. This function has the same semantics
|
||||||
|
* as
|
||||||
|
* .Xr realloc 3 ,
|
||||||
|
* except that it takes the file name and line number at which the
|
||||||
|
* reallocation occurred.
|
||||||
|
*/
|
||||||
|
extern void * MemoryReallocate(void *, size_t, const char *, int);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
MemoryFree(void *, const char *, int);
|
* Free the memory at the given pointer. This function has the same
|
||||||
|
* semantics as
|
||||||
|
* .Xr free 3 ,
|
||||||
|
* except that it takes the file name and line number at which the
|
||||||
|
* free occurred.
|
||||||
|
*/
|
||||||
|
extern void MemoryFree(void *, const char *, int);
|
||||||
|
|
||||||
extern size_t
|
/**
|
||||||
MemoryAllocated(void);
|
* Get the total number of bytes that the program has allocated on the
|
||||||
|
* heap. This operation iterates over all heap allocations made with
|
||||||
|
* .Fn MemoryAllocate
|
||||||
|
* and then returns a total count, in bytes.
|
||||||
|
*/
|
||||||
|
extern size_t MemoryAllocated(void);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
MemoryFreeAll(void);
|
* Iterate over all heap allocations made with
|
||||||
|
* .Fn MemoryAllocate
|
||||||
|
* and call
|
||||||
|
* .Fn MemoryFree
|
||||||
|
* on them. This function immediately invalidates all pointers to
|
||||||
|
* blocks on the heap, and any subsequent attempt to read or write to
|
||||||
|
* data on the heap will result in undefined behavior. This is
|
||||||
|
* typically called at the end of the program, just before exit.
|
||||||
|
*/
|
||||||
|
extern void MemoryFreeAll(void);
|
||||||
|
|
||||||
extern MemoryInfo *
|
/**
|
||||||
MemoryInfoGet(void *);
|
* Fetch information about an allocation. This function takes a raw
|
||||||
|
* pointer, and if
|
||||||
|
* . Nm
|
||||||
|
* knows about the pointer, it returns a structure that can be used
|
||||||
|
* to obtain information about the block of memory that the pointer
|
||||||
|
* points to.
|
||||||
|
*/
|
||||||
|
extern MemoryInfo * MemoryInfoGet(void *);
|
||||||
|
|
||||||
extern size_t
|
/**
|
||||||
MemoryInfoGetSize(MemoryInfo *);
|
* Get the size in bytes of the block of memory represented by the
|
||||||
|
* specified memory info structure.
|
||||||
|
*/
|
||||||
|
extern size_t MemoryInfoGetSize(MemoryInfo *);
|
||||||
|
|
||||||
extern const char *
|
/**
|
||||||
MemoryInfoGetFile(MemoryInfo *);
|
* Get the file name in which the block of memory represented by the
|
||||||
|
* specified memory info structure was allocated.
|
||||||
|
*/
|
||||||
|
extern const char * MemoryInfoGetFile(MemoryInfo *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
MemoryInfoGetLine(MemoryInfo *);
|
* Get the line number on which the block of memory represented by the
|
||||||
|
* specified memory info structure was allocated.
|
||||||
|
*/
|
||||||
|
extern int MemoryInfoGetLine(MemoryInfo *);
|
||||||
|
|
||||||
extern void *
|
/**
|
||||||
MemoryInfoGetPointer(MemoryInfo *);
|
* Get a pointer to the block of memory represented by the specified
|
||||||
|
* memory info structure.
|
||||||
|
*/
|
||||||
|
extern void * MemoryInfoGetPointer(MemoryInfo *);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
MemoryIterate(void (*) (MemoryInfo *, void *), void *);
|
* This function takes a pointer to a function that takes the memory
|
||||||
|
* info structure, as well as a void pointer for caller-provided
|
||||||
|
* arguments. It iterates over all the heap memory currently allocated
|
||||||
|
* at the time of calling, executing the function on each allocation.
|
||||||
|
*/
|
||||||
|
extern void MemoryIterate(void (*) (MemoryInfo *, void *), void *);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
MemoryHook(void (*) (MemoryAction, MemoryInfo *, void *), void *);
|
* Specify a function to be executed whenever a memory operation
|
||||||
|
* occurs. The MemoryAction argument specifies the operation that
|
||||||
|
* occurred on the block of memory represented by the memory info
|
||||||
|
* structure. The function also takes a void pointer to caller-provided
|
||||||
|
* arguments.
|
||||||
|
*/
|
||||||
|
extern void MemoryHook(void (*) (MemoryAction, MemoryInfo *, void *), void *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read over the block of memory represented by the given memory info
|
||||||
|
* structure and generate a hexadecimal and ASCII string for each
|
||||||
|
* chunk of the block. This function takes a callback function that
|
||||||
|
* takes the following parameters in order:
|
||||||
|
* .Bl -bullet -offset indent
|
||||||
|
* .It
|
||||||
|
* The current offset from the beginning of the block of memory in
|
||||||
|
* bytes.
|
||||||
|
* .It
|
||||||
|
* A null-terminated string containing the next 16 bytes of the block
|
||||||
|
* encoded as space-separated hex values.
|
||||||
|
* .It
|
||||||
|
* A null-terminated string containing the ASCII representation of the
|
||||||
|
* same 16 bytes of memory. This ASCII representation is safe to print
|
||||||
|
* to a terminal or other text device, because non-printable characters
|
||||||
|
* are encoded as a . (period).
|
||||||
|
* .It
|
||||||
|
* Caller-passed pointer.
|
||||||
|
* .El
|
||||||
|
*/
|
||||||
extern void
|
extern void
|
||||||
MemoryHexDump(MemoryInfo *, void (*) (size_t, char *, char *, void *), void *);
|
MemoryHexDump(MemoryInfo *, void (*) (size_t, char *, char *, void *), void *);
|
||||||
|
|
||||||
|
|
|
@ -24,29 +24,82 @@
|
||||||
#ifndef TELODENDRIA_QUEUE_H
|
#ifndef TELODENDRIA_QUEUE_H
|
||||||
#define TELODENDRIA_QUEUE_H
|
#define TELODENDRIA_QUEUE_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm Queue
|
||||||
|
* @Nd A simple static queue data structure.
|
||||||
|
* @Dd November 25 2022
|
||||||
|
* @Xr Array HashMap
|
||||||
|
*
|
||||||
|
* .Nm
|
||||||
|
* implements a simple queue data structure that is statically sized.
|
||||||
|
* This implementation does not actually store the values of the items
|
||||||
|
* in it; it only stores pointers to the data. As such, you will have
|
||||||
|
* to manually maintain data and make sure it remains valid as long as
|
||||||
|
* it is in the queue. The advantage of this is that
|
||||||
|
* .Nm
|
||||||
|
* doesn't have to copy data, and thus doesn't care how big the data
|
||||||
|
* is. Furthermore, any arbitrary data can be stored in the queue.
|
||||||
|
* .Pp
|
||||||
|
* This queue implementation operates on the heap. It is a circular
|
||||||
|
* queue, and it does not grow as it is used. Once the size is set,
|
||||||
|
* the queue never gets any bigger.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These functions operate on a queue structure that is opaque to the
|
||||||
|
* caller.
|
||||||
|
*/
|
||||||
typedef struct Queue Queue;
|
typedef struct Queue Queue;
|
||||||
|
|
||||||
extern Queue *
|
/**
|
||||||
QueueCreate(size_t);
|
* Allocate a new queue that is able to store the specified number of
|
||||||
|
* items in it.
|
||||||
|
*/
|
||||||
|
extern Queue * QueueCreate(size_t);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
QueueFree(Queue *);
|
* Free the memory associated with the specified queue structure. Note
|
||||||
|
* that this function does not free any of the values stored in the
|
||||||
|
* queue; it is the caller's job to manage memory for each item.
|
||||||
|
* Typically, the caller would dequeue all the items in the queue and
|
||||||
|
* deal with them before freeing the queue itself.
|
||||||
|
*/
|
||||||
|
extern void QueueFree(Queue *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
QueuePush(Queue *, void *);
|
* Push an element into the queue. This function returns a boolean
|
||||||
|
* value indicating whether or not the push succeeded. Pushing items
|
||||||
|
* into the queue will fail if the queue is full.
|
||||||
|
*/
|
||||||
|
extern int QueuePush(Queue *, void *);
|
||||||
|
|
||||||
extern void *
|
/**
|
||||||
QueuePop(Queue *);
|
* Pop an element out of the queue. This function returns NULL if the
|
||||||
|
* queue is empty. Otherwise, it returns a pointer to the item that is
|
||||||
|
* next up in the queue.
|
||||||
|
*/
|
||||||
|
extern void * QueuePop(Queue *);
|
||||||
|
|
||||||
extern void *
|
/**
|
||||||
QueuePeek(Queue *);
|
* Retrieve a pointer to the item that is next up in the queue without
|
||||||
|
* actually discarding it, such that the next call to
|
||||||
|
* .Fn QueuePeek
|
||||||
|
* or
|
||||||
|
* .Fn QueuePop
|
||||||
|
* will return the same pointer.
|
||||||
|
*/
|
||||||
|
extern void * QueuePeek(Queue *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
QueueFull(Queue *);
|
* Determine whether or not the queue is full.
|
||||||
|
*/
|
||||||
|
extern int QueueFull(Queue *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
QueueEmpty(Queue *);
|
* Determine whether or not the queue is empty.
|
||||||
|
*/
|
||||||
|
extern int QueueEmpty(Queue *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,12 +24,58 @@
|
||||||
|
|
||||||
#ifndef TELODENDRIA_RAND_H
|
#ifndef TELODENDRIA_RAND_H
|
||||||
#define TELODENDRIA_RAND_H
|
#define TELODENDRIA_RAND_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm Rand
|
||||||
|
* @Nd Thread-safe random numbers.
|
||||||
|
* @Dd February 16 2023
|
||||||
|
* @Xr Util
|
||||||
|
*
|
||||||
|
* .Nm
|
||||||
|
* is used for generating random numbers in a thread-safe way.
|
||||||
|
* Currently, one generator state is shared across all threads, which
|
||||||
|
* means that only one thread can generate random numbers at a time.
|
||||||
|
* This state is protected with a mutex to guarantee this behavior.
|
||||||
|
* In the future, a seed pool may be maintained to allow multiple
|
||||||
|
* threads to generate random numbers at the same time.
|
||||||
|
* .Pp
|
||||||
|
* The generator state is seeded on the first call to a function that
|
||||||
|
* needs it. The seed is determined by the current timestamp, the ID
|
||||||
|
* of the process, and the thread ID. These should all be sufficiently
|
||||||
|
* random sources, so the seed should be secure enough.
|
||||||
|
* .Pp
|
||||||
|
* .Nm
|
||||||
|
* currently uses a simple Mersenne Twister algorithm to generate
|
||||||
|
* random numbers. This algorithm was chosen because it is extremely
|
||||||
|
* popular and widespread. While it is likely not cryptographically
|
||||||
|
* secure, and does suffer some unfortunate pitfalls, this algorithm
|
||||||
|
* has stood the test of time and is simple enough to implement, so
|
||||||
|
* it was chosen over the alternatives.
|
||||||
|
* .Pp
|
||||||
|
* .Nm
|
||||||
|
* does not use any random number generator functions from the C
|
||||||
|
* standard library, since these are often flawed.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
RandInt(unsigned int);
|
* Generate a single random integer between 0 and the passed value.
|
||||||
|
*/
|
||||||
|
extern int RandInt(unsigned int);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
RandIntN(int *, size_t, unsigned int);
|
* Generate the number of integers specified by the second argument
|
||||||
|
* storing them into the buffer pointed to in the first argument.
|
||||||
|
* Ensure that each number is between 0 and the third argument.
|
||||||
|
* .Pp
|
||||||
|
* This function allows a caller to get multiple random numbers at once
|
||||||
|
* in a more efficient manner than repeatedly calling
|
||||||
|
* .Fn RandInt ,
|
||||||
|
* since each call to these functions
|
||||||
|
* has to lock and unlock a mutex. It is therefore better to obtain
|
||||||
|
* multiple random numbers in one pass if multiple are needed.
|
||||||
|
*/
|
||||||
|
extern void RandIntN(int *, size_t, unsigned int);
|
||||||
|
|
||||||
#endif /* TELODENDRIA_RAND_H */
|
#endif /* TELODENDRIA_RAND_H */
|
||||||
|
|
|
@ -24,7 +24,19 @@
|
||||||
#ifndef TELODENDRIA_ROUTES_H
|
#ifndef TELODENDRIA_ROUTES_H
|
||||||
#define TELODENDRIA_ROUTES_H
|
#define TELODENDRIA_ROUTES_H
|
||||||
|
|
||||||
#include <string.h>
|
/***
|
||||||
|
* @Nm Routes
|
||||||
|
* @Nd Matrix API endpoint handler functions.
|
||||||
|
* @Dd April 28 2023
|
||||||
|
* @Xr Matrix HttpRouter
|
||||||
|
*
|
||||||
|
* .Nm
|
||||||
|
* provides all of the Matrix API route functions, which for the sake
|
||||||
|
* of brevity are not documented here---consult the official Matrix
|
||||||
|
* specification for documentation on Matrix routes and the
|
||||||
|
* .Xr telodendria-admin
|
||||||
|
* page for admin API routes.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <HashMap.h>
|
#include <HashMap.h>
|
||||||
#include <Array.h>
|
#include <Array.h>
|
||||||
|
@ -32,25 +44,34 @@
|
||||||
#include <HttpRouter.h>
|
#include <HttpRouter.h>
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define MATRIX_PATH_EQUALS(pathPart, str) \
|
#define MATRIX_PATH_EQUALS(pathPart, str) \
|
||||||
((pathPart != NULL) && (strcmp(pathPart, str) == 0))
|
((pathPart != NULL) && (strcmp(pathPart, str) == 0))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every route function takes this structure, which contains the data
|
||||||
|
* it needs to successfully handle an API request.
|
||||||
|
*/
|
||||||
typedef struct RouteArgs
|
typedef struct RouteArgs
|
||||||
{
|
{
|
||||||
MatrixHttpHandlerArgs *matrixArgs;
|
MatrixHttpHandlerArgs *matrixArgs;
|
||||||
HttpServerContext *context;
|
HttpServerContext *context;
|
||||||
} RouteArgs;
|
} RouteArgs;
|
||||||
|
|
||||||
HttpRouter *
|
/**
|
||||||
RouterBuild(void);
|
* Build an HTTP router that sets up all the route functions to be
|
||||||
|
* executed at the correct HTTP paths.
|
||||||
|
*/
|
||||||
|
extern HttpRouter * RouterBuild(void);
|
||||||
|
|
||||||
#define ROUTE(name) \
|
#define ROUTE(name) \
|
||||||
extern void * \
|
extern void * \
|
||||||
name(Array *, void *)
|
name(Array *, void *)
|
||||||
|
|
||||||
#define ROUTE_IMPL(name, matchesName, argsName) \
|
#define ROUTE_IMPL(name, path, args) \
|
||||||
void * \
|
void * \
|
||||||
name(Array * matchesName, void * argsName)
|
name(Array * path, void * args)
|
||||||
|
|
||||||
ROUTE(RouteVersions);
|
ROUTE(RouteVersions);
|
||||||
ROUTE(RouteWellKnown);
|
ROUTE(RouteWellKnown);
|
||||||
|
|
|
@ -24,24 +24,68 @@
|
||||||
#ifndef TELODENDRIA_STR_H
|
#ifndef TELODENDRIA_STR_H
|
||||||
#define TELODENDRIA_STR_H
|
#define TELODENDRIA_STR_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm Str
|
||||||
|
* @Nd Functions for creating and manipulating strings.
|
||||||
|
* @Dd February 15 2023
|
||||||
|
* @Xr Memory
|
||||||
|
*
|
||||||
|
* .Nm
|
||||||
|
* provides string-related functions. It is called
|
||||||
|
* .Nm ,
|
||||||
|
* not String, because some platforms (Windows) do not have
|
||||||
|
* case-sensitive filesystems, which poses a problem since
|
||||||
|
* .Pa string.h
|
||||||
|
* is a standard library header.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
extern char *
|
/**
|
||||||
StrUtf8Encode(unsigned long);
|
* Take a UTF-8 codepoint and encode it into a string buffer containing
|
||||||
|
* between 1 and 4 bytes. The string buffer is allocated on the heap,
|
||||||
|
* so it should be freed when it is no longer needed.
|
||||||
|
*/
|
||||||
|
extern char * StrUtf8Encode(unsigned long);
|
||||||
|
|
||||||
extern char *
|
/**
|
||||||
StrDuplicate(const char *);
|
* Duplicate a null-terminated string, returning a new string on the
|
||||||
|
* heap. This is useful when a function takes in a string that it needs
|
||||||
|
* to store for long amounts of time, even perhaps after the original
|
||||||
|
* string is gone.
|
||||||
|
*/
|
||||||
|
extern char * StrDuplicate(const char *);
|
||||||
|
|
||||||
extern char *
|
/**
|
||||||
StrSubstr(const char *, size_t, size_t);
|
* Extract part of a null-terminated string, returning a new string on
|
||||||
|
* the heap containing only the requested subsection. Like the
|
||||||
|
* substring functions included with most programming languages, the
|
||||||
|
* starting index is inclusive, and the ending index is exclusive.
|
||||||
|
*/
|
||||||
|
extern char * StrSubstr(const char *, size_t, size_t);
|
||||||
|
|
||||||
extern char *
|
/**
|
||||||
StrConcat(size_t,...);
|
* A varargs function that takes a number of null-terminated strings
|
||||||
|
* specified by the first argument, and returns a new string that
|
||||||
|
* contains their concatenation. It works similarly to
|
||||||
|
* .Xr strcat 3 ,
|
||||||
|
* but it takes care of allocating memory big enough to hold all the
|
||||||
|
* strings. Any string in the list may be NULL. If a NULL pointer is
|
||||||
|
* passed, it is treated like an empty string.
|
||||||
|
*/
|
||||||
|
extern char * StrConcat(size_t,...);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
StrBlank(const char *str);
|
* Return a boolean value indicating whether or not the null-terminated
|
||||||
|
* string consists only of blank characters, as determined by
|
||||||
|
* .Xr isblank 3 .
|
||||||
|
*/
|
||||||
|
extern int StrBlank(const char *str);
|
||||||
|
|
||||||
extern char *
|
/**
|
||||||
StrRandom(size_t);
|
* Generate a string of the specified length, containing random
|
||||||
|
* lowercase and uppercase letters.
|
||||||
|
*/
|
||||||
|
extern char * StrRandom(size_t);
|
||||||
|
|
||||||
#endif /* TELODENDRIA_STR_H */
|
#endif /* TELODENDRIA_STR_H */
|
||||||
|
|
|
@ -24,26 +24,121 @@
|
||||||
#ifndef TELODENDRIA_UIA_H
|
#ifndef TELODENDRIA_UIA_H
|
||||||
#define TELODENDRIA_UIA_H
|
#define TELODENDRIA_UIA_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm Uia
|
||||||
|
* @Nd User Interactive Authentication.
|
||||||
|
* @Dd April 28 2023
|
||||||
|
* @Xr User Db Cron
|
||||||
|
*
|
||||||
|
* .Nm
|
||||||
|
* takes care of all the logic for performing user interactive
|
||||||
|
* authentication as defined by the Matrix specification. API endpoints
|
||||||
|
* that require authentication via user interactive authentication
|
||||||
|
* build up flows and add any necessary parameters, and pass them all
|
||||||
|
* into
|
||||||
|
* .Fn UiaComplete .
|
||||||
|
* The goal is to make it easy for the numerous API endpoints that
|
||||||
|
* utilize this authentication mechanism to implement it.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <Array.h>
|
#include <Array.h>
|
||||||
#include <HashMap.h>
|
#include <HashMap.h>
|
||||||
#include <HttpServer.h>
|
#include <HttpServer.h>
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An opaque structure that represents a single stage, which consists
|
||||||
|
* of the type and a JSON object that contains implementation-specific
|
||||||
|
* parameters for completing the stage.
|
||||||
|
*/
|
||||||
typedef struct UiaStage UiaStage;
|
typedef struct UiaStage UiaStage;
|
||||||
|
|
||||||
extern UiaStage *
|
/**
|
||||||
UiaStageBuild(char *, HashMap *);
|
* Build a single stage with the type and a JSON object of parameters
|
||||||
|
* the client may require to complete it. Consult the Matrix
|
||||||
|
* specification for the valid types.
|
||||||
|
*/
|
||||||
|
extern UiaStage * UiaStageBuild(char *, HashMap *);
|
||||||
|
|
||||||
extern Array *
|
/**
|
||||||
UiaDummyFlow(void);
|
* Build a flow that consists only of a dummy stage. This is useful
|
||||||
|
* when an endpoint is required by the specification to use user
|
||||||
|
* interactive authentication, but doesn't want to actually require
|
||||||
|
* the user to do anything. Since the dummy flow is a fairly common
|
||||||
|
* flow, it seemed sensible to have a function for it. Other flows are
|
||||||
|
* built manually by the caller that that wishes to perform user
|
||||||
|
* interactive authentication.
|
||||||
|
*/
|
||||||
|
extern Array * UiaDummyFlow(void);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
UiaCleanup(MatrixHttpHandlerArgs *);
|
* This function should be called periodically to purge old sessions.
|
||||||
|
* Sessions are only valid for a few minutes after their last access.
|
||||||
|
* After that, they should be purged so that the database doesn't fill
|
||||||
|
* up with old session files. This function is specifically designed
|
||||||
|
* to be called via the Cron API.
|
||||||
|
*/
|
||||||
|
extern void UiaCleanup(MatrixHttpHandlerArgs *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate an auth object and maintain session state to track the
|
||||||
|
* progress of a client through user interactive authentication flows.
|
||||||
|
* The idea is that an API endpoint will not progress until user
|
||||||
|
* interactive authentication has succeeded.
|
||||||
|
* .Pp
|
||||||
|
* This function does the bulk of the work for user interactive
|
||||||
|
* authentication. It takes many parameters:
|
||||||
|
* .Bl -bullet -offset indent
|
||||||
|
* .It
|
||||||
|
* An array of arrays of stages. Stages should be created with
|
||||||
|
* .Fn UiaStageBuild
|
||||||
|
* and then put into an array to create a flow. Those flows should then
|
||||||
|
* be put into an array and passed as this parameter. It is important
|
||||||
|
* to note here that because of the loose typing of the Array API, it
|
||||||
|
* is very easy to make mistakes here; if you are implementing a new
|
||||||
|
* endpoint that requires user interactive authentication, it is best
|
||||||
|
* to refer to the source code of an existing endpoint to get a better
|
||||||
|
* idea of how it works.
|
||||||
|
* .It
|
||||||
|
* An HTTP server context. This is required to set the response headers
|
||||||
|
* in the even of an error.
|
||||||
|
* .It
|
||||||
|
* The database where user interactive authentication sessions are
|
||||||
|
* persisted.
|
||||||
|
* .It
|
||||||
|
* The JSON request body that contains the client's auth object, which
|
||||||
|
* will be read, parsed, and handled as appropriate.
|
||||||
|
* .It
|
||||||
|
* A pointer to a pointer where a JSON response can be placed if
|
||||||
|
* necessary. If this function encounters a client error, such as a
|
||||||
|
* failure to authenticate, or outstanding stages that have not yet
|
||||||
|
* been completed, it will place a JSON response here that is expected
|
||||||
|
* to be returned to the client. This response will include a
|
||||||
|
* description of all the flows, stages, and the stage parameters.
|
||||||
|
* .It
|
||||||
|
* A valid configuration structure, because a few values are read from
|
||||||
|
* the configuration during certain stages of authentication.
|
||||||
|
* .El
|
||||||
|
* .Pp
|
||||||
|
* This function returns an integer value less than zero if it
|
||||||
|
* experiences an internal failure, such as a failure to allocate
|
||||||
|
* memory memory, or a corrupted database. It returns 0 if the client
|
||||||
|
* has remaining stages to complete, including the current stage if
|
||||||
|
* that one did not complete successfully. In this case, this function
|
||||||
|
* will set the proper response headers and the passed response
|
||||||
|
* pointer, so the caller should immediately return the response to
|
||||||
|
* the client. This function returns 1 if and only if the client has
|
||||||
|
* successfully completed all stages. Only in this latter case shall
|
||||||
|
* the caller proceed with its logic.
|
||||||
|
*/
|
||||||
extern int
|
extern int
|
||||||
UiaComplete(Array *, HttpServerContext *, Db *, HashMap *, HashMap **, Config *);
|
UiaComplete(Array *, HttpServerContext *, Db *, HashMap *, HashMap **, Config *);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
UiaFlowsFree(Array *);
|
* Free an array of flows, as described above. Even though the caller
|
||||||
|
* constructs this array, it is convenient to free it in its
|
||||||
|
* entirety in a single function call.
|
||||||
|
*/
|
||||||
|
extern void UiaFlowsFree(Array *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,21 +24,50 @@
|
||||||
#ifndef TELODENDRIA_USER_H
|
#ifndef TELODENDRIA_USER_H
|
||||||
#define TELODENDRIA_USER_H
|
#define TELODENDRIA_USER_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm User
|
||||||
|
* @Nd Convenience functions for working with local users.
|
||||||
|
* @Dd April 28 2023
|
||||||
|
* @Xr Db
|
||||||
|
*
|
||||||
|
* The
|
||||||
|
* .Nm
|
||||||
|
* API provides a wrapper over the database and offers an easy way to
|
||||||
|
* manage local users. It supports all of the locking mechanisms that
|
||||||
|
* the database does, and provides features for authenticating local
|
||||||
|
* users, among many other tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <Db.h>
|
#include <Db.h>
|
||||||
|
|
||||||
#include <Json.h>
|
#include <Json.h>
|
||||||
|
|
||||||
#define USER_DEACTIVATE (1 << 0)
|
/**
|
||||||
#define USER_ISSUE_TOKENS (1 << 1)
|
* Many functions here operate on an opaque user structure.
|
||||||
#define USER_CONFIG (1 << 2)
|
*/
|
||||||
#define USER_GRANT_PRIVILEGES (1 << 3)
|
|
||||||
#define USER_PROC_CONTROL (1 << 4)
|
|
||||||
|
|
||||||
#define USER_NONE 0
|
|
||||||
#define USER_ALL ((1 << 5) - 1)
|
|
||||||
|
|
||||||
typedef struct User User;
|
typedef struct User User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local users can have privileges to access the administrator API.
|
||||||
|
* These are the individual privileges that Telodendria supports.
|
||||||
|
* Note that they are bit flags, so they can be bitwise OR-ed together
|
||||||
|
* to have multiple privileges.
|
||||||
|
*/
|
||||||
|
typedef enum UserPrivileges
|
||||||
|
{
|
||||||
|
USER_NONE = 0,
|
||||||
|
USER_DEACTIVATE = (1 << 0),
|
||||||
|
USER_ISSUE_TOKENS = (1 << 1),
|
||||||
|
USER_CONFIG = (1 << 2),
|
||||||
|
USER_GRANT_PRIVILEGES = (1 << 3),
|
||||||
|
USER_PROC_CONTROL = (1 << 4),
|
||||||
|
USER_ALL = ((1 << 5) - 1)
|
||||||
|
} UserPrivileges;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A description of an access token, which users use to authenticate
|
||||||
|
* with the client-server API.
|
||||||
|
*/
|
||||||
typedef struct UserAccessToken
|
typedef struct UserAccessToken
|
||||||
{
|
{
|
||||||
char *user;
|
char *user;
|
||||||
|
@ -47,95 +76,214 @@ typedef struct UserAccessToken
|
||||||
long lifetime;
|
long lifetime;
|
||||||
} UserAccessToken;
|
} UserAccessToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login information, which is in most cases returned to the user
|
||||||
|
* upon a successful login.
|
||||||
|
*/
|
||||||
typedef struct UserLoginInfo
|
typedef struct UserLoginInfo
|
||||||
{
|
{
|
||||||
UserAccessToken *accessToken;
|
UserAccessToken *accessToken;
|
||||||
char *refreshToken;
|
char *refreshToken;
|
||||||
} UserLoginInfo;
|
} UserLoginInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A description of a Matrix user ID.
|
||||||
|
*/
|
||||||
typedef struct UserId
|
typedef struct UserId
|
||||||
{
|
{
|
||||||
char *localpart;
|
char *localpart;
|
||||||
char *server;
|
char *server;
|
||||||
} UserId;
|
} UserId;
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UserValidate(char *, char *);
|
* Take a localpart and domain as separate parameters and validate them
|
||||||
|
* against the rules of the Matrix specification. The reasion the
|
||||||
|
* domain is required is because the spec imposes limitations on the
|
||||||
|
* length of the user ID, so the longer the domain name is, the shorter
|
||||||
|
* the local part is allowed to be. This function is used to ensure
|
||||||
|
* that client-provided Matrix IDs are valid on this server.
|
||||||
|
*/
|
||||||
|
extern int UserValidate(char *, char *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UserHistoricalValidate(char *, char *);
|
* This function behaves just like
|
||||||
|
* .Fn UserValidate ,
|
||||||
|
* except that it is a little more lenient in what is considers to be
|
||||||
|
* a valid Matrix ID. This is typically to validate users that exist
|
||||||
|
* on other servers, since some usernames may exist that are not fully
|
||||||
|
* spec compliant but remain in use since before the new restrictions
|
||||||
|
* were put in place.
|
||||||
|
*/
|
||||||
|
extern int UserHistoricalValidate(char *, char *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UserExists(Db *, char *);
|
* Determine whether the user identified by the specified localpart
|
||||||
|
* exists in the database.
|
||||||
|
*/
|
||||||
|
extern int UserExists(Db *, char *);
|
||||||
|
|
||||||
extern User *
|
/**
|
||||||
UserCreate(Db *, char *, char *);
|
* Create a new user with the specified localpart and password, in
|
||||||
|
* that order.
|
||||||
|
*/
|
||||||
|
extern User * UserCreate(Db *, char *, char *);
|
||||||
|
|
||||||
extern User *
|
/**
|
||||||
UserLock(Db *, char *);
|
* Take a localpart and obtain a database reference to the user
|
||||||
|
* identified by that localpart. This function behaves analogously
|
||||||
|
* to
|
||||||
|
* .Fn DbLock ,
|
||||||
|
* and in fact it uses it under the hood to ensure that the user can
|
||||||
|
* only be modified by the thread that has locked it.
|
||||||
|
*/
|
||||||
|
extern User * UserLock(Db *, char *);
|
||||||
|
|
||||||
extern User *
|
/**
|
||||||
UserAuthenticate(Db *, char *);
|
* Take an access token, figure out what user it belongs to, and then
|
||||||
|
* returns a reference to that user. This function should be used by
|
||||||
|
* most endpoints that require user authentication, since most
|
||||||
|
* endpoints are authenticated via access tokens.
|
||||||
|
*/
|
||||||
|
extern User * UserAuthenticate(Db *, char *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UserUnlock(User *);
|
* Return a user reference back to the database. This function uses
|
||||||
|
* .Fn DbUnlock
|
||||||
|
* under the hood.
|
||||||
|
*/
|
||||||
|
extern int UserUnlock(User *);
|
||||||
|
|
||||||
extern UserLoginInfo *
|
/**
|
||||||
UserLogin(User *, char *, char *, char *, int);
|
* Log in a user. This function takes the user's password, desired
|
||||||
|
* device ID and display name, and a boolean value indicating whether
|
||||||
|
* or not the client supports refresh tokens. This function logs the
|
||||||
|
* the user in and generates an access token to be returned to the
|
||||||
|
* client.
|
||||||
|
*/
|
||||||
|
extern UserLoginInfo * UserLogin(User *, char *, char *, char *, int);
|
||||||
|
|
||||||
extern char *
|
/**
|
||||||
UserGetName(User *);
|
* Get the localpart attached to a user object. This function may be
|
||||||
|
* useful in the few cases where the localpart is not known already.
|
||||||
|
*/
|
||||||
|
extern char * UserGetName(User *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UserCheckPassword(User *, char *);
|
* Take a password and verify it against a user object. Telodendria
|
||||||
|
* does not store passwords in plain text, so this function hashes the
|
||||||
|
* password and checks it against what is stored in the database.
|
||||||
|
*/
|
||||||
|
extern int UserCheckPassword(User *, char *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UserSetPassword(User *, char *);
|
* Reset the given user's password by hashing a plain text password and
|
||||||
|
* storing it in the database.
|
||||||
|
*/
|
||||||
|
extern int UserSetPassword(User *, char *);
|
||||||
|
|
||||||
extern int
|
extern int
|
||||||
UserDeactivate(User *);
|
UserDeactivate(User *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UserDeactivated(User *);
|
* Immediately deactivate the given user account such that it can no
|
||||||
|
* longer be used to log in, but the username is still reserved. This
|
||||||
|
* is to prevent future users from pretending to be a previous user
|
||||||
|
* of a given localpart. The user is logged out; all access tokens are
|
||||||
|
* invalidated.
|
||||||
|
*/
|
||||||
|
extern int UserDeactivated(User *);
|
||||||
|
|
||||||
extern HashMap *
|
/**
|
||||||
UserGetDevices(User *);
|
* Fetches the devices that belong to the user, in JSON format,
|
||||||
|
* identical to what's stored in the database. In fact, this JSON is
|
||||||
|
* still linked to the database, so it should not be freed with
|
||||||
|
* .Fn JsonFree .
|
||||||
|
*/
|
||||||
|
extern HashMap * UserGetDevices(User *);
|
||||||
|
|
||||||
extern UserAccessToken *
|
/**
|
||||||
UserAccessTokenGenerate(User *, char *, int);
|
* Generate a new access token for the given user. This is mainly
|
||||||
|
* used internally. It takes the device ID and a boolean value
|
||||||
|
* indicating whether or not the token should expire.
|
||||||
|
*/
|
||||||
|
extern UserAccessToken * UserAccessTokenGenerate(User *, char *, int);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UserAccessTokenSave(Db *, UserAccessToken *);
|
* Write the specified access token to the database, returning a
|
||||||
|
* boolean value indicating success.
|
||||||
|
*/
|
||||||
|
extern int UserAccessTokenSave(Db *, UserAccessToken *);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
UserAccessTokenFree(UserAccessToken *);
|
* Free the memory associated with the given access token.
|
||||||
|
*/
|
||||||
|
extern void UserAccessTokenFree(UserAccessToken *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UserDeleteToken(User *, char *);
|
* Delete a specific access token by name.
|
||||||
|
*/
|
||||||
|
extern int UserDeleteToken(User *, char *);
|
||||||
|
|
||||||
extern char *
|
/**
|
||||||
UserGetProfile(User *, char *);
|
* Get a string property from the user's profile given the specified
|
||||||
|
* key.
|
||||||
|
*/
|
||||||
|
extern char * UserGetProfile(User *, char *);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
UserSetProfile(User *, char *, char *);
|
* Set a string property on the user's profile. A key/value pair should
|
||||||
|
* be provided.
|
||||||
|
*/
|
||||||
|
extern void UserSetProfile(User *, char *, char *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UserDeleteTokens(User *, char *);
|
* Delete all of the access tokens that belong to the specified user,
|
||||||
|
* except for the one provided by name, unless NULL is provided for
|
||||||
|
* the name.
|
||||||
|
*/
|
||||||
|
extern int UserDeleteTokens(User *, char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current privileges of the user as a packed bit field. Use
|
||||||
|
* the flags defined in UserPrivileges to deterine what privileges a
|
||||||
|
* user has.
|
||||||
|
*/
|
||||||
extern int UserGetPrivileges(User *);
|
extern int UserGetPrivileges(User *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the privileges of the user.
|
||||||
|
*/
|
||||||
extern int UserSetPrivileges(User *, int);
|
extern int UserSetPrivileges(User *, int);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the JSON that represents the user privileges into a packed
|
||||||
|
* bit field for simple manipulation.
|
||||||
|
*/
|
||||||
extern int UserDecodePrivileges(JsonValue *);
|
extern int UserDecodePrivileges(JsonValue *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode the packed bit field that represents user privileges as a
|
||||||
|
* JSON value.
|
||||||
|
*/
|
||||||
extern JsonValue *UserEncodePrivileges(int);
|
extern JsonValue *UserEncodePrivileges(int);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a string privilege into its bit in the bit field. This is
|
||||||
|
* mainly intended to be used internally. At the time of writing, I
|
||||||
|
* don't recall exactly why it's in the public API.
|
||||||
|
*/
|
||||||
extern int UserDecodePrivilege(const char *);
|
extern int UserDecodePrivilege(const char *);
|
||||||
|
|
||||||
extern UserId *
|
/**
|
||||||
UserIdParse(char *, char *);
|
* Parse either a localpart or a fully qualified Matrix ID. If the
|
||||||
|
* first argument is a localpart, then the second argument is used as
|
||||||
|
* the server name.
|
||||||
|
*/
|
||||||
|
extern UserId * UserIdParse(char *, char *);
|
||||||
|
|
||||||
extern void
|
/**
|
||||||
UserIdFree(UserId *);
|
* Free the memory associated with the parsed Matrix ID.
|
||||||
|
*/
|
||||||
|
extern void UserIdFree(UserId *);
|
||||||
|
|
||||||
#endif /* TELODENDRIA_USER_H */
|
#endif /* TELODENDRIA_USER_H */
|
||||||
|
|
|
@ -25,31 +25,81 @@
|
||||||
#ifndef TELODENDRIA_UTIL_H
|
#ifndef TELODENDRIA_UTIL_H
|
||||||
#define TELODENDRIA_UTIL_H
|
#define TELODENDRIA_UTIL_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm Util
|
||||||
|
* @Nd Some misc. helper functions that don't need their own headers.
|
||||||
|
* @Dd February 15 2023
|
||||||
|
*
|
||||||
|
* This header holds a number of random functions related to strings,
|
||||||
|
* time, the filesystem, and other simple tasks that don't require a
|
||||||
|
* full separate API. For the most part, the functions here are
|
||||||
|
* entirely standalone, depending only on POSIX functions, however
|
||||||
|
* there are a few that depend explicitly on a few other APIs. Those
|
||||||
|
* are noted.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <Stream.h>
|
#include <Stream.h>
|
||||||
|
|
||||||
extern unsigned long
|
/**
|
||||||
UtilServerTs(void);
|
* Get the current timestamp in milliseconds since the Unix epoch. This
|
||||||
|
* uses
|
||||||
|
* .Xr gettimeofday 2
|
||||||
|
* and time_t, and converts it to a single number, which is then
|
||||||
|
* returned to the caller.
|
||||||
|
* .Pp
|
||||||
|
* A note on the 2038 problem: as long as sizeof(long) >= 8, that is,
|
||||||
|
* as long as the long data type is 64 bits or more, then everything
|
||||||
|
* should be fine. On most, if not, all, 64-bit systems, long is 64
|
||||||
|
* bits. I would expect Telodendria to break for 32 bit systems
|
||||||
|
* eventually, but we should have a ways to go before that happens.
|
||||||
|
* I didn't want to try to hack together some system to store larger
|
||||||
|
* numbers than the architecture supports. But we can always
|
||||||
|
* re-evaluate over the next few years.
|
||||||
|
*/
|
||||||
|
extern unsigned long UtilServerTs(void);
|
||||||
|
|
||||||
extern unsigned long
|
/**
|
||||||
UtilLastModified(char *);
|
* Use
|
||||||
|
* .Xr stat 2
|
||||||
|
* to get the last modified time of the given file, or zero if there
|
||||||
|
* was an error getting the last modified time of a file. This is
|
||||||
|
* primarily useful for caching file data.
|
||||||
|
*/
|
||||||
|
extern unsigned long UtilLastModified(char *);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UtilMkdir(const char *, const mode_t);
|
* This function behaves just like the system call
|
||||||
|
* .Xr mkdir 2 ,
|
||||||
|
* but it creates any intermediate directories as necessary, unlike
|
||||||
|
* .Xr mkdir 2 .
|
||||||
|
*/
|
||||||
|
extern int UtilMkdir(const char *, const mode_t);
|
||||||
|
|
||||||
extern int
|
/**
|
||||||
UtilSleepMillis(long);
|
* Sleep the calling thread for the given number of milliseconds.
|
||||||
|
* POSIX does not have a very friendly way to sleep, so this wraps
|
||||||
|
* .Xr nanosleep 2
|
||||||
|
* to make its usage much, much simpler.
|
||||||
|
*/
|
||||||
|
extern int UtilSleepMillis(long);
|
||||||
|
|
||||||
extern size_t
|
/**
|
||||||
UtilParseBytes(char *);
|
* This function works identically to the POSIX
|
||||||
|
* .Xr getdelim 3 ,
|
||||||
|
* except that it assumes pointers were allocated with the Memory API
|
||||||
|
* and it reads from a Stream instead of a file pointer.
|
||||||
|
*/
|
||||||
|
extern ssize_t UtilGetDelim(char **, size_t *, int, Stream *);
|
||||||
|
|
||||||
extern ssize_t
|
/**
|
||||||
UtilGetDelim(char **, size_t *, int, Stream *);
|
* This function is just a special case of
|
||||||
|
* .Fn UtilGetDelim
|
||||||
extern ssize_t
|
* that sets the delimiter to the newline character.
|
||||||
UtilGetLine(char **, size_t *, Stream *);
|
*/
|
||||||
|
extern ssize_t UtilGetLine(char **, size_t *, Stream *);
|
||||||
|
|
||||||
#endif /* TELODENDRIA_UTIL_H */
|
#endif /* TELODENDRIA_UTIL_H */
|
||||||
|
|
|
@ -32,7 +32,7 @@ if which makewhatis 2>&1 > /dev/null; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export PATH="$(pwd)/tools/bin:$(pwd)/build/tools:$PATH"
|
export PATH="$(pwd)/tools/bin:$(pwd)/build/tools:$PATH"
|
||||||
export MANPATH="$(pwd)/man:$MANPATH"
|
export MANPATH="$(pwd)/man:$(pwd)/build/man:$MANPATH"
|
||||||
|
|
||||||
if [ "$(uname)" = "OpenBSD" ]; then
|
if [ "$(uname)" = "OpenBSD" ]; then
|
||||||
# Other platforms use different MALLOC_OPTIONS
|
# Other platforms use different MALLOC_OPTIONS
|
||||||
|
|
Loading…
Reference in a new issue