diff --git a/Makefile b/Makefile index 06ee118..5bde52d 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ INCLUDES=src/include CC=cc CFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -g -ggdb -Wall -Werror LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -g -ggdb -AFLAGS=-C "$(ETC)/ayadoc/style.css" +AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)" BINARY=parsee # ============================ Compilation ================================= SRC_FILES:=$(shell find $(SOURCE) -name '*.c') @@ -52,3 +52,5 @@ ayadoc: utils $(AYA_FILES) $(AYAS)/%.html: $(INCLUDES)/%.h @mkdir -p $(shell dirname "$@") tools/out/aya $(AFLAGS) -i $< -o $@ + +# TODO: a install rule that reads prefix for software packagers? diff --git a/etc/ayadoc/style.css b/etc/ayadoc/style.css index 3bc27dd..af27ea7 100644 --- a/etc/ayadoc/style.css +++ b/etc/ayadoc/style.css @@ -27,7 +27,8 @@ a:link } -/* ---------- AYADOC-SPECIFIC STYLING ----------*/ +/* ---------- AYADOC-SPECIFIC STYLING ---------- + * Colors taken from Gruvbox. */ .aya-return { color: #fe8019; @@ -45,3 +46,7 @@ a:link { text-decoration: underline; } +.rets-span +{ + color: #b16286; +} diff --git a/src/include/AS.h b/src/include/AS.h index 56e7a8c..cc824e2 100644 --- a/src/include/AS.h +++ b/src/include/AS.h @@ -1,5 +1,11 @@ #ifndef PARSEE_AS_H #define PARSEE_AS_H +/*-* + * Functions used to communicate with a Matrix server and execute actions + * over HTTP(S)+JSON. This effectively serves as Parsee's Matrix SDK. + * TODO: Write my own RanSDK for KappaChat. + * -------- + * Writren-By: LDA */ #include #include @@ -7,9 +13,11 @@ #include #include -/* Verifies a request from the homeserver to match the - * hs_token. */ -extern HashMap * ASVerifyRequest(ParseeHttpArg *); +/** Verifies a request from the homeserver to match the {hs_token}. + * ---------------- + * Returns: A JSON error message[HEAP] | NULL on success + * Thrasher: JsonFree */ +extern HashMap * ASVerifyRequest(ParseeHttpArg *arg); /* Authenticates a request with the correct as_token. * It does not send the request, however. */ @@ -49,8 +57,19 @@ extern void ASRedact(const ParseeConfig *, char *room, char *user, char *e_id); /* Sets a state event with a specific type and body */ extern void ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *event); -/* Gets/Sets the content for a PL event */ +/** Gets the content for a PL event in the room pointed by {id} + * ------------------------ + * Returns: A valid JSON object[HEAP] | NULL + * Thrasher: JsonFree + * See-Also: ASSetPL */ extern HashMap *ASGetPL(const ParseeConfig *conf, char *id); + +/** Sets the content for a PL event in the room pointed by {id} if + * possible. + * ------------------------ + * Returns: NOTHING + * Modifies: the room pointed by {id} + * See-Also: ASGetPL */ extern void ASSetPL(const ParseeConfig *conf, char *id, HashMap *m); /* Creates a room, with a masquerade user as its creator. This function @@ -62,14 +81,14 @@ extern char * ASCreateDM(const ParseeConfig *c, char *by, char *with); /** Sets the user's global display{name} * -------- * Returns: NOTHING - * Modifies: [EXT:User status] + * Modifies: the users' status in Matrix * See-Also: ASGetName, ASSetStatus, ASSetAvatar */ extern void ASSetName(const ParseeConfig *c, char *user, char *name); /** Sets the {user}'s global avatar, as an {mxc} URI * -------- * Returns: NOTHING - * Modifies: [EXT:User status] + * Modifies: the users' status in Matrix * See-Also: ASGetName, ASSetStatus */ extern void ASSetAvatar(const ParseeConfig *c, char *user, char *mxc); @@ -81,18 +100,32 @@ typedef enum UserStatus { /** Sets the user's status and message, to be seen by other clients. * -------- * Returns: NOTHING - * Modifies: [EXT:User status] + * Modifies: the users' status in Matrix * See-Also: https://spec.matrix.org/v1.11/client-server-api/#put_matrixclientv3presenceuseridstatus, ASSetName, ASSetAvatar */ extern void ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, char *msg); -/* Returns the user's name in a room, or a copy of the MXID itself, to be - * Free'd. */ +/** Returns the user's name in a room, or a copy of the MXID itself, to be + * Free'd + * ------------- + * Returns: The user's name in the room[HEAP] | A copy of {user}[HEAP] + * Thrasher: Free + * Modifies: NOTHING */ extern char * ASGetName(const ParseeConfig *c, char *room, char *user); -/* Uploads data to Matrix to be used later */ +/** Uploads data to Matrix to be used later + * ---------------- + * Returns: A valid MXC URI[HEAP] | NULL + * Thrasher: Free + * Modifies: the internal Matrix homeserver's media repository + * See-Also: ASReupload */ extern char * ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime); -/* Reuploads a HTTP URL to Matrix, with an optional MIME type returned. */ +/** Reuploads a HTTP URL to Matrix, with an optional MIME type returned. + * ---------------- + * Returns: A valid MXC URI[HEAP] | NULL + * Thrasher: Free + * Modifies: the internal Matrix homeserver's media repository, {from}s server + * See-Also: ASUpload */ extern char * ASReupload(const ParseeConfig *c, char *from, char **mime); extern HashMap * ASGetUserConfig(const ParseeConfig *c, char *user, char *key); diff --git a/src/include/Command.h b/src/include/Command.h index 44734c4..5261db8 100644 --- a/src/include/Command.h +++ b/src/include/Command.h @@ -1,5 +1,9 @@ #ifndef PARSEE_COMMAND_H #define PARSEE_COMMAND_H +/*-* + * A Matrix command manager (with a JCL/dd-style format). + * ----------- + * Written-By: LDA */ #include diff --git a/src/include/Glob.h b/src/include/Glob.h index a2dfdd1..2824441 100644 --- a/src/include/Glob.h +++ b/src/include/Glob.h @@ -1,10 +1,17 @@ #ifndef PARSEE_GLOB_H #define PARSEE_GLOB_H +/*-* + * A simple Matrix globrule matcher. + * ----------------- + * Written-By: LDA */ #include -/* Verifies if a string matches a rule, as specified in - * https://spec.matrix.org/v1.11/appendices/#glob-style-matching. */ +/** Verifies if a string matches a rule, as specified in the Matrix + * specification on globrules. + * ----------------- + * Returns: true if it does match, otherwise false + * See-Also https://spec.matrix.org/v1.11/appendices/#glob-style-matching */ extern bool GlobMatches(char *rule, char *string); #endif diff --git a/src/include/Parsee.h b/src/include/Parsee.h index de7709b..38552d9 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -1,6 +1,11 @@ #ifndef PARSEE_PARSEE_H #define PARSEE_PARSEE_H +/*-*

