diff options
author | June McEnroe <june@causal.agency> | 2020-02-28 00:06:46 -0500 |
---|---|---|
committer | June McEnroe <june@causal.agency> | 2020-02-28 00:13:42 -0500 |
commit | c0fafbc887a147ee77278a5bfd852e171aeb471c (patch) | |
tree | 79e75b6c48f3e8b22a1583a9ddafdb1cb18040bf | |
parent | Include <>/-/* around nicks in coloring (diff) | |
download | litterbox-c0fafbc887a147ee77278a5bfd852e171aeb471c.tar.gz litterbox-c0fafbc887a147ee77278a5bfd852e171aeb471c.zip |
Implement the causal.agency/consumer capability
-rw-r--r-- | database.h | 30 | ||||
-rw-r--r-- | litterbox.1 | 13 | ||||
-rw-r--r-- | litterbox.c | 39 |
3 files changed, 74 insertions, 8 deletions
diff --git a/database.h b/database.h index d9b2718..5893f77 100644 --- a/database.h +++ b/database.h @@ -31,7 +31,7 @@ #define DATABASE_PATH "litterbox/litterbox.sqlite" -enum { DatabaseVersion = 1 }; +enum { DatabaseVersion = 2 }; #define ENUM_TYPE \ X(Privmsg, "privmsg") \ @@ -172,9 +172,10 @@ static inline void dbBindNull(sqlite3_stmt *stmt, const char *param) { errx(EX_SOFTWARE, "sqlite3_bind_null: %s", sqlite3_errmsg(db)); } -static inline void dbBindInt(sqlite3_stmt *stmt, const char *param, int value) { - if (!sqlite3_bind_int(stmt, dbParam(stmt, param), value)) return; - errx(EX_SOFTWARE, "sqlite3_bind_int: %s", sqlite3_errmsg(db)); +static inline void +dbBindInt(sqlite3_stmt *stmt, const char *param, sqlite3_int64 value) { + if (!sqlite3_bind_int64(stmt, dbParam(stmt, param), value)) return; + errx(EX_SOFTWARE, "sqlite3_bind_int64: %s", sqlite3_errmsg(db)); } static inline void dbBindText5( @@ -301,7 +302,14 @@ static const char *InitSQL = SQL( ) SELECT 'delete', * FROM text WHERE event = old.event; END; - PRAGMA user_version = 1; + CREATE TABLE consumers ( + host STRING NOT NULL, + port INTEGER NOT NULL, + pos INTEGER NOT NULL, + UNIQUE (host, port) + ); + + PRAGMA user_version = 2; COMMIT TRANSACTION; ); @@ -328,6 +336,18 @@ static const char *MigrationSQL[] = { PRAGMA user_version = 1; COMMIT TRANSACTION; ), + + SQL( + BEGIN TRANSACTION; + CREATE TABLE consumers ( + host STRING NOT NULL, + port INTEGER NOT NULL, + pos INTEGER NOT NULL, + UNIQUE (host, port) + ); + PRAGMA user_version = 2; + COMMIT TRANSACTION; + ), }; static inline void dbMigrate(void) { diff --git a/litterbox.1 b/litterbox.1 index 6a6a9e7..198f289 100644 --- a/litterbox.1 +++ b/litterbox.1 @@ -1,4 +1,4 @@ -.Dd January 14, 2020 +.Dd February 28, 2020 .Dt LITTERBOX 1 .Os . @@ -323,6 +323,17 @@ daemon implements the following: .Re .El . +.Ss Extensions +The +.Nm +daemon can take advantage of the +.Sy causal.agency/consumer +and +.Sy causal.agency/passive +vendor-specific IRCv3 capabilities +implemented by +.Xr pounce 1 . +. .Sh AUTHORS .An June Bug Aq Mt june@causal.agency . diff --git a/litterbox.c b/litterbox.c index 0213b31..b8bc25d 100644 --- a/litterbox.c +++ b/litterbox.c @@ -33,6 +33,8 @@ int getopt_config( const struct option *longopts, int *longindex ); +static const char *host; +static const char *port = "6697"; static struct tls *client; static void clientWrite(const char *ptr, size_t len) { @@ -58,6 +60,7 @@ static void format(const char *format, ...) { enum { ParamCap = 15 }; struct Message { + size_t pos; char *time; char *nick; char *user; @@ -75,6 +78,9 @@ static struct Message parse(char *line) { char *tag = strsep(&tags, ";"); char *key = strsep(&tag, "="); if (!strcmp(key, "time")) msg.time = tag; + if (!strcmp(key, "causal.agency/pos")) { + msg.pos = strtoull(tag, NULL, 10); + } } } if (line[0] == ':') { @@ -614,6 +620,20 @@ static int compar(const void *cmd, const void *_handler) { return strcmp(cmd, handler->cmd); } +static void updateConsumer(size_t pos) { + static sqlite3_stmt *stmt; + const char *sql = SQL( + INSERT INTO consumers (host, port, pos) VALUES (:host, :port, :pos) + ON CONFLICT (host, port) DO + UPDATE SET pos = :pos WHERE host = :host AND port = :port; + ); + dbPersist(&stmt, sql); + dbBindText(stmt, ":host", host); + dbBindText(stmt, ":port", port); + dbBindInt(stmt, ":pos", pos); + dbRun(stmt); +} + static void handle(struct Message msg) { if (!msg.cmd) return; const struct Handler *handler = bsearch( @@ -623,6 +643,7 @@ static void handle(struct Message msg) { if (handler->transaction) { dbExec(SQL(BEGIN TRANSACTION;)); handler->fn(&msg); + if (msg.pos) updateConsumer(msg.pos); dbExec(SQL(COMMIT TRANSACTION;)); } else { handler->fn(&msg); @@ -650,8 +671,6 @@ int main(int argc, char *argv[]) { bool insecure = false; const char *cert = NULL; const char *priv = NULL; - const char *host = NULL; - const char *port = "6697"; const char *defaultNetwork = NULL; const char *nick = "litterbox"; @@ -764,10 +783,26 @@ int main(int argc, char *argv[]) { error = tls_connect(client, host, port); if (error) errx(EX_UNAVAILABLE, "tls_connect: %s", tls_error(client)); + size_t consumerPos = 0; + sqlite3_stmt *stmt = dbPrepare( + SQL(SELECT pos FROM consumers WHERE host = :host AND port = :port;) + ); + dbBindText(stmt, ":host", host); + dbBindText(stmt, ":port", port); + if (dbStep(stmt) == SQLITE_ROW) { + consumerPos = sqlite3_column_int64(stmt, 0); + } + sqlite3_finalize(stmt); + if (pass) format("PASS :%s\r\n", pass); if (cert) format("CAP REQ :sasl\r\n"); format("CAP REQ :server-time\r\n"); format("CAP REQ :causal.agency/passive\r\n"); + if (consumerPos) { + format("CAP REQ :causal.agency/consumer=%zu\r\n", consumerPos); + } else { + format("CAP REQ :causal.agency/consumer\r\n"); + } if (!cert) format("CAP END\r\n"); format("NICK :%s\r\nUSER %s 0 * :Litterbox\r\n", nick, user); |