about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--edit.c158
1 files changed, 88 insertions, 70 deletions
diff --git a/edit.c b/edit.c
index 4003fac..7401a3c 100644
--- a/edit.c
+++ b/edit.c
@@ -232,74 +232,101 @@ static void handleError(struct Message *msg) {
 	errx(EX_UNAVAILABLE, "%s", msg->params[0]);
 }
 
-static const char *Boolean[] = {
-	"no-names", "no-sts", "palaver", "sasl-external", "verbose",
-};
+typedef const char *Check(const char *value);
 
-static const char *Integer[] = {
-	"local-port", "port", "queue-interval", "size",
-};
+static const char *checkBool(const char *value) {
+	return (value ? "does not take a value" : NULL);
+}
 
-// FIXME: local-pass needs to be validated for hash
-// FIXME: sasl-plain needs to be validated for colon
-static const char *String[] = {
-	"away", "bind", "blind-req", "client-cert", "client-priv", "host", "join",
-	"local-ca", "local-cert", "local-host", "local-pass", "local-path",
-	"local-priv", "mode", "nick", "pass", "quit", "real", "sasl-plain", "save",
-	"trust", "user",
-};
+static const char *checkStr(const char *value) {
+	return (value ? NULL : "requires a value");
+}
+
+static const char *checkInt(const char *value) {
+	if (!value) return "requires a value";
+	char *end;
+	strtoull(value, &end, 10);
+	if (!*value || *end) return "must be an integer";
+	return NULL;
+}
 
