From 2d8d5244c4178813f17e5a2beb18a70645d31c4a Mon Sep 17 00:00:00 2001 From: Jordan Bancino Date: Sat, 29 Apr 2023 15:04:16 +0000 Subject: [PATCH] Document some new headers. --- TODO.txt | 12 +-- src/include/HeaderParser.h | 47 +++++++++- src/include/HttpClient.h | 73 +++++++++++--- src/include/HttpRouter.h | 62 ++++++++++-- src/include/Io.h | 188 ++++++++++++++++++++++++++++++++----- src/include/RegToken.h | 111 ++++++++++++++++++---- 6 files changed, 422 insertions(+), 71 deletions(-) diff --git a/TODO.txt b/TODO.txt index d2d51e8..d8e93e7 100644 --- a/TODO.txt +++ b/TODO.txt @@ -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 diff --git a/src/include/HeaderParser.h b/src/include/HeaderParser.h index 6c3b02a..ae255aa 100644 --- a/src/include/HeaderParser.h +++ b/src/include/HeaderParser.h @@ -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 #include -/* 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 */ diff --git a/src/include/HttpClient.h b/src/include/HttpClient.h index f5329e8..5da1b8f 100644 --- a/src/include/HttpClient.h +++ b/src/include/HttpClient.h @@ -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 #include #include +/** + * 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 */ diff --git a/src/include/HttpRouter.h b/src/include/HttpRouter.h index 64bb1ca..bd8b74f 100644 --- a/src/include/HttpRouter.h +++ b/src/include/HttpRouter.h @@ -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 +/** + * 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 */ diff --git a/src/include/Io.h b/src/include/Io.h index 5c6421c..e722890 100644 --- a/src/include/Io.h +++ b/src/include/Io.h @@ -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 #include #include @@ -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 */ diff --git a/src/include/RegToken.h b/src/include/RegToken.h index b80e6b3..82f3293 100644 --- a/src/include/RegToken.h +++ b/src/include/RegToken.h @@ -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 +/** + * 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