Document some new headers.

This commit is contained in:
Jordan Bancino 2023-04-29 15:04:16 +00:00
parent b70c3f0bed
commit 2d8d5244c4
6 changed files with 422 additions and 71 deletions

View file

@ -22,10 +22,10 @@ Milestone: v0.3.0
[ ] Documentation
[x] Array
[ ] Io
[x] Io
[ ] Stream
[ ] Tls
[ ] HttpClient
[x] HttpClient
[ ] Uri
[ ] td
[ ] tt
@ -35,15 +35,15 @@ Milestone: v0.3.0
[x] Log
[ ] TelodendriaConfig -> Config
[x] HashMap
[ ] HttpRouter
[x] HttpRouter
[x] Html
[ ] Str
[ ] HeaderParser
[x] Str
[x] HeaderParser
[ ] hdoc
[x] telodendria-admin
[x] telodendria-setup
[x] telodendria-config
[~] Refactor dev pages so function description and
[x] Refactor dev pages so function description and
return value are in the same location.
[x] Debug memory leaks with caching

View file

@ -24,12 +24,32 @@
#ifndef TELODENDRIA_HEADERPARSER_H
#define TELODENDRIA_HEADERPARSER_H
/***
* @Nm HeaderParser
* @Nd Parse simple C header files.
* @Dd April 29 2023
*
* .Nm
* is an extremely simple parser that lacks most of the functionality
* one would expect from a C code parser. It simply maps a stream
* of tokens into a few categories, parsing major ``milestones'' in
* a header, without actually understanding any of the details.
* .Pp
* This exists because it is used to generate man pages from headers.
* See
* .Xr hdoc 1
* for example usage of this parser.
*/
#include <Stream.h>
#include <Array.h>
/* Here's a comment */
#define HEADER_EXPR_MAX 4096
/**
* Headers are parsed as expressions. These are the expressions that
* this parser recognizes.
*/
typedef enum HeaderExprType
{
HP_COMMENT,
@ -42,6 +62,9 @@ typedef enum HeaderExprType
HP_EOF
} HeaderExprType;
/**
* A representation of a function declaration.
*/
typedef struct HeaderDeclaration
{
char returnType[64];
@ -49,12 +72,26 @@ typedef struct HeaderDeclaration
Array *args;
} HeaderDeclaration;
/**
* A global variable declaration. The type must be of the same size
* as the function declaration's return type due to the way parsing
* them is implemented.
*/
typedef struct HeaderGlobal
{
char type[64];
char name[HEADER_EXPR_MAX - 64];
} HeaderGlobal;
/**
* A representation of a single header expression. Note that that state
* structure is entirely internally managed, so it should not be
* accessed or manipulated by functions outside the functions defined
* here.
* .Pp
* The type field should be used to determine which field in the data
* union is valid.
*/
typedef struct HeaderExpr
{
HeaderExprType type;
@ -77,7 +114,11 @@ typedef struct HeaderExpr
} state;
} HeaderExpr;
extern void
HeaderParse(Stream *, HeaderExpr *);
/**
* Parse the next expression into the given header expression structure.
* To parse an entire C header, this function should be called in a
* loop until the type of the expression is HP_EOF.
*/
extern void HeaderParse(Stream *, HeaderExpr *);
#endif /* TELODENDRIA_HEADERPARSER_H */

View file

