From: Arran Cudbard-Bell Date: Mon, 27 May 2024 23:23:24 +0000 (-0400) Subject: Add str.printable to check if a string only consists of printable i.e. non-whitespace... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8ed70f5e408cda144eb04989c48298b8531190cb;p=thirdparty%2Ffreeradius-server.git Add str.printable to check if a string only consists of printable i.e. non-whitespace, non-control chars, and valid utf8 sequences --- diff --git a/src/lib/unlang/xlat_builtin.c b/src/lib/unlang/xlat_builtin.c index 1b37edf1d56..57672320fa4 100644 --- a/src/lib/unlang/xlat_builtin.c +++ b/src/lib/unlang/xlat_builtin.c @@ -2975,6 +2975,66 @@ static xlat_action_t xlat_func_strlen(TALLOC_CTX *ctx, fr_dcursor_t *out, return XLAT_ACTION_DONE; } +static xlat_arg_parser_t const xlat_func_str_printable_arg[] = { + { .concat = true, .type = FR_TYPE_STRING }, + { .single = true, .type = FR_TYPE_BOOL }, + XLAT_ARG_PARSER_TERMINATOR +}; + +/** Return whether a string has only printable chars + * + * This function returns true if the input string contains UTF8 sequences and printable chars. + * + * @note "\t" and " " are considered unprintable chars, unless the second argument(relaxed) is true. + * + * Example: +@verbatim +%str.printable("🍉abcdef🍓") == true +%str.printable("\000\n\r\t") == false +%str.printable("\t abcd", yes) == true +@endverbatim + * + * @ingroup xlat_functions + */ +static xlat_action_t xlat_func_str_printable(TALLOC_CTX *ctx, fr_dcursor_t *out, + UNUSED xlat_ctx_t const *xctx, + UNUSED request_t *request, fr_value_box_list_t *args) +{ + fr_value_box_t *vb; + fr_value_box_t *str; + fr_value_box_t *relaxed_vb; + uint8_t const *p, *end; + bool relaxed = false; + + XLAT_ARGS(args, &str, &relaxed_vb); + + if (relaxed_vb) relaxed = relaxed_vb->vb_bool; + + p = (uint8_t const *)str->vb_strvalue; + end = p + str->vb_length; + + MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL)); + fr_dcursor_append(out, vb); + vb->vb_bool = false; + + do { + size_t clen; + + if ((*p < '!') && + (!relaxed || ((*p != '\t') && (*p != ' ')))) return XLAT_ACTION_DONE; + + if (*p == 0x7f) return XLAT_ACTION_DONE; + + clen = fr_utf8_char(p, end - p); + if (clen == 0) return XLAT_ACTION_DONE; + p += clen; + } while (p < end); + + vb->vb_bool = true; + + return XLAT_ACTION_DONE; +} + static xlat_arg_parser_t const xlat_func_str_utf8_arg[] = { { .concat = true, .type = FR_TYPE_STRING }, XLAT_ARG_PARSER_TERMINATOR @@ -4092,6 +4152,7 @@ do { \ XLAT_REGISTER_PURE("string", xlat_func_string, FR_TYPE_STRING, xlat_func_string_arg); XLAT_REGISTER_PURE("strlen", xlat_func_strlen, FR_TYPE_SIZE, xlat_func_strlen_arg); XLAT_REGISTER_PURE("str.utf8", xlat_func_str_utf8, FR_TYPE_BOOL, xlat_func_str_utf8_arg); + XLAT_REGISTER_PURE("str.printable", xlat_func_str_printable, FR_TYPE_BOOL, xlat_func_str_printable_arg); XLAT_REGISTER_PURE("tolower", xlat_func_tolower, FR_TYPE_STRING, xlat_change_case_arg); XLAT_REGISTER_PURE("toupper", xlat_func_toupper, FR_TYPE_STRING, xlat_change_case_arg); XLAT_REGISTER_PURE("urlquote", xlat_func_urlquote, FR_TYPE_STRING, xlat_func_urlquote_arg); diff --git a/src/tests/keywords/xlat-printable b/src/tests/keywords/xlat-printable new file mode 100644 index 00000000000..01522c27fd2 --- /dev/null +++ b/src/tests/keywords/xlat-printable @@ -0,0 +1,31 @@ +# +# PRE: if +# +string printable +string unprintable + +&printable = "🍩abcdef🍩" +&unprintable = "\t🍩\n" + +# Relaxed mode allows tabs and spaces +if (!%str.printable("\t 🍩", "yes")) { + test_fail +} + +if (%str.printable("\xfe")) { + test_fail +} + +if (%str.printable(%{unprintable})) { + test_fail +} + +if (%str.printable("\000abcd")) { + test_fail +} + +if (!%str.printable(%{printable})) { + test_fail +} + +success