]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Allow %{date:+%A} to mean "Monday"
authorAlan T. DeKok <aland@freeradius.org>
Tue, 14 Jun 2022 22:00:52 +0000 (17:00 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 17 Jun 2022 12:26:21 +0000 (08:26 -0400)
which mirrors the "date +%A" command on Unix.

Also update the documentation and add examples.

raddb/mods-available/date
src/modules/rlm_date/rlm_date.c

index 9030460fc6e56cb92cb9f30a857fc34e1aac3c64..8cc67ede6b010e0fee4ec7c19f8e811785ceb660 100644 (file)
@@ -7,64 +7,34 @@
 #
 #  = Date Module
 #
-#  The `date` module xlat does convert time formats.
+#  The `date` module parses dates, and prints them.
 #
-
+#  The server will normally print dates in its own pre-defined format.
+#  It will also parse dates in a few limited formats.  However, these
+#  operations are for full dates (e.g. January 1, 2020 12:34pm).  The
+#  server does not print dates in other formats, and does not parse
+#  dates in other formats.
 #
-#  ## Configuration Settings
+#  The `date` module adds that functionality.  It allows you to print
+#  dates in almost any format you want.  It allows you to parse dates
+#  in almost any format, so long as you know what the fields are, and
+#  how they are defined.
 #
 
 #
-#  ### Simple date
+#  ## Configuration Settings
 #
 date {
        #
-       #  format::
-       #
-       #  The `xlat` performs in three modes depending on the input it
-       #  is passed.
-       #
-       #  The input string can be:
-       #
-       #    - an expanded date or integer attribute;
-       #    - an expanded string attribute;
-       #    - the fixed strings `request` or `now`.
-       #
-       #  ."Attribute" mode:
-       #
-       #  If the given attribute is of `date` or `integer` type, the date
-       #  xlat will convert it to a `time` string in the format of the
-       #  format config item.
+       #  format:: Formatting of the output string.
        #
-       #  If the given attribute is a `string` type, the `xlat` will attempt
-       #  to parse it in the format specified by the format config item,
-       #  and will expand to a Unix timestamp (seconds since the epoch).
-       #
-       #  ."Get time" mode:
-       #
-       #  If the input is the string `request`, the `xlat` will format the
-       #  time the current request packet arrived according to the format
-       #  string, and return it as a string.
-       #
-       #  If the input is the string `now`, the `xlat` will behave as
-       #  above, for the current time.
-       #
-       #  See also the core provided expansions which are equivalent to
-       #  `request` and `now`, but return numerical values:
-       #
-       #  [options="header,autowidth"]
-       #  |===
-       #  | Parameter | Description
-       #  | %l        | request time in seconds since the epoch.
-       #  | %M        | request time microsecond component.
-       #  | %c        | current time in seconds since the epoch.
-       #  | %C        | current time microsecond component.
-       #  |===
+       #  The format arguments are the same as for the system
+       #  `strftime` call.  See `man strftime` for documentation.
        #
        format = "%b %e %Y %H:%M:%S %Z"
 
        #
-       #  utc::
+       #  utc:: Whether conversions are in UTC or local time.
        #
        #  If `utc` is enabled then any conversions will be made
        #  as UTC, not localtime.
@@ -87,3 +57,58 @@ date date_iso {
        format = "%Y-%m-%dT%H:%M:%SZ"
        utc = yes
 }
+
+#
+# ### xlat expansions
+#
+#  The `date` module defines an expansion `%{date:}` When the
+#  expansion is not passed an argument, it returns the current date
+#  printed according to the `format` string defined above.
+#
+#  ."Attribute" mode:
+#
+#  If the argument to `%{date:...}` is an attribute of `date` or
+#  `integer` type, the date used will be the time given by the
+#  relevant attribute.
+#
+#  For example, `%{date:&Event-Timestamp}` will use the date from the
+#  `Event-Timestamp` attribute as the source of the date for printing.
+#
+#  ."Get time" mode:
+#
+#  If the input is the string `request`, the `xlat` will format the
+#  time the current request packet arrived according to the format
+#  string, and return it as a string.
+#
+#  If the input is the string `now`, the `xlat` will behave as
+#  above, for the current time.
+#
+#  If the input string begins with `+`, then the remainder of the
+#  string is interpreted as if the string had been given in the
+#  `format` configuration item.
+#
+#  For example `%{date:'+%A'}` will return `Monday` if today is Monday.
+#
+#  Note that the `%` character is special for xlat expansions, and therefore
+#  either has to be "protected" by string quotation, or the `%` character has
+#  to be escaped itself, as in `%{date:+%%A}`
+#
+#  ."Integer output"
+#
+#  In some cases, it is useful for the module to parse dates instead
+#  of printing them.  In this mode, the format string is ignored.
+#  Instead, the input arguments are parsed, and the output is an
+#  integer containg the requested value.
+#
+#  The following expansions return integer numbers:
+#
+#  [options="header,autowidth"]
+#  |===
+#  | Parameter       | Description
+#  | %l              | request time in seconds since the epoch.
+#  | %M              | request time microsecond component.
+#  | %c              | current time in seconds since the epoch.
+#  | %C              | current time microsecond component.
+#  | &Attribute-Name | for string attributes, parse the string according to `format`, and return the integer value as a Unix timestamp.
+#  |===
+#
index 4e8fd27acfaa01cb036b2f5791af31a410c9ef43..ce51a2423ee71bdab247057ba647b35c6eb4b84e 100644 (file)
@@ -107,7 +107,7 @@ static xlat_action_t date_convert_string(TALLOC_CTX *ctx, fr_dcursor_t *out, req
 }
 
 static xlat_action_t date_encode_strftime(TALLOC_CTX *ctx, fr_dcursor_t *out, rlm_date_t const *inst,
-                                         request_t *request, time_t date)
+                                         request_t *request, char const *fmt, time_t date)
 {
        struct tm       tminfo;
        char            buff[64];
@@ -125,7 +125,7 @@ static xlat_action_t date_encode_strftime(TALLOC_CTX *ctx, fr_dcursor_t *out, rl
                }
        }
 
-       if (strftime(buff, sizeof(buff), inst->fmt, &tminfo) == 0) return XLAT_ACTION_FAIL;
+       if (strftime(buff, sizeof(buff), fmt, &tminfo) == 0) return XLAT_ACTION_FAIL;
 
        MEM(vb = fr_value_box_alloc_null(ctx));
        MEM(fr_value_box_strdup(ctx, vb, NULL, buff, false) == 0);
@@ -182,13 +182,20 @@ static xlat_action_t xlat_date_convert(TALLOC_CTX *ctx, fr_dcursor_t *out,
         */
        if (arg->type == FR_TYPE_STRING) {
                if (strcmp(arg->vb_strvalue, "request") == 0) {
-                       return date_encode_strftime(ctx, out, inst, request,
+                       return date_encode_strftime(ctx, out, inst, request, inst->fmt,
                                                    fr_time_to_sec(request->packet->timestamp));
                }
 
                if (strcmp(arg->vb_strvalue, "now") == 0) {
                now:
-                       return date_encode_strftime(ctx, out, inst, request, fr_time_to_sec(fr_time()));
+                       return date_encode_strftime(ctx, out, inst, request, inst->fmt, fr_time_to_sec(fr_time()));
+               }
+
+               /*
+                *      %{date:'+%A'} == "Monday", to mirror the behavior of the `date` command.
+                */
+               if (arg->vb_strvalue[0] == '+') {
+                       return date_encode_strftime(ctx, out, inst, request, arg->vb_strvalue + 1, fr_time_to_sec(fr_time()));
                }
        }
 
@@ -199,13 +206,13 @@ static xlat_action_t xlat_date_convert(TALLOC_CTX *ctx, fr_dcursor_t *out,
         *      format as a string.
         */
        case FR_TYPE_DATE:
-               return date_encode_strftime(ctx, out, inst, request, fr_unix_time_to_sec(arg->vb_date));
+               return date_encode_strftime(ctx, out, inst, request, inst->fmt, fr_unix_time_to_sec(arg->vb_date));
 
        case FR_TYPE_UINT32:
-               return date_encode_strftime(ctx, out, inst, request, (time_t) arg->vb_uint32);
+               return date_encode_strftime(ctx, out, inst, request, inst->fmt, (time_t) arg->vb_uint32);
 
        case FR_TYPE_UINT64:
-               return date_encode_strftime(ctx, out, inst, request, (time_t) arg->vb_uint64);
+               return date_encode_strftime(ctx, out, inst, request, inst->fmt, (time_t) arg->vb_uint64);
 
        /*
         *      These are 'from' types, i.e. we'll convert the input string