@ -24,32 +24,81 @@
#ifndef TELODENDRIA_HTTPCLIENT_H
#define TELODENDRIA_HTTPCLIENT_H
/***
* @Nm HttpClient
* @Nd Extremely simple HTTP client.
* @Dd April 29 2023
* @Xr Http HttpServer Tls
*
* .Nm
* HttpClient
* builds on the HTTP API to provide a simple yet functional HTTP
* client. It aims at being easy to use and minimal, yet also
* efficient.
*/
#include <stdio.h>
#include <HashMap.h>
#include <Http.h>
/**
* A server response is represented by a client context. It is
* opaque, so the functions defined in this API should be used to
* fetch data from and manipulate it.
*/
typedef struct HttpClientContext HttpClientContext;
/**
* Make an HTTP request. This function takes the request method,
* any flags defined in the HTTP API, the port, hostname, and path,
* all in that order. It returns NULL if there was an error making
* the request. Otherwise it returns a client context. Note that this
* function does not actually send any data, it simply makes the
* connection. Use
* .Fn HttpRequestHeader
* to add headers to the request. Then, send headers with
* .Fn HttpRequestSendHeaders .
* Finally, the request body, if any, can be written to the output
* stream, and then the request can be fully sent using
* .Fn HttpRequestSend .
*/
extern HttpClientContext *
HttpRequest(HttpRequestMethod, int, unsigned short, char *, char *);
extern void
HttpRequestHeader(HttpClientContext *, char *, char *);
/**
* Set a request header to send to the server when making the
* request.
*/
extern void HttpRequestHeader(HttpClientContext *, char *, char *);
extern void
HttpRequestSendHeaders(HttpClientContext *);
/**
* Send the request headers to the server. This must be called before
* the request body can be written or a response body can be read.
*/
extern void HttpRequestSendHeaders(HttpClientContext *);
extern HttpStatus
HttpRequestSend(HttpClientContext *);
/**
* Flush the request stream to the server. This function should be
* called before the response body is read.
*/
extern HttpStatus HttpRequestSend(HttpClientContext *);
extern HashMap *
HttpResponseHeaders(HttpClientContext *);
/**
* Get the headers returned by the server.
*/
extern HashMap * HttpResponseHeaders(HttpClientContext *);
extern Stream *
HttpClientStream(HttpClientContext *);
/**
* Get the stream used to write the request body and read the
* response body.
*/
extern Stream * HttpClientStream(HttpClientContext *);
extern void
HttpClientContextFree(HttpClientContext *);
/**
* Free all memory associated with the client context. This closes the
* connection, if it was still open.
*/
extern void HttpClientContextFree(HttpClientContext *);
#endif /* TELODENDRIA_HTTPCLIENT_H */

View file

@ -24,22 +24,68 @@
#ifndef TELODENDRIA_HTTPROUTER_H
#define TELODENDRIA_HTTPROUTER_H
/***
* @Nm HttpRouter
* @Nd Simple HTTP request router with regular expression support.
* @Dd April 29 2023
* @Xr HttpServer Http
*
* .Nm
* provides a simple mechanism for assigning functions to an HTTP
* request path. It is a simple tree data structure that parses the
* registered request paths and maps functions onto each part of the
* path. Then, requests can be easily routed to their appropriate
* handler functions.
*/
#include <Array.h>
/**
* The router structure is opaque and thus managed entirely by the
* functions defined in this API.
*/
typedef struct HttpRouter HttpRouter;
/**
* A function written to handle an HTTP request takes an array
* consisting of the matched path parts in the order they appear in
* the path, and a pointer to caller-provided arguments, if any.
* It returns a pointer that the caller is assumed to know how to
* handle.
*/
typedef void *(HttpRouteFunc) (Array *, void *);
extern HttpRouter *
HttpRouterCreate(void);
/**
* Create a new empty routing tree.
*/
extern HttpRouter * HttpRouterCreate(void);
extern void
HttpRouterFree(HttpRouter *);
/**
* Free all the memory associated with the given routing tree.
*/
extern void HttpRouterFree(HttpRouter *);
extern int
HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *);
/**
* Register the specified route function to be executed upon requests
* for the specified HTTP path. The path is parsed by splitting at
* each path separator. Each part of the path is a regular expression
* that matches the entire path part. A regular expression cannot
* match more than one path part. This allows for paths like
* .Pa /some/path/(.*)/parts
* to work as one would expect.
*/
extern int HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *);
extern int
HttpRouterRoute(HttpRouter *, char *, void *, void **);
/**
* Route the specified request path using the specified routing
* tree. This function will parse the path and match it to the
* appropriate route handler function. The return value is a boolean
* value that indicates whether or not an appropriate route function
* was found. If an appropriate function was found, then the void
* pointer is passed to it as arguments that it is expected to know
* how to handle, and the pointer to a void pointer is where the
* route function's response will be placed.
*/
extern int HttpRouterRoute(HttpRouter *, char *, void *, void **);
#endif /* TELODENDRIA_HTTPROUTER_H */

View file

