about summary refs log tree commit diff
path: root/bounce.h
diff options
context:
space:
mode:
Diffstat (limited to 'bounce.h')
-rw-r--r--bounce.h123
1 files changed, 100 insertions, 23 deletions
diff --git a/bounce.h b/bounce.h
index a5dc836..a7bad16 100644
--- a/bounce.h
+++ b/bounce.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2019  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -12,33 +12,59 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with OpenSSL (or a modified version of that library),
+ * containing parts covered by the terms of the OpenSSL License and the
+ * original SSLeay license, the licensors of this Program grant you
+ * additional permission to convey the resulting work. Corresponding
+ * Source for a non-source form of such a combination shall include the
+ * source code for the parts of OpenSSL used as well as that of the
+ * covered work.
  */
 
+#include <err.h>
+#include <limits.h>
+#include <stdarg.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/time.h>
+#include <sysexits.h>
 #include <tls.h>
 
-#include "compat.h"
-
-#ifndef CERTBOT_PATH
-#define CERTBOT_PATH "/usr/local/etc/letsencrypt"
-#endif
-
 #ifndef OPENSSL_BIN
 #define OPENSSL_BIN "openssl"
 #endif
 
 #define SOURCE_URL "https://git.causal.agency/pounce"
-#define ORIGIN "irc.invalid"
 
 #define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit
 #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
 
 typedef unsigned char byte;
 
+static inline char *seprintf(char *ptr, char *end, const char *fmt, ...)
+	__attribute__((format(printf, 3, 4)));
+static inline char *seprintf(char *ptr, char *end, const char *fmt, ...) {
+	va_list ap;
+	va_start(ap, fmt);
+	int n = vsnprintf(ptr, end - ptr, fmt, ap);
+	va_end(ap);
+	if (n < 0) return NULL;
+	if (n > end - ptr) return end;
+	return ptr + n;
+}
+
+static inline void set(char **field, const char *value) {
+	if (*field) free(*field);
+	*field = strdup(value);
+	if (!*field) err(EX_OSERR, "strdup");
+}
+
 enum { MessageCap = 8191 + 512 };
 
 enum { ParamCap = 15 };
@@ -51,9 +77,10 @@ struct Message {
 
 static inline struct Message parse(char *line) {
 	struct Message msg = {0};
-	if (line[0] == '@') msg.tags = 1 + strsep(&line, " ");
-	if (line[0] == ':') msg.origin = 1 + strsep(&line, " ");
+	if (line && line[0] == '@') msg.tags = 1 + strsep(&line, " ");
+	if (line && line[0] == ':') msg.origin = 1 + strsep(&line, " ");
 	msg.cmd = strsep(&line, " ");
+	if (msg.cmd && !msg.cmd[0]) msg.cmd = NULL;
 	for (size_t i = 0; line && i < ParamCap; ++i) {
 		if (line[0] == ':') {
 			msg.params[i] = &line[1];
@@ -73,16 +100,21 @@ static inline struct Message parse(char *line) {
 	X("causal.agency/consumer", CapConsumer) \
 	X("causal.agency/passive", CapPassive) \
 	X("chghost", CapChghost) \
+	X("draft/read-marker", CapReadMarker) \
+	X("echo-message", CapEchoMessage) \
 	X("extended-join", CapExtendedJoin) \
+	X("extended-monitor", CapExtendedMonitor) \
 	X("invite-notify", CapInviteNotify) \
 	X("labeled-response", CapLabeledResponse) \
 	X("message-tags", CapMessageTags) \
 	X("multi-prefix", CapMultiPrefix) \
+	X("palaverapp.com", CapPalaverApp) \
 	X("sasl", CapSASL) \
 	X("server-time", CapServerTime) \
 	X("setname", CapSetname) \
 	X("sts", CapSTS) \
 	X("userhost-in-names", CapUserhostInNames) \
+	X("znc.in/self-message", CapSelfMessage) \
 	X("", CapUnsupported)
 
 enum Cap {
@@ -127,13 +159,14 @@ static inline enum Cap capParse(const char *list, const char *values[CapBits]) {
 static inline const char *capList(enum Cap caps, const char *values[CapBits]) {
 	static char buf[1024];
 	buf[0] = '\0';
+	char *ptr = buf, *end = &buf[sizeof(buf)];
 	for (size_t i = 0; i < ARRAY_LEN(CapNames); ++i) {
 		if (caps & (1 << i)) {
-			if (buf[0]) strlcat(buf, " ", sizeof(buf));
-			strlcat(buf, CapNames[i], sizeof(buf));
+			ptr = seprintf(
+				ptr, end, "%s%s", (ptr > buf ? " " : ""), CapNames[i]
+			);
 			if (values && values[i]) {
-				strlcat(buf, "=", sizeof(buf));
-				strlcat(buf, values[i], sizeof(buf));
+				ptr = seprintf(ptr, end, "=%s", values[i]);
 			}
 		}
 	}
@@ -141,6 +174,13 @@ static inline const char *capList(enum Cap caps, const char *values[CapBits]) {
 }
 
 extern bool verbose;
+static inline void
+verboseLog(const char *prefix, const char *line, size_t len) {
+	if (!verbose) return;
+	if (len && line[len - 1] == '\n') len--;
+	if (len && line[len - 1] == '\r') len--;
+	printf("%s %.*s\n", prefix, (int)len, line);
+}
 
 void ringAlloc(size_t len);
 void ringProduce(const char *line);
@@ -154,36 +194,68 @@ void ringInfo(void);
 int ringSave(FILE *file);
 void ringLoad(FILE *file);
 
-void localConfig(FILE *cert, FILE *priv, FILE *ca, bool require);
+int localConfig(
+	const char *cert, const char *priv, const char *ca, bool require
+);
 size_t localBind(int fds[], size_t cap, const char *host, const char *port);
 size_t localUnix(int fds[], size_t cap, const char *path);
-struct tls *localAccept(int *fd, int bind);
+int localAccept(struct tls **tls, int bind);
 
-void serverConfig(bool insecure, const char *cert, const char *priv);
+extern struct timeval serverQueueInterval;
+void serverConfig(
+	bool insecure, const char *trust, const char *cert, const char *priv
+);
 int serverConnect(const char *bindHost, const char *host, const char *port);
+void serverPrintCert(void);
 void serverRecv(void);
 void serverSend(const char *ptr, size_t len);
 void serverFormat(const char *format, ...)
 	__attribute__((format(printf, 1, 2)));
+void serverEnqueue(const char *format, ...)
+	__attribute__((format(printf, 1, 2)));
+void serverDequeue(void);
+void serverClose(void);
 
-extern bool clientCA;
-extern bool clientSTS;
+enum Need {
+	BIT(NeedHandshake),
+	BIT(NeedNick),
+	BIT(NeedUser),
+	BIT(NeedPass),
+	BIT(NeedCapEnd),
+};
+struct Client {
+	bool remove;
+	int sock;
+	struct tls *tls;
+	time_t time;
+	time_t idle;
+	enum Need need;
+	enum Cap caps;
+	size_t consumer;
+	size_t setPos;
+	char buf[MessageCap];
+	size_t len;
+};
+extern enum Cap clientCaps;
+extern char *clientOrigin;
 extern char *clientPass;
 extern char *clientAway;
-struct Client *clientAlloc(struct tls *tls);
+extern char *clientQuit;
+struct Client *clientAlloc(int sock, struct tls *tls);
 void clientFree(struct Client *client);
-bool clientError(const struct Client *client);
 void clientRecv(struct Client *client);
 void clientSend(struct Client *client, const char *ptr, size_t len);
 void clientFormat(struct Client *client, const char *format, ...)
 	__attribute__((format(printf, 2, 3)));
-size_t clientDiff(const struct Client *client);
 void clientConsume(struct Client *client);
+void clientGetMarker(struct Client *client, const char *target);
 
 extern bool stateNoNames;
 extern enum Cap stateCaps;
+extern char *stateAccount;
+extern bool stateAway;
 void stateLogin(
-	const char *pass, bool sasl, const char *plain,
+	const char *pass, enum Cap blind, const char *plain,
 	const char *nick, const char *user, const char *real
 );
 bool stateReady(void);
@@ -192,6 +264,11 @@ void stateSync(struct Client *client);
 const char *stateNick(void);
 const char *stateEcho(void);
 
+char *configPath(char *buf, size_t cap, const char *path, int i);
+char *dataPath(char *buf, size_t cap, const char *path, int i);
+FILE *configOpen(const char *path, const char *mode);
+FILE *dataOpen(const char *path, const char *mode);
+
 struct option;
 int getopt_config(
 	int argc, char *const *argv,