Some fields used as Parsee-specific functions/structures.

+ *

TODO: Consider separating some declarations here...

+ * -------- + * Writren-By: LDA */ + #include #include #include @@ -107,7 +112,11 @@ extern char * ParseeEncodeMXID(char *); extern char * ParseeDecodeMXID(char *); -/* HTTP server handler for Parsee, takes in a config. */ +/** Callback function for the Parsee HTTP handler, to be used by + * the HTTP thread itself. + * -------- + * UB-If: not used as the HTTP callback function + * Returns: NOTHING */ extern void ParseeRequest(HttpServerContext *, void *); /* A pthread callback used for listening to a component */ @@ -219,38 +228,61 @@ extern void ParseePushHeadTable(char *room, char *id); extern char *ParseeLookupHead(char *room); extern void ParseeDestroyHeadTable(void); -/* Globally bans a Matrix user from ever interacting with Parsee, and bans - * them from bridged rooms where the bot has administrator. */ +/* Disables a user/room/MUC's ability to interact from Parsee, and attempts + * to ban them from rooms where Parsee has the ability to do so ("noflying"). + * --------------- + * Returns: NOTHING + * See-Also: ParseeManageBan + * Modifies: the database */ extern void ParseeGlobalBan(ParseeData *, char *user, char *reason); -/* Verifies if a user was globally banned. If so, then apply actions to the - * room ID */ +/* Verifies if a user was banned globally. If so (and if {room} is set), + * tries to ban the user from it. + * --------------- + * Returns: NOTHING + * See-Also: ParseeManageBan + * Modifies: the database */ extern bool ParseeManageBan(ParseeData *, char *user, char *room); /* Same as ParseeVerifyStanza, but DMs */ extern bool ParseeVerifyDMStanza(ParseeData *data, char *room_id, char *id); -/* Checks if any user is an admin */ +/** Checks if a Matrix/XMPP user is considered as "administrator" by Parsee. + * ---------------------- + * Returns: (whenever the user is an admin) + * Modifies: NOTHING */ extern bool ParseeIsAdmin(ParseeData *data, char *user); -/* Measures Parsee's overall uptime */ +/* Measures Parsee's overall uptime. + * ---------------- + * Returns: uptime since the call to Main in milliseconds. + * Modifies: NOTHING */ extern uint64_t ParseeUptime(void); -/** Turns a duration into a nice "X minutes, Y seconds" string +/** Turns a duration into a nice "X minutes, Y seconds" + * string. * --------- * Returns: A human-readable string showing the duration[LA:HEAP] * Modifies: NOTHING */ extern char * ParseeStringifyDate(uint64_t millis); -/* Generates the JID of the Parsee bridge user. */ +/** Generates the Jabber ID of the main Parsee user available. + * ---------------------- + * Returns: An XMPP JID[LA:HEAP] + * Thrasher: Free */ extern char * ParseeJID(ParseeData *data); + +/** Generates the MXID of the main Parsee user available. + * ---------------------- + * Returns: A Matrix ID[LA:HEAP] + * Thrasher: Free */ extern char * ParseeMXID(ParseeData *data); /** Prints an _fatal_ and _strange_, error message, then congratulates - * the user for it(an "achievement"). - * Use this for errors that have _no business_ happening, at all. - * NOTE to users: If you see this, _*OPEN AN ISSUE*_. + * the user for it(an "achievement").
+ * Use this for errors that have no business happening, at all. + * NOTE to users: If you see this, OPEN AN ISSUE. * --------------------------------------------------- * Returns: NOTHING | NORETURN */ extern void ParseeAchievement(const char *func, const char *msg, bool die); diff --git a/src/include/Routes.h b/src/include/Routes.h index 4b99234..0baf81b 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -1,6 +1,11 @@ #ifndef PARSEE_ROUTES_H #define PARSEE_ROUTES_H +/*-* + * The HTTP routes and Matrix bot endpoints for Parser + * ----------- + * Written-By: LDA */ + #include typedef struct ParseeHttpArg { ParseeData *data; diff --git a/src/include/XEP393.h b/src/include/XEP393.h index a820dea..19f8b38 100644 --- a/src/include/XEP393.h +++ b/src/include/XEP393.h @@ -1,6 +1,11 @@ #ifndef PARSEE_XEP393_H #define PARSEE_XEP393_H +/*-* + * A basic XEP-0393 parser and XHTML-iser. + * -------- + * Writren-By: LDA */ + #include typedef enum XEP393Type { diff --git a/src/include/XMPP.h b/src/include/XMPP.h index 7d6cf1e..4852a30 100644 --- a/src/include/XMPP.h +++ b/src/include/XMPP.h @@ -1,6 +1,11 @@ #ifndef PARSEE_XMPP_H #define PARSEE_XMPP_H +/*-* + * Functions used to communicate with an XMPP server over an JCP stream. + * -------- + * Writren-By: LDA */ + #include #include diff --git a/tools/aya.c b/tools/aya.c index 2f26392..64f9b8f 100644 --- a/tools/aya.c +++ b/tools/aya.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -88,6 +89,7 @@ ParseAyadoc(char *raw) } } + line_content = StrDuplicate(""); for (index_ptr = start; index_ptr < line_break; index_ptr++) { char char_buffer[2] = { *index_ptr, '\0' }; @@ -97,11 +99,6 @@ ParseAyadoc(char *raw) Free(temporary); } - if (!line_content) - { - break; - } - if (!strncmp(line_content, "---", 3)) { parsing_notes = true; @@ -140,18 +137,133 @@ line_end: } static void -GenerateReturns(Stream *out, AyadocComment *ayadoc, HeaderDeclaration decl, char *val) +HandleReturnValue(Stream *out, AyadocComment *aya, HeaderDeclaration d, char *field) { - if (StrEquals(val, "NOTHING")) + char *tag_del = NULL; + if (StrEquals(field, "NOTHING")) { - StreamPrintf(out, "Nothing."); + StreamPrintf(out, "nothing"); + return; + } + else if (StrEquals(field, "NORETURN")) + { + StreamPrintf(out, "doesn't return in the thread"); return; } - /* TODO: Split all arguments by the '|', and handle them automatically - * (with live-alongs, special capped params, ... */ - StreamPrintf(out, " %s", val); + if ((tag_del = strchr(field, '['))) + { + /* We're free to edit field, as it is in the heap */ + *tag_del = '\0'; + } + + /* Already write the field */ + StreamPrintf(out, " %s", field); + + if (tag_del) + { + /* Locate the last ]. */ + char *end_tags = strrchr(++tag_del, ']'); + if (!end_tags) + { + end_tags = tag_del + strlen(tag_del); + } + + *end_tags = '\0'; + if (StrEquals(tag_del, "LA:HEAP") || + StrEquals(tag_del, "HEAP")) + { + StreamPrintf(out, " (stored in the heap)"); + return; + } + else if (!strncmp(tag_del, "LA:", 3)) + { + tag_del += 3; + } + StreamPrintf(out, " (stored along %s)", tag_del); + } } +static void +HandleSeeValue(Stream *out, AyadocComment *aya, HeaderDeclaration d, char *field) +{ + Uri *uri = UriParse(field); + if (uri) + { + StreamPrintf(out, + "%s", + field, uri->host + ); + UriFree(uri); + return; + } + StreamPrintf(out, + "%s", + field, field + ); +} +#define SplitBy(sym, cb, del_str) \ + char *start_of_arg = val, *separator = NULL; \ + bool end_of_field = false; \ + \ + while (!end_of_field) \ + { \ + char *temporary, *field = NULL, *index; \ + char *last_char; \ + while (*start_of_arg && isspace(*start_of_arg)) \ + { \ + start_of_arg++; \ + } \ + if (!(separator = strchr(start_of_arg, sym))) \ + { \ + end_of_field = true; \ + separator = start_of_arg + strlen(start_of_arg); \ + } \ + if (start_of_arg >= separator) \ + { \ + break; \ + } \ + \ + /* Trim out spaces */ \ + last_char = separator - 1; \ + while (*last_char && isspace(*last_char)) \ + { \ + last_char--; \ + } \ + \ + field = StrDuplicate(""); \ + for (index = start_of_arg; index <= last_char; index++) \ + { \ + char buffer[2] = { *index, '\0' }; \ + \ + temporary = field; \ + field = StrConcat(2, field, buffer); \ + Free(temporary); \ + } \ + \ + /* Process the field */ \ + cb(out, ayadoc, decl, field); \ + \ + if (!end_of_field) \ + { \ + StreamPrintf(out, del_str); \ + } \ + \ + Free(field); \ + start_of_arg = separator + 1; \ + } + +static void +GenerateReturns(Stream *out, AyadocComment *ayadoc, HeaderDeclaration decl, char *val) +{ + SplitBy('|', HandleReturnValue, " or "); +} +static void +GenerateSee(Stream *out, AyadocComment *ayadoc, HeaderDeclaration decl, char *val) +{ + SplitBy(',', HandleSeeValue, ", "); +} +#undef SplitBy + static void GenerateHTML(Stream *out, AyadocComment *ayadoc, HeaderDeclaration decl) { @@ -225,6 +337,26 @@ GenerateHTML(Stream *out, AyadocComment *ayadoc, HeaderDeclaration decl) StreamFlush(out); continue; } + else if (StrEquals(attr, "See-Also")) + { + /* TODO: Be a little more advanced. */ + StreamPrintf(out, "
", decl.name); + StreamPrintf(out, "

