diff --git a/doc/RELEASE-NOTES.md b/doc/RELEASE-NOTES.md index ee436dddb..e80973af0 100644 --- a/doc/RELEASE-NOTES.md +++ b/doc/RELEASE-NOTES.md @@ -12,6 +12,11 @@ This is work in progress and may not always be a stable version. * New `@if module-exists("modulename")` to check if a module exists on disk. You can use this with a subsequent `loadmodule` and config items, this can be handy with 3rd party modules. + * New `@if minimum-version("6.2.4")` to check if the UnrealIRCd version + is at least the specified version. + * New `@if file-exists("filename")` to check if a file exists. + Paths are relative to the conf directory, or absolute if starting + with `/`. ### Changes: * [GeoIP](https://www.unrealircd.org/docs/GeoIP): diff --git a/include/h.h b/include/h.h index 1ff3d6d9f..cfa4d3832 100644 --- a/include/h.h +++ b/include/h.h @@ -1106,6 +1106,7 @@ extern MODVAR Client *remote_rehash_client; extern MODVAR json_t *json_rehash_log; extern MODVAR int debugfd; extern void convert_to_absolute_path(char **path, const char *reldir); +extern char *convert_to_absolute_path_duplicate(char *path, char *reldir); extern int has_user_mode(Client *acptr, char mode); extern int has_channel_mode(Channel *channel, char mode); extern int has_channel_mode_raw(Cmode_t m, char mode); diff --git a/include/struct.h b/include/struct.h index d9f4ddf80..b7ef79c6f 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1649,7 +1649,7 @@ struct AuthConfig { * conf2 stuff -stskeeps */ -typedef enum ConfigIfCondition { IF_DEFINED=1, IF_VALUE=2, IF_MODULE_LOADED=3, IF_MODULE_EXISTS=4} ConfigIfCondition; +typedef enum ConfigIfCondition { IF_DEFINED=1, IF_VALUE=2, IF_MODULE_LOADED=3, IF_MODULE_EXISTS=4, IF_MINIMUM_VERSION=5, IF_FILE_EXISTS=6} ConfigIfCondition; struct ConditionalConfig { diff --git a/src/conf_preprocessor.c b/src/conf_preprocessor.c index 4cb8f8744..ae3f6db85 100644 --- a/src/conf_preprocessor.c +++ b/src/conf_preprocessor.c @@ -35,6 +35,10 @@ PreprocessorItem evaluate_preprocessor_if(char *statement, const char *filename, * !module-loaded("something") * module-exists("something") * !module-exists("something") + * minimum-version("6.2.0") + * !minimum-version("6.2.0") + * file-exists("something") + * !file-exists("something") * defined($XYZ) * !defined($XYZ) * We do not support && or || or anything else at this time. @@ -110,6 +114,66 @@ PreprocessorItem evaluate_preprocessor_if(char *statement, const char *filename, *cc_out = cc; return PREPROCESSOR_IF; } else + if (!strncmp(p, "minimum-version", 15)) + { + p += 15; + skip_whitespace(&p); + if (*p != '(') + { + config_error("%s:%i: expected '(' for minimum-version(...", + filename, linenumber); + return PREPROCESSOR_ERROR; + } + p++; + skip_whitespace(&p); + if (*p == '"') + p++; + name = p; + read_until(&p, ")\""); + if (!*p) + { + config_error("%s:%i: invalid if statement (termination error): %s", + filename, linenumber, statement); + return PREPROCESSOR_ERROR; + } + *p = '\0'; + cc = safe_alloc(sizeof(ConditionalConfig)); + cc->condition = IF_MINIMUM_VERSION; + cc->negative = negative; + safe_strdup(cc->name, name); + *cc_out = cc; + return PREPROCESSOR_IF; + } else + if (!strncmp(p, "file-exists", 11)) + { + p += 11; + skip_whitespace(&p); + if (*p != '(') + { + config_error("%s:%i: expected '(' for file-exists(...", + filename, linenumber); + return PREPROCESSOR_ERROR; + } + p++; + skip_whitespace(&p); + if (*p == '"') + p++; + name = p; + read_until(&p, ")\""); + if (!*p) + { + config_error("%s:%i: invalid if statement (termination error): %s", + filename, linenumber, statement); + return PREPROCESSOR_ERROR; + } + *p = '\0'; + cc = safe_alloc(sizeof(ConditionalConfig)); + cc->condition = IF_FILE_EXISTS; + cc->negative = negative; + safe_strdup(cc->name, name); + *cc_out = cc; + return PREPROCESSOR_IF; + } else if (!strncmp(p, "defined", 7)) { p += 7; @@ -392,6 +456,18 @@ int preprocessor_resolve_if(ConditionalConfig *cc, PreprocessorPhase phase) if (file_exists(fullpath)) result = 1; } else + if (cc->condition == IF_MINIMUM_VERSION) + { + if (strnatcasecmp(VERSIONONLY, cc->name) >= 0) + result = 1; + } else + if (cc->condition == IF_FILE_EXISTS) + { + char *fullpath = convert_to_absolute_path_duplicate(cc->name, CONFDIR); + if (file_exists(fullpath)) + result = 1; + safe_free(fullpath); + } else if (cc->condition == IF_DEFINED) { NameValuePrioList *d = find_config_define(cc->name);