diff options
Diffstat (limited to 'edit.c')
-rw-r--r-- | edit.c | 158 |
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; |