See also

"); + GenerateSee(out, ayadoc, decl, value); + StreamPrintf(out, "
"); + StreamFlush(out); + continue; + } + else if (StrEquals(attr, "Thrasher")) + { + /* TODO: Be a little more advanced. */ + StreamPrintf(out, "

", decl.name); + StreamPrintf(out, "This function may be destroyed with "); + StreamPrintf(out, "%s", value, value); + StreamPrintf(out, "

"); + StreamFlush(out); + continue; + } } } @@ -241,10 +373,12 @@ Main(Array *args, HashMap *env) int flag; char *header = NULL, *xhtml = NULL, *css = NULL; + char *project = "Ayaya!"; Stream *input, *output; + bool help = false; ArgParseStateInit(&state); - while ((flag = ArgParse(&state, args, "i:o:C:")) != -1) + while ((flag = ArgParse(&state, args, "i:o:p:C:h")) != -1) { switch (flag) { @@ -257,13 +391,23 @@ Main(Array *args, HashMap *env) case 'C': css = state.optArg; break; + case 'p': + project = state.optArg; + break; + case 'h': + help = true; + break; } } - if (!header || !xhtml) + if (!header || !xhtml || help) { - Log(LOG_ERR, - "Usage: %s -i [C header file] -o [Generated Aya HTML]", + Log(help ? LOG_INFO : LOG_ERR, + "Usage: %s " + "-i [C header file] " + "-o [Generated Aya HTML] " + "-p {project name} " + "-C {CSS stylefile}", ArrayGet(args, 0) ); return EXIT_FAILURE; @@ -286,7 +430,7 @@ Main(Array *args, HashMap *env) StreamPrintf(output, ""); StreamPrintf(output, ""); - StreamPrintf(output, "

Ayaya!: %s

", header); + StreamPrintf(output, "

%s: %s

", project, header); StreamPrintf(output, "
"); /* TODO */ { @@ -295,6 +439,7 @@ Main(Array *args, HashMap *env) while (true) { HeaderDeclaration decl; + bool raw_put = false; HeaderParse(input, &expression); if (expression.type == HP_EOF) @@ -305,15 +450,29 @@ Main(Array *args, HashMap *env) switch (expression.type) { case HP_COMMENT: - if (strncmp(expression.data.text, "*", 1)) + if (strncmp(expression.data.text, "*", 1) && + strncmp(expression.data.text, "-*", 2)) { break; } + if (!strncmp(expression.data.text, "-*", 2)) + { + raw_put = true; + } if (comm) { FreeAyadoc(comm); } - comm = ParseAyadoc(expression.data.text); + comm = ParseAyadoc(expression.data.text + !!raw_put); + if (raw_put) + { + StreamPrintf(output, "

"); + StreamPrintf(output, comm->description); + StreamPrintf(output, "

"); + + FreeAyadoc(comm); + comm = NULL; + } break; case HP_DECLARATION: if (!comm)