@ -24,6 +24,37 @@
#ifndef TELODENDRIA_IO_H
#define TELODENDRIA_IO_H
/***
* @Nm Io
* @Nd Source/sink-agnostic I/O for implementing custom streams.
* @Dd April 29 2023
* @Xr Stream Tls
*
* Many systems provide platform-specific means of implementing custom
* streams using file pointers. However, POSIX does not define a way
* of programmatically creating custom streams.
* .Nm
* therefore fills this gap in POSIX by mimicking all of the
* functionality of these platform-specific functions, but in pure
* POSIX C. It defines a number of callback funtions to be executed
* in place of the standard POSIX I/O functions, which are used to
* implement arbitrary streams that may not be to a file or socket.
* Additionally, streams can now be pipelined; the sink of one stream
* may be the source of another lower-level stream. Additionally, all
* streams, regardless of their source or sink, share the same API, so
* streams can be handled in a much more generic manner. This allows
* the HTTP client and server libraries to seemlessly support TLS and
* plain connections without having to handle each separately.
* .Pp
* .Nm
* was heavily inspired by GNU's
* .Fn fopencookie
* and BSD's
* .Fn funopen .
* It aims to combine the best of both of these functions into a single
* API that is intuitive and easy to use.
*/
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
@ -33,13 +64,61 @@
#define IO_BUFFER 4096
#endif
/**
* An opaque structure analogous to a POSIX file descriptor.
*/
typedef struct Io Io;
typedef ssize_t(IoReadFunc) (void *, void *, size_t);
typedef ssize_t(IoWriteFunc) (void *, void *, size_t);
typedef off_t(IoSeekFunc) (void *, off_t, int);
/**
* Read input from the source of a stream. This function should
* attempt to read the specified number of bytes of data from the
* given cookie into the given buffer. It should behave identically
* to the POSIX
* .Xr read 2
* system call, except instead of using an integer descriptor as the
* first parameter, a pointer to an implementation-defined cookie
* stores any information the function needs to read from the source.
*/
typedef ssize_t (IoReadFunc) (void *, void *, size_t);
/**
* Write output to a sink. This function should attempt to write the
* specified number of bytes of data from the given buffer into the
* stream described by the given cookie. It should behave identically
* to the POSIX
* .Xr write 2
* system call, except instead of using an integer descriptor as the
* first parameter, a pointer to an implementation-defined cookie
* stores any information the function needs to write to the sink.
*/
typedef ssize_t (IoWriteFunc) (void *, void *, size_t);
/**
* Repositions the offset of the stream described by the specified
* cookie. This function should behave identically to the POSIX
* .Xr lseek 2
* system call, except instead of using an integer descriptor as the
* first parameter, a pointer to an implementation-defined cookie
* stores any information the function needs to seek the stream.
*/
typedef off_t (IoSeekFunc) (void *, off_t, int);
/**
* Close the given stream, making future reads or writes result in
* undefined behavior. This function should also free all memory
* associated with the cookie. It should behave identically to the
* .Xr close 2
* system call, except instead of using an integer descriptor for the
* parameter, a pointer to an implementation-defined cookie stores any
* information the function needs to close the stream.
*/
typedef int (IoCloseFunc) (void *);
/**
* A simple mechanism for grouping together a set of stream functions,
* to be passed to
* .Fn IoCreate .
*/
typedef struct IoFunctions
{
IoReadFunc *read;
@ -48,37 +127,96 @@ typedef struct IoFunctions
IoCloseFunc *close;
} IoFunctions;
extern Io *
IoCreate(void *, IoFunctions);
/**
* Create a new stream using the specified cookie and the specified
* I/O functions.
*/
extern Io * IoCreate(void *, IoFunctions);
extern ssize_t
IoRead(Io *, void *, size_t);
/**
* Read the specified number of bytes from the specified stream into
* the specified buffer. This calls the stream's underlying IoReadFunc,
* which should behave identically to the POSIX
* .Xr read 2
* system call.
*/
extern ssize_t IoRead(Io *, void *, size_t);
extern ssize_t
IoWrite(Io *, void *, size_t);
/**
* Write the specified number of bytes from the specified stream into
* the specified buffer. This calls the stream's underlying
* IoWriteFunc, which should behave identically to the POSIX
* .Xr write 2
* system call.
*/
extern ssize_t IoWrite(Io *, void *, size_t);
extern off_t
IoSeek(Io *, off_t, int);
/**
* Seek the specified stream using the specified offset and whence
* value. This calls the stream's underlying IoSeekFunc, which should
* behave identically to the POSIX
* .Xr lseek 2
* system call.
*/
extern off_t IoSeek(Io *, off_t, int);
extern int
IoClose(Io *);
/**
* Close the specified stream. This calls the stream's underlying
* IoCloseFunc, which should behave identically to the POSIX
* .Xr close 2
* system call.
*/
extern int IoClose(Io *);
extern int
IoVprintf(Io *, const char *, va_list);
/**
* Print a formatted string to the given stream. This is a
* re-implementation of the standard library function
* .Xr vfprintf 3 ,
* and behaves identically.
*/
extern int IoVprintf(Io *, const char *, va_list);
extern int
IoPrintf(Io *, const char *,...);
/**
* Print a formatted string to the given stream. This is a
* re-implementation of the standard library function
* .Xr fprintf 3 ,
* and behaves identically.
*/
extern int IoPrintf(Io *, const char *,...);
extern ssize_t
IoCopy(Io *, Io *);
/**
* Read all the bytes from the first stream and write them to the
* second stream. Neither stream is closed upon the completion of this
* function. This can be used for quick and convenient buffered
* copying of data from one stream into another.
*/
extern ssize_t IoCopy(Io *, Io *);
extern Io *
IoFd(int);
/**
* Wrap a POSIX file descriptor to take advantage of this API. The
* common use case for this function is when a regular file descriptor
* needs to be accessed by an application that uses this API to also
* access non-POSIX streams.
*/
extern Io * IoFd(int);
extern Io *
IoOpen(const char *, int, mode_t);
/**
* Open or create a file for reading or writing. The specified file
* name is opened for reading or writing as specified by the given
* flags and mode. This function is a simple convenience wrapper around
* the POSIX
* .Xr open 2
* system call that passes the opened file descriptor into
* .Fn IoFd .
*/
extern Io * IoOpen(const char *, int, mode_t);
extern Io *
IoFile(FILE *);
/**
* Wrap a standard C file pointer to take advantage of this API. The
* common use case for this function is when a regular C file pointer
* needs to be accessed by an application that uses this API to also
* access custom streams.
*/
extern Io * IoFile(FILE *);
#endif /* TELODENDRIA_IO_H */

View file

@ -24,49 +24,126 @@
#ifndef TELODENDRIA_REGTOKEN_H
#define TELODENDRIA_REGTOKEN_H
/**
* @Nm RegToken
* @Nd Manage Matrix client registration tokens.
* @Dd April 29 2023
* @Xr User
*
* .Nm
* manages registration tokens, which are used to allow specific
* trusted people to register accounts on a homeserver without
* necessarily allowing entirely open registration. In Telodendria,
* registration tokens can also grant privileges to a user upon
* registration. This allows the initial server administrator account
* to be granted all privileges when it is created.
*/
#include <Db.h>
/**
* This structure describes a registration token that is in the
* database.
*/
typedef struct RegTokenInfo
{
Db *db;
DbRef *ref;
/*
* The token itself.
*/
char *name;
/*
* Who created this token. Note that this can be NULL if the
* token was created by Telodendria itself.
*/
char *owner;
/*
* How many times the token was used.
*/
int used;
/*
* How many uses are allowed.
*/
int uses;
/*
* Timestamp when this token was created.
*/
unsigned long created;
/*
* Timestamp when this token expires, or 0 if it does not
* expire.
*/
unsigned long expires;
int grants; /* privileges */
/*
* A bit field describing the privileges this token grants. See
* the User API documentation for the privileges supported.
*/
int grants;
} RegTokenInfo;
/**
* ``Use'' the specified registration token by increasing the used
* count by one and writing the changes to the database. This function
* only takes action if the token is allowed to be used. Check
* .Fn RegTokenValid
* to see if a token actually can be used.
*/
extern void RegTokenUse(RegTokenInfo *);
extern void
RegTokenUse(RegTokenInfo *);
/**
* Return a boolean value indicating whether or not the specified
* token exists in the database.
*/
extern int RegTokenExists(Db *, char *);
extern int
RegTokenExists(Db *, char *);
/**
* Delete the specified registration token from the database.
*/
extern int RegTokenDelete(RegTokenInfo *);
extern int
RegTokenDelete(RegTokenInfo *);
/**
* Retrieve information about the specified registration token from
* the database.
*/
extern RegTokenInfo * RegTokenGetInfo(Db *, char *);
/**
* Create a new registration with the given name, owner, expiration
* timestamp, number of uses, and privileges to grant, all specified
* in that order. Upon success, a registration token information
* structure will be returned. Otherwise, NULL will be returned.
*/
extern RegTokenInfo *
RegTokenGetInfo(Db *, char *);
RegTokenCreate(Db *, char *, char *, unsigned long, int, int);
extern RegTokenInfo *
RegTokenCreate(Db *, char *, char *, unsigned long, int, int);
/**
* Free the memory associated with the registration token. This should
* be called after
* .Fn RegTokenClose .
*/
extern void RegTokenFree(RegTokenInfo *);
extern void
RegTokenFree(RegTokenInfo *);
/**
* Return a boolean value indicating whether or not the specified token
* is valid. A registration token is only valid if it has not expired
* and still has at least one remaining use.
*/
extern int RegTokenValid(RegTokenInfo *);
extern int
RegTokenValid(RegTokenInfo *);
extern int
RegTokenClose(RegTokenInfo *);
/**
* Unlock the database reference associated with the given registration
* token. This should be called before
* .Fn RegTokenFree .
*/
extern int RegTokenClose(RegTokenInfo *);
#endif