From ffb8c54644486771f76cce5142eb7632b48bd637 Mon Sep 17 00:00:00 2001 From: Emil Date: Mon, 7 Aug 2023 08:04:15 -0600 Subject: [PATCH] Aggressive restructuring --- Makefile | 13 ++- build.sh | 30 ------ include/api.h | 12 --- include/config.mk.h | 1 + include/error.h | 7 -- include/free.h | 22 ----- include/help.h | 6 -- include/irc.h | 14 --- include/irccolors.h | 5 - include/parse.h | 32 ------- install.sh | 23 +++-- src/api.c | 126 +++++++++++++------------ src/irc.c | 177 +++++++++++++++++++++++------------ src/main.c | 115 ++++++++++++++++++++--- src/parse.c | 223 --------------------------------------------- src/{sql_stmt.c => stmt.c} | 2 + src/unity.c | 13 +-- src/unity.o | Bin 68912 -> 0 bytes uninstall.sh | 7 +- 19 files changed, 313 insertions(+), 515 deletions(-) delete mode 100755 build.sh delete mode 100644 include/api.h create mode 100644 include/config.mk.h delete mode 100644 include/free.h delete mode 100644 include/help.h delete mode 100644 include/irc.h delete mode 100644 include/parse.h delete mode 100644 src/parse.c rename src/{sql_stmt.c => stmt.c} (98%) delete mode 100644 src/unity.o diff --git a/Makefile b/Makefile index 8def37b..602a690 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,9 @@ LDLIBS:=-lsqlite3 LIB:=./lib/libircclient/src/libircclient.o SRC := api.c irc.c main.c parse.c unity.c -HDR := api.h error.h irc.h irccolors.h parse.h stmt.h +HDR := config.h api.h error.h irc.h irccolors.h parse.h stmt.h + +VPATH := src include ifeq (${DEBUG},1) CFLAGS += -Og -ggdb @@ -30,18 +32,21 @@ ifeq (${ENABLE_SSL},1) CPPFLAGS += -DIRC_SSL_SUPPORT endif -all: ${PROGN} +all: include/config.h ${PROGN} ${PROGN}: ${HDR} ${SRC} ${LINK.c} -pipe ${LIB} src/unity.c -o $@ ${LDLIBS} # do nothing but update them... -$(SRC) $(HDR): +${SRC} ${HDR}: submodules: git submodule update --init --recursive +include/config.h: config.mk.h + cp -f $< $@ + clean: -rm ${OBJ} -.PHONY: submodules clean +.PHONY: all submodules clean diff --git a/build.sh b/build.sh deleted file mode 100755 index f699171..0000000 --- a/build.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# Script handling unity builds and options. - -DIR=$(dirname $(readlink -f "$0")) -cd $DIR - -PROGN=${PROGN:-probotic} -PREFIX=${PREFIX:-$DIR} - -CC=${CC-cc} -CFLAGS='-std=c99 -Wall -Wextra -Wpedantic -Wno-unused-function' -CPPFLAGS="-I/usr/include/libircclient/ -Iinclude -D_GNU_SOURCE -DPROGN=\"$PROGN\"" -LDFLAGS='-lircclient -lsqlite3' - -mkdir -p $PREFIX && echo "Made directory: $PREFIX" - -# Bourne shell is evil -if [ ${DEBUG-0} -eq 1 ] -then - CFLAGS=`echo "${CFLAGS} -O0 -ggdb"` -else - CPPFLAGS="${CPPFLAGS} -DNDEBUG" - CFLAGS=`echo "${CFLAGS} -O2 -flto=auto -fomit-frame-pointer -s"` -fi - -[ ! -z ${SAN} ] && CFLAGS=`echo "$CFLAGS -fsanitize=$SAN"` - -echo "$CC $CFLAGS -pipe $DIR/src/unity.c -o $PREFIX/$PROGN $CPPFLAGS $LDFLAGS" -time $CC $CFLAGS -pipe $DIR/src/unity.c -o $PREFIX/$PROGN $CPPFLAGS $LDFLAGS -echo -e "\nStatus: $?" diff --git a/include/api.h b/include/api.h deleted file mode 100644 index 12edea0..0000000 --- a/include/api.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef API_H_ - -VARDECL char const * db; - -DECL int api_init(void); -DECL void api_rope(void); -DECL void rope(void); -DECL char * remind(char * who); -/* DECL int is_no_assignment(char const * const who); */ - -#define API_H_ -#endif diff --git a/include/config.mk.h b/include/config.mk.h new file mode 100644 index 0000000..ccfc09d --- /dev/null +++ b/include/config.mk.h @@ -0,0 +1 @@ +#define DBFILE "probotic_data.sqlite" diff --git a/include/error.h b/include/error.h index be2e428..d5d7783 100644 --- a/include/error.h +++ b/include/error.h @@ -1,7 +1,3 @@ -#ifndef ERROR_H_ - -#include - #define ERR(ret,msg) \ do { fputs(PROGN ": " msg "\n", stderr); return (ret); } while (0) @@ -17,6 +13,3 @@ #define DB_ERROR 100 #define IRC_ERROR 101 #define CREDS_ERROR 102 - -#define ERROR_H_ -#endif diff --git a/include/free.h b/include/free.h deleted file mode 100644 index dba7441..0000000 --- a/include/free.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef FREE_H_ - -#define FREE(obj) \ - do \ - { \ - free(obj); \ - (obj) = NULL; \ - } while (0) - -#define FULL_FREE(obj) \ - do \ - { \ - if ((obj)) \ - { \ - memset((obj), '\0', strlen((obj))); \ - FREE((obj)); \ - } \ - } while (0) - - -#define FREE_H_ -#endif diff --git a/include/help.h b/include/help.h deleted file mode 100644 index c62d093..0000000 --- a/include/help.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HELP_H_ - -VARDECL char const * help_msg; - -#define HELP_H_ -#endif diff --git a/include/irc.h b/include/irc.h deleted file mode 100644 index 7175220..0000000 --- a/include/irc.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef IRC_H_ - -#include - -#include "parse.h" - -VARDECL irc_session_t * session; -VARDECL irc_callbacks_t callbacks; -VARDECL char * current_username; - -DECL int init(void); - -#define IRC_H_ -#endif diff --git a/include/irccolors.h b/include/irccolors.h index f446a92..f72432b 100644 --- a/include/irccolors.h +++ b/include/irccolors.h @@ -1,5 +1,3 @@ -#ifndef IRCCOLOR_H_ - // Formatting macros #define IRC_BOLD "[B]" #define IRC_ITALIC "[I]" @@ -22,6 +20,3 @@ #define IRC_DARKGRAY "[COLOR=DARKGRAY]" #define IRC_LIGHTGRAY "[COLOR=LIGHTGRAY]" #define IRC_STOP "[/COLOR]" - -#define IRCCOLOR_H_ -#endif diff --git a/include/parse.h b/include/parse.h deleted file mode 100644 index a5b3ba5..0000000 --- a/include/parse.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef CREDS_PARSER_H - -typedef struct -{ - char * username; - char * password; - char * channel; - char * server; - int port; -} creds_t; - -VARDECL creds_t creds; -/* nickserv identify password */ -VARDECL char * ident_password = NULL; - -/* DECL char ** str_split(char const * s, char c); */ -/* DECL void split_clean(char ** split); */ -/* DECL int is_admin(char const * user); */ - -#ifndef NO_VULN_COMMANDS -DECL char * dump(void); -DECL char * raw(char const * const sql); -#endif /* !NO_VULN_COMMANDS */ -DECL char * remind(char * who); -DECL void creds_free(void); -DECL void parse_command(char const * const cmd); -DECL void purge_assignments(char const * const who); -DECL void random_assign(char const * const sql); -DECL void set_repo(char const * const who, char const * const link); - -#define CREDS_PARSER_H -#endif diff --git a/install.sh b/install.sh index 4cee13c..652c937 100644 --- a/install.sh +++ b/install.sh @@ -1,13 +1,20 @@ #!/bin/sh + +echo "./uninstall.sh will destroy to whatever TARGET is set to, be careful!" + +# VARIABLES AND PATH +TARGET=${TARGET-/opt/probotic} DIR=$(dirname $(readlink -f "$0")) cd $DIR -[ ! -e $DIR/probotic ] && make DEBUG=0 NVULN=1 || return 1 -# run bootstrapper -[ ! -e $DIR/bootstrap/ ] && $DIR/bootstrap/bootstrap.sh || return 1 -# generate directories and install -TARGET=${TARGET-/opt/probotic} -useradd probotic -r -s /sbin/nologin -d $TARGET + +# BUILD & BOOTSTRAP +make -C $DIR DEBUG=0 NVULN=1 || exit 1 +$DIR/bootstrap/bootstrap.sh || exit 1 + +# INSTALL mkdir -p $TARGET -install -g probotic -o probotic -m 744 \ - $DIR/bootstrap/probotic_data.sqlite probotic -v $TARGET +useradd probotic -r -s /sbin/nologin -d $TARGET +install -v -g probotic -o probotic -m 744 \ + $DIR/bootstrap/probotic_data.sqlite probotic \ + $TARGET chown probotic:probotic $TARGET -R diff --git a/src/api.c b/src/api.c index 1ac539d..5ecfa7d 100644 --- a/src/api.c +++ b/src/api.c @@ -1,6 +1,6 @@ -#define DBFILE "probotic_data.sqlite" +/* api.c - Database API */ -#define stmt_prepare(stmt) \ +#define stmt_prepare(stmt) \ sqlite3_prepare_v2(connection, stmt ## _template, -1, &stmt, NULL) VARDECL char const * db = DBFILE; @@ -9,25 +9,25 @@ VARDECL sqlite3 * connection = NULL; DECL void DBERR(const int l) { - if(l != SQLITE_OK && l != SQLITE_ROW && l != SQLITE_DONE) - { - fprintf(stderr, - "sqlite (%d): %s\n", - sqlite3_errcode(connection), sqlite3_errmsg(connection)); - exit(DB_ERROR); - } + if(l != SQLITE_OK && l != SQLITE_ROW && l != SQLITE_DONE) + { + fprintf(stderr, + "sqlite (%d): %s\n", + sqlite3_errcode(connection), sqlite3_errmsg(connection)); + exit(DB_ERROR); + } } DECL int api_init(void) { DBERR(sqlite3_open_v2(db, &connection, SQLITE_OPEN_READWRITE, NULL)); - // dont you fucking dare to remove this spacing + /* dont you fucking dare to remove this spacing */ DBERR(stmt_prepare(remind_stmt)); DBERR(stmt_prepare(set_repo_stmt)); DBERR(stmt_prepare(get_nth_id_stmt)); - DBERR(stmt_prepare(new_assignment_stmt)); - DBERR(stmt_prepare(purge_assignments_stmt)); + DBERR(stmt_prepare(new_assignment_stmt)); + DBERR(stmt_prepare(purge_assignments_stmt)); DBERR(stmt_prepare(is_no_assignment_stmt)); return 0; } @@ -44,21 +44,13 @@ api_rope(void) sqlite3_close(connection); } -DECL void -rope(void) -{ - if (session) - { irc_destroy_session(session); } - api_rope(); -} - DECL char * remind(char * who) { char * r; char * title; char * desc; - char * repo; + /* char * repo; */ DBERR(sqlite3_reset(remind_stmt)); DBERR(sqlite3_bind_text(remind_stmt, 1, who, -1, SQLITE_STATIC)); const int i = sqlite3_step(remind_stmt); @@ -69,20 +61,24 @@ remind(char * who) title = strdup(title); desc = (char *) sqlite3_column_text(remind_stmt, 1); if (desc) { desc = strdup(desc); } else { desc = ""; } - repo = (char *) sqlite3_column_text(remind_stmt, 3); - if (repo) { repo = strdup(repo); } else { repo = ""; } - if (-1 == asprintf(&r, - IRC_RED "%s: " IRC_YELLOW "%s" IRC_GREEN - " (@" IRC_BLUE "%s" IRC_GREEN ")" IRC_STOP, - title, desc, repo)) + /* repo = (char *) sqlite3_column_text(remind_stmt, 3); */ + /* if (repo) { repo = strdup(repo); } else { repo = ""; } */ + /* if (-1 == asprintf(&r, */ + /* IRC_RED "%s: " IRC_YELLOW "%s" IRC_GREEN */ + /* " (@" IRC_BLUE "%s" IRC_GREEN ")" IRC_STOP, */ + /* title, desc, repo)) */ + if (-1 == asprintf(&r, + IRC_RED "%s: " IRC_YELLOW "%s" IRC_GREEN, + title, desc)) { /* this will probably never happen. But it implies a memory failure */ - r = strdup(IRC_RED "No memory!" IRC_STOP); } - } - else - { - r = strdup(IRC_RED "No current assignment." IRC_STOP); - } - return r; + r = strdup(IRC_RED "No memory!" IRC_STOP); + } + else + { + r = strdup(IRC_RED "No current assignment." IRC_STOP); + } + } + return r; } DECL void @@ -129,14 +125,14 @@ rtos(void * data, *r = (char *)calloc(data_len, sizeof(char)); for(int i = 0; i < argc; i++){ - strcat(*r, "|"); - if(argv[i]){ - strcat(*r, argv[i]); - } - else - { - strcat(*r, "NULL"); - } + strcat(*r, "|"); + if(argv[i]){ + strcat(*r, argv[i]); + } + else + { + strcat(*r, "NULL"); + } } strcat(*r, "|\n"); @@ -164,7 +160,7 @@ raw(char const * const sql) if (errmsg){ free(r); - r = errmsg; + r = errmsg; } else { strcat(r, "\00"); } return r; } @@ -176,40 +172,39 @@ get_project_count_callback(void* data, int argc, char** argv, char** colname) { (void)argc; (void)colname; - int* count = (int*)data; - *count = atoi(argv[0]); - return 0; + int* count = (int*)data; + *count = atoi(argv[0]); + return 0; } DECL int -get_project_count() +get_project_count(void) { - int r = 0; - - char const * sql = "SELECT COUNT(*) FROM project;"; - DBERR(sqlite3_exec(connection, sql, get_project_count_callback, &r, NULL)); - - return r; + int r = 0; + char const * sql = "SELECT COUNT(*) FROM project;"; + DBERR(sqlite3_exec(connection, sql, get_project_count_callback, &r, NULL)); + return r; } DECL int get_nth_id(const int i) { - int r; - DBERR(sqlite3_reset(get_nth_id_stmt)); - DBERR(sqlite3_bind_int(get_nth_id_stmt, 1, i)); - DBERR(sqlite3_step(get_nth_id_stmt)); - r = sqlite3_column_int(get_nth_id_stmt, 0); - return r; + int r; + DBERR(sqlite3_reset(get_nth_id_stmt)); + DBERR(sqlite3_bind_int(get_nth_id_stmt, 1, i)); + DBERR(sqlite3_step(get_nth_id_stmt)); + r = sqlite3_column_int(get_nth_id_stmt, 0); + return r; } DECL void -new_assignment(char const * const who, const int project) +new_assignment(char const * const who, + int const project) { - DBERR(sqlite3_reset(new_assignment_stmt)); - DBERR(sqlite3_bind_text(new_assignment_stmt, 1, who, -1, SQLITE_STATIC)); - DBERR(sqlite3_bind_int(new_assignment_stmt, 2, project)); - DBERR(sqlite3_step(new_assignment_stmt)); + DBERR(sqlite3_reset(new_assignment_stmt)); + DBERR(sqlite3_bind_text(new_assignment_stmt, 1, who, -1, SQLITE_STATIC)); + DBERR(sqlite3_bind_int(new_assignment_stmt, 2, project)); + DBERR(sqlite3_step(new_assignment_stmt)); } DECL void @@ -233,9 +228,10 @@ purge_assignments(char const * const who) DECL int is_no_assignment(char const * const who) { + const int e; DBERR(sqlite3_reset(is_no_assignment_stmt)); DBERR(sqlite3_bind_text(is_no_assignment_stmt, 1, who, -1, SQLITE_STATIC)); - const int e = sqlite3_step(is_no_assignment_stmt); + e = sqlite3_step(is_no_assignment_stmt); DBERR(e); return (e == SQLITE_DONE); } diff --git a/src/irc.c b/src/irc.c index 42e8362..537b5e6 100644 --- a/src/irc.c +++ b/src/irc.c @@ -1,18 +1,48 @@ -/* irc.c - IRC interface +/* irc.c - IRC interface */ + +#define FULL_FREE(obj) \ + do \ + { \ + if ((obj)) \ + { \ + memset((obj), '\0', strlen((obj))); \ + free((obj)); \ + (obj) = NULL; \ + } \ + } while (0) - Probotic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License version 3 only as - published by the Free Software Foundation. +#define PREFIX_COMMAND_CHAR '!' - Probotic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License version 3 for more details. +#define IRCMSG(msg) irc_cmd_msg(session, creds.channel, msg) - You should have received a copy of the GNU General Public License - version 3 along with Probotic. +typedef struct +{ + char * username; + char * password; + char * channel; + char * server; + int port; +} creds_t; + +VARDECL creds_t creds = +{ + .username = NULL, + .password = NULL, + .channel = NULL, + .server = NULL, /* localhost? */ +#ifndef IRC_SSL_SUPPORT + .port = 6667 +#else + .port = 6669 +#endif /* !IRC_SSL_SUPPORT */ +}; + +VARDECL char * ident_password = NULL; -*/ +VARDECL irc_session_t * session; +VARDECL irc_callbacks_t callbacks; + +VARDECL char * current_username = NULL; VARDECL char const * help_msg = /* IRC_GREEN "!help " IRC_STOP " : This message\n" */ @@ -24,15 +54,7 @@ IRC_GREEN "!reroll " IRC_STOP " : Rerolls assignment\n" IRC_GREEN "!request " IRC_STOP " : Request personal project\n" IRC_GREEN "!remind " IRC_STOP " : Prints your assignment\n"; -#define PREFIX_COMMAND_CHAR '!' -#define PREFIX_CHANNEL_COMMAND_CHAR '%' - -VARDECL irc_session_t * session; -VARDECL irc_callbacks_t callbacks; - -VARDECL char * current_username = NULL; - -#define IRCMSG(msg) irc_cmd_msg(session, creds.channel, msg) +DECL void parse_command(char const * cmd); DECL char * get_username(const char * origin) @@ -127,15 +149,8 @@ event_channel(irc_session_t * lsession, (void) message; (void) count; /* parses the command */ - switch(*message) - { - case PREFIX_CHANNEL_COMMAND_CHAR: - current_username = strdup(creds.channel); - break; - case PREFIX_COMMAND_CHAR: - current_username = get_username(origin); - break; - } + if (*message == PREFIX_COMMAND_CHAR) + { current_username = get_username(origin); } if (!current_username || message[1] == '\0') { return; } @@ -144,44 +159,88 @@ event_channel(irc_session_t * lsession, current_username = NULL; } +/* 'abc' SINGLE + 'def ' SINGLE + 'ghi jkl' MULTI */ + DECL int -init(void) +has_arg(char const * cmd) { - srand(time(NULL)); - if(api_init()) - { ERR(DB_ERROR, "Error initializing database."); } - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.event_connect = event_connect; - callbacks.event_channel = event_channel; - session = irc_create_session(&callbacks); - if (!session) + while (isalnum(*cmd)) { - ERRMSG("Error creating IRC session"); - goto fail; + if (*cmd == '\0') + { break; } + ++cmd; } - atexit(rope); - assert(creds.username != NULL); - assert(creds.server != NULL); - if (irc_connect(session, - creds.server, creds.port, creds.password, - creds.username, creds.username, creds.username)) + while (*cmd != '\0') { - fprintf(stderr, "IRC ERROR: %s\n", irc_strerror(irc_errno(session))); - exit(1); + if (!isspace(*(++cmd))) + { return 1; } } - FULL_FREE(creds.password); return 0; -fail: - FULL_FREE(creds.password); - return 1; } -DECL int -loop(void) +DECL void +parse_command(char const * cmd) { - /* We should figure out how the failure happens so we can tell the user that. */ - if (irc_run(session) != 0) - { ERR(1, "Error running IRC session\nPossible issue: bad URL," - " no network connection, bad port, refused connection."); } - return 0; + size_t i = 0; + char* msgswp = NULL; + /* size_t len = strlen(cmd); */ + + if ((i += has_arg(cmd))) + { + /* NO ARGUMENTS */ + if (strcmp(cmd, "remind") == 0) + { + msgswp = remind(current_username); + ircmsg(creds.channel, "%s: %s", current_username, msgswp); + } + else if (strcmp(cmd, "help") == 0) + { ircmsg(creds.channel, help_msg); } + else if (strcmp(cmd, "magic") == 0) + { ircmsg(creds.channel, "%s: " IRC_YELLOW "%d" IRC_STOP, current_username, (rand() % 100) + 1); } +#ifndef NO_VULN_COMMANDS + else if (strcmp(cmd, "dump") == 0) + { + ircmsg(creds.channel, "%s: All projects:", current_username); + msgswp = dump(); + ircmsg(creds.channel, msgswp); + } +#endif /* !NO_VULN_COMMANDS */ + else if (strcmp(cmd, "reroll") == 0) + { + purge_assignments(current_username); + random_assign(current_username); + ircmsg(creds.channel, "%s: %s", current_username, remind(current_username)); + } + } + else /* HAS ARGUMENTS */ + { + char const * const arg = cmd + i + 1; +#ifndef NO_VULN_COMMANDS + if (strncmp(cmd, "raw", i) == 0) + { + /* ircmsg(creds.channel, "%s: Executing SQL `%s'.", current_username, arg); */ + msgswp = raw(arg); + ircmsg(creds.channel, msgswp); + } else +#endif /* !NO_VULN_COMMANDS */ + if (strncmp(cmd, "repo", i) == 0) + { + /* ircmsg(creds.channel, "%s: Setting project repository...", current_username); */ + set_repo(creds.channel, arg); + msgswp = remind(creds.channel); + ircmsg(creds.channel, "%s: %s", current_username, msgswp); + } + } + free(msgswp); +} + +DECL void +creds_free(void) +{ + FULL_FREE(creds.username); + FULL_FREE(creds.password); + FULL_FREE(creds.channel); + FULL_FREE(creds.server); } diff --git a/src/main.c b/src/main.c index 4c6f44e..5c7f870 100644 --- a/src/main.c +++ b/src/main.c @@ -1,20 +1,105 @@ -/* main.c +/* main.c */ - Probotic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License version 3 only as - published by the Free Software Foundation. +#define VERSION_STRING "1" + +/* Parses the format username[:password]@server[:port] */ - Probotic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License version 3 for more details. +#define GENCOPY(dst,src,l) \ + do \ + { \ + dst = malloc(sep + 1); \ + if (dst) \ + { \ + strncpy(dst,src,l); \ + dst[len] = '\0'; \ + } \ + else \ + { return 1; } \ + } while (0) - You should have received a copy of the GNU General Public License - version 3 along with Probotic. +DECL int +parse_url(char const * url) +{ + size_t len = strlen(url); + size_t sep = 0, ls = 0, rs; + while (++sep < len && url[sep] != '@'); + while (++ls < len && url[ls] != ':') + { + if (ls >= sep) + { ls = 0; break; } + } + rs = sep; + while (++rs < len && url[rs] != ':'); + if (rs == len) + { rs = 0; } + GENCOPY(creds.username, url, ls ? ls : sep); + if (ls) + { + GENCOPY(creds.password, url + ls + 1, sep - ls - 1); + } + if (rs) + { + GENCOPY(creds.server, url + sep + 1, rs - sep - 1); + creds.port = atoi(url + rs + 1); + } + else + { + GENCOPY(creds.server, url + sep + 1, len - sep - 1); + } + return 0; +} -*/ +#undef GENCOPY -#define VERSION_STRING "1" +DECL void +rope(void) +{ + if (session) + { irc_destroy_session(session); } + api_rope(); +} + +DECL int +init(void) +{ + srand(time(NULL)); + if(api_init()) + { ERR(DB_ERROR, "Error initializing database."); } + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.event_connect = event_connect; + callbacks.event_channel = event_channel; + session = irc_create_session(&callbacks); + if (!session) + { + ERRMSG("Error creating IRC session"); + goto fail; + } + atexit(rope); + assert(creds.username != NULL); + assert(creds.server != NULL); + if (irc_connect(session, + creds.server, creds.port, creds.password, + creds.username, creds.username, creds.username)) + { + fprintf(stderr, "IRC ERROR: %s\n", irc_strerror(irc_errno(session))); + exit(1); + } + FULL_FREE(creds.password); + return 0; +fail: + FULL_FREE(creds.password); + return 1; +} + +DECL int +loop(void) +{ + /* We should figure out how the failure happens so we can tell the user that. */ + if (irc_run(session) != 0) + { ERR(1, "Error running IRC session\nPossible issue: bad URL," + " no network connection, bad port, refused connection."); } + return 0; +} DECL void help(void) @@ -24,6 +109,8 @@ help(void) "-url URL - Sets the target URL\n" "-db DBFILE - Sets the database file (default: probotic_data.sqlite)\n" "-identify PASSWORD - Identifies against NickServ\n" + "-version - Prints Version information\n" + "-help - Prints this information\n" "\nUse format username[:password]@server[:port], port defaults to 6667.\n"); } @@ -54,7 +141,7 @@ main (int argc, else if (strcmp(arg, "help") == 0) { goto help; } if (argc < 2) - { goto nop; } + { goto help; } if (strcmp(arg, "db") == 0) { db = argv[1]; } else if (strcmp(arg, "url") == 0) @@ -70,7 +157,7 @@ main (int argc, ++argv; --argc; } else - { nop: ERRFMT(1, "Oprand without option '%s'", arg); } + { goto help; } } } atexit(creds_free); diff --git a/src/parse.c b/src/parse.c deleted file mode 100644 index dcb9816..0000000 --- a/src/parse.c +++ /dev/null @@ -1,223 +0,0 @@ -/* parse.c - - Probotic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License version 3 only as - published by the Free Software Foundation. - - Probotic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License version 3 for more details. - - You should have received a copy of the GNU General Public License - version 3 along with Probotic. - -*/ - -#define PARAMS_COUNT 6 - -VARDECL creds_t creds = -{ - .username = NULL, - .password = NULL, - .channel = NULL, - .server = NULL, /* localhost? */ -#ifndef IRC_SSL_SUPPORT - .port = 6667 -#else - .port = 6669 -#endif /* IRC_SSL_SUPPORT */ -}; - - - -#if 0 - -DECL char ** -str_split(char const * s, char c) -{ - char ** ret = NULL; - size_t i = 0; - - size_t current_token_i = 0; - - size_t token_start_i = 0; - size_t tokens_q = 0; - - /* count tokens */ - for (i = 1; s[i]; ++i) - { - /* end of a token*/ - if (s[i] == c && s[i - 1] != c) - { ++tokens_q; } - } - ++tokens_q; - - ret = (char **)calloc(tokens_q + 1, sizeof(char *)); - if (!ret) - { return ret; } - - for (i = 1; s[i]; ++i) - { - - if ((s[i + 1] == c || !s[i + 1]) && s[i] != c) - { - /* end of a token*/ - ret[current_token_i] = strndup(s + token_start_i, i - token_start_i + 1); - if (!ret[current_token_i]) - { - split_clean(ret); - return NULL; - } - ++current_token_i; - } - else if (s[i] != c && s[i - 1] == c) - { - /* start of a token */ - token_start_i = i; - } - } - - /* Signal that the split array is ended (for iteration purposes) */ - ret[current_token_i + 1] = NULL; - - return ret; -} - -DECL void -split_clean(char ** split) -{ - while (*split) - { - free(*split); - } - free(split); -} - -#endif /* 0 */ - -DECL void -parse_command(char const * cmd) -{ - size_t i = 0; - char* msgswp = NULL; - /* size_t len = strlen(cmd); */ - /* TODO does not handle commands with leading space, - use custom implemented to-spec isspace implementation */ - while (cmd[i] != '\0' && - cmd[i] != ' ') - { ++i; } - if (cmd[i] == '\0') - { - /* no arguments */ -#ifndef NO_VULN_COMMANDS - if (strcmp(cmd, "kill") == 0) - { exit(1); } -#endif /* !NO_VULN_COMMANDS */ - if (strcmp(cmd, "remind") == 0) - { - msgswp = remind(current_username); - ircmsg(creds.channel, "%s: %s", current_username, msgswp); - } - else if (strcmp(cmd, "help") == 0) - { ircmsg(creds.channel, help_msg); } - else if (strcmp(cmd, "magic") == 0) - { ircmsg(creds.channel, "%s: " IRC_YELLOW "%d" IRC_STOP, current_username, (rand() % 100) + 1); } - else if (strcmp(cmd, "dump") == 0) - { -#ifndef NO_VULN_COMMANDS - ircmsg(creds.channel, "%s: All projects:", current_username); - msgswp = dump(); - ircmsg(creds.channel, msgswp); -#else - ircmsg(creds.channel, "%s: dump disabled", current_username); -#endif /* !NO_VULN_COMMANDS */ - } - else if (strcmp(cmd, "reroll") == 0) - { - /* ircmsg(creds.channel, "%s: Rerolling...", current_username); */ - purge_assignments(current_username); - random_assign(current_username); - ircmsg(creds.channel, "%s: %s", current_username, remind(current_username)); - } - } - else - { - /* some arguments */ - char const * const arg = cmd + i + 1; - if (strncmp(cmd, "raw", i) == 0) - { -#ifndef NO_VULN_COMMANDS - /* ircmsg(creds.channel, "%s: Executing SQL `%s'.", current_username, arg); */ - msgswp = raw(arg); - ircmsg(creds.channel, msgswp); -#else - ircmsg(creds.channel, "%s: raw disabled", current_username); -#endif /* !NO_VULN_COMMANDS */ - } - else if (strncmp(cmd, "repo", i) == 0) - { - /* ircmsg(creds.channel, "%s: Setting project repository...", current_username); */ - set_repo(creds.channel, arg); - msgswp = remind(creds.channel); - ircmsg(creds.channel, "%s: %s", current_username, msgswp); - } - } - free(msgswp); -} - -/* Parses the format username[:password]@server[:port] */ - -#define GENCOPY(dst,src,l) \ - do \ - { \ - dst = malloc(sep + 1); \ - if (dst) \ - { \ - strncpy(dst,src,l); \ - dst[len] = '\0'; \ - } \ - else \ - { return 1; } \ - } while (0) - -DECL int -parse_url(char const * url) -{ - size_t len = strlen(url); - size_t sep = 0, ls = 0, rs; - while (++sep < len && url[sep] != '@'); - while (++ls < len && url[ls] != ':') - { - if (ls >= sep) - { ls = 0; break; } - } - rs = sep; - while (++rs < len && url[rs] != ':'); - if (rs == len) - { rs = 0; } - GENCOPY(creds.username, url, ls ? ls : sep); - if (ls) - { - GENCOPY(creds.password, url + ls + 1, sep - ls - 1); - } - if (rs) - { - GENCOPY(creds.server, url + sep + 1, rs - sep - 1); - creds.port = atoi(url + rs + 1); - } - else - { - GENCOPY(creds.server, url + sep + 1, len - sep - 1); - } - return 0; -} - -DECL void -creds_free(void) -{ - FULL_FREE(creds.username); - FULL_FREE(creds.password); - FULL_FREE(creds.channel); - FULL_FREE(creds.server); -} diff --git a/src/sql_stmt.c b/src/stmt.c similarity index 98% rename from src/sql_stmt.c rename to src/stmt.c index fa4a293..899b205 100644 --- a/src/sql_stmt.c +++ b/src/stmt.c @@ -1,3 +1,5 @@ +/* stmt.c */ + VARDECL sqlite3_stmt * remind_stmt; VARDECL char const remind_stmt_template[] = "SELECT " diff --git a/src/unity.c b/src/unity.c index 048d8fe..5fc0490 100644 --- a/src/unity.c +++ b/src/unity.c @@ -7,19 +7,16 @@ #include #include #include +#include +#include #include -#include "api.h" #include "error.h" -#include "free.h" -#include "help.h" -#include "irc.h" #include "irccolors.h" -#include "parse.h" +#include "config.h" -#include "sql_stmt.c" -#include "irc.c" -#include "parse.c" +#include "stmt.c" #include "api.c" +#include "irc.c" #include "main.c" diff --git a/src/unity.o b/src/unity.o deleted file mode 100644 index 884780a1c8bcc4ac079969fb89b3d1e1b703bcf4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68912 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 E2O1vON&o-= diff --git a/uninstall.sh b/uninstall.sh index ac13841..abf88f0 100644 --- a/uninstall.sh +++ b/uninstall.sh @@ -1,7 +1,2 @@ #!/bin/sh -# doubles as a cleanup script -DIR=$(dirname $(readlink -f "$0")) -cd $DIR -TARGET=${TARGET-/opt/probotic} -userdel probotic -rm -rf $TARGET +userdel -rf probotic