-// TODO: nick, user aren't safe until pounce can fall back in case
-// they're invalid
-static const char *Safe[] = {
-	"away", "join", "local-pass", "mode", "nick", "no-names", "no-sts",
-	"palaver", "quit", "real", "user",
+static const char *checkSize(const char *value) {
+	const char *error = checkInt(value);
+	if (error) return error;
+	size_t n = strtoull(value, NULL, 10);
+	if (!n || n & (n-1)) return "must be a power of two";
+	return NULL;
+}
+
+static const char *checkHash(const char *value) {
+	// TODO
+	return NULL;
+}
+
+static const char *checkPair(const char *value) {
+	// TODO
+	return NULL;
+}
+
+static const struct {
+	const char *name;
+	bool safe;
+	Check *check;
+} Options[] = {
+	{ "away", true, checkStr },
+	{ "bind", false, checkStr },
+	{ "blind-req", false, checkStr },
+	{ "client-cert", false, checkStr },
+	{ "client-priv", false, checkStr },
+	{ "host", false, checkStr },
+	{ "join", true, checkStr },
+	{ "local-ca", false, checkStr },
+	{ "local-cert", false, checkStr },
+	{ "local-host", false, checkStr },
+	{ "local-pass", true, checkHash },
+	{ "local-path", false, checkStr },
+	{ "local-port", false, checkInt },
+	{ "local-priv", false, checkStr },
+	{ "mode", true, checkStr },
+	{ "nick", true, checkStr }, // FIXME: not safe until pounce can fallback
+	{ "no-names", true, checkBool },
+	{ "no-sts", true, checkBool },
+	{ "palaver", true, checkBool },
+	{ "pass", false, checkStr },
+	{ "port", false, checkInt },
+	{ "queue-interval", false, checkInt },
+	{ "quit", true, checkStr },
+	{ "real", true, checkStr },
+	{ "sasl-external", false, checkBool },
+	{ "sasl-plain", false, checkPair },
+	{ "save", false, checkStr },
+	{ "size", true, checkSize },
+	{ "trust", false, checkStr },
+	{ "user", true, checkStr }, // FIXME: not safe until pounce can fallback
+	{ "verbose", true, checkBool },
 };
 
-static bool allowUnsafe;
-static bool safe(const char *name) {
-	if (allowUnsafe) return true;
-	for (size_t i = 0; i < ARRAY_LEN(Safe); ++i) {
-		if (!strcmp(Safe[i], name)) return true;
+static bool exists(const char *name) {
+	for (size_t i = 0; i < ARRAY_LEN(Options); ++i) {
+		if (!strcmp(Options[i].name, name)) return true;
 	}
 	return false;
 }
 
-static bool exists(const char *name) {
-	for (size_t i = 0; i < ARRAY_LEN(Boolean); ++i) {
-		if (!strcmp(Boolean[i], name)) return true;
-	}
-	for (size_t i = 0; i < ARRAY_LEN(Integer); ++i) {
-		if (!strcmp(Integer[i], name)) return true;
-	}
-	for (size_t i = 0; i < ARRAY_LEN(String); ++i) {
-		if (!strcmp(String[i], name)) return true;
+static bool allowUnsafe;
+static bool safe(const char *name) {
+	if (allowUnsafe) return true;
+	for (size_t i = 0; i < ARRAY_LEN(Options); ++i) {
+		if (!strcmp(Options[i].name, name)) return Options[i].safe;
 	}
 	return false;
 }
 
-static const char *validate(const char *name, const char *value) {
-	for (size_t i = 0; i < ARRAY_LEN(Boolean); ++i) {
-		if (strcmp(Boolean[i], name)) continue;
-		if (!safe(name)) return "cannot be set";
-		return (value ? "does not take a value" : NULL);
-	}
-	for (size_t i = 0; i < ARRAY_LEN(Integer); ++i) {
-		if (strcmp(Integer[i], name)) continue;
-		if (!safe(name)) return "cannot be set";
-		if (!value) return "requires a value";
-		char *end;
-		size_t n = strtoull(value, &end, 10);
-		if (!*value || *end) return "must be an integer";
-		if (!strcmp(name, "size") && (!n || n & (n-1))) {
-			return "must be a power of two";
-		}
-		return NULL;
-	}
-	for (size_t i = 0; i < ARRAY_LEN(String); ++i) {
-		if (strcmp(String[i], name)) continue;
-		if (!safe(name)) return "cannot be set";
-		return (value ? NULL : "requires a value");
+static const char *check(const char *name, const char *value) {
+	for (size_t i = 0; i < ARRAY_LEN(Options); ++i) {
+		if (strcmp(Options[i].name, name)) continue;
+		if (!allowUnsafe && !Options[i].safe) return "cannot be set";
+		return Options[i].check(value);
 	}
 	return "is not an option";
 }
@@ -348,26 +375,17 @@ static void handlePrivmsg(struct Message *msg) {
 	} else if (!strcmp(cmd, "set")) {
 		if (!name) {
 			format("NOTICE %s :options: ", msg->nick);
-			if (allowUnsafe) {
-				for (size_t i = 0; i < ARRAY_LEN(Boolean); ++i) {
-					format("%s\2%s\2", (i ? ", " : ""), Boolean[i]);
-				}
-				for (size_t i = 0; i < ARRAY_LEN(Integer); ++i) {
-					format(", \2%s\2", Integer[i]);
-				}
-				for (size_t i = 0; i < ARRAY_LEN(String); ++i) {
-					format(", \2%s\2", String[i]);
-				}
-			} else {
-				for (size_t i = 0; i < ARRAY_LEN(Safe); ++i) {
-					format("%s\2%s\2", (i ? ", " : ""), Safe[i]);
-				}
+			bool first = true;
+			for (size_t i = 0; i < ARRAY_LEN(Options); ++i) {
+				if (!allowUnsafe && !Options[i].safe) continue;
+				format("%s\2%s\2", (first ? "" : ", "), Options[i].name);
+				first = false;
 			}
 			format("\r\n");
 			return;
 		}
 
-		const char *error = validate(name, value);
+		const char *error = check(name, value);
 		if (error) {
 			format("NOTICE %s :\2%s\2 %s\r\n", msg->nick, name, error);
 			return;