The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_date.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * @file rlm_date.c
19  * @brief Translates timestrings between formats.
20  *
21  * @author Artur Malinowski (artur@wow.com)
22  *
23  * @copyright 2013 Artur Malinowski (artur@wow.com)
24  * @copyright 1999-2018 The FreeRADIUS Server Project.
25  */
26 
27 #include <freeradius-devel/server/base.h>
28 #include <freeradius-devel/server/module_rlm.h>
29 #include <freeradius-devel/unlang/xlat_func.h>
30 #include <ctype.h>
31 #include <time.h>
32 
33 typedef struct {
34  char const *fmt;
35  bool utc;
36 } rlm_date_t;
37 
38 static const conf_parser_t module_config[] = {
39  { FR_CONF_OFFSET("format", rlm_date_t, fmt), .dflt = "%b %e %Y %H:%M:%S %Z" },
40  { FR_CONF_OFFSET("utc", rlm_date_t, utc), .dflt = "no" },
42 };
43 
44 DIAG_OFF(format-nonliteral)
45 static xlat_action_t date_convert_string(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request,
46  const char *str, rlm_date_t const *inst)
47 {
48  struct tm tminfo;
49  time_t date = 0;
50  fr_value_box_t *vb;
51  bool utc = inst->utc;
52 
53 #ifdef __APPLE__
54  /*
55  * OSX "man strptime" says it only accepts the local time zone, and GMT.
56  *
57  * However, when printing dates via strftime(), it prints
58  * "UTC" instead of "GMT". So... we have to fix it up
59  * for stupid nonsense.
60  */
61  char const *tz = strstr(str, "UTC");
62  if (tz) {
63  char *my_str;
64  char *p;
65 
66  /*
67  *
68  */
69  MEM(my_str = talloc_strdup(ctx, str));
70  p = my_str + (tz - str);
71  memcpy(p, "GMT", 3);
72 
73  p = strptime(my_str, inst->fmt, &tminfo);
74  if (!p) {
75  REDEBUG("Failed to parse time string \"%s\" as format '%s'", my_str, inst->fmt);
76  talloc_free(my_str);
77  return XLAT_ACTION_FAIL;
78  }
79  talloc_free(my_str);
80 
81  /*
82  * The output is converted to the local time zone, so
83  * we can't use UTC.
84  */
85  utc = false;
86  } else
87 #endif
88 
89  if (strptime(str, inst->fmt, &tminfo) == NULL) {
90  REDEBUG("Failed to parse time string \"%s\" as format '%s'", str, inst->fmt);
91  return XLAT_ACTION_FAIL;
92  }
93 
94  if (utc) {
95  date = timegm(&tminfo);
96  } else {
97  date = mktime(&tminfo);
98  }
99  if (date < 0) {
100  REDEBUG("Failed converting parsed time into unix time");
101  return XLAT_ACTION_FAIL;
102  }
103 
104  MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL));
105  vb->vb_date = fr_unix_time_from_sec(date);
106  fr_dcursor_append(out, vb);
107  return XLAT_ACTION_DONE;
108 }
109 
111  request_t *request, char const *fmt, time_t date)
112 {
113  struct tm tminfo;
114  char buff[64];
115  fr_value_box_t *vb;
116 
117  if (inst->utc) {
118  if (gmtime_r(&date, &tminfo) == NULL) {
119  REDEBUG("Failed converting time string to gmtime: %s", fr_syserror(errno));
120  return XLAT_ACTION_FAIL;
121  }
122  } else {
123  if (localtime_r(&date, &tminfo) == NULL) {
124  REDEBUG("Failed converting time string to localtime: %s", fr_syserror(errno));
125  return XLAT_ACTION_FAIL;
126  }
127  }
128 
129  if (strftime(buff, sizeof(buff), fmt, &tminfo) == 0) return XLAT_ACTION_FAIL;
130 
131  MEM(vb = fr_value_box_alloc_null(ctx));
132  MEM(fr_value_box_strdup(ctx, vb, NULL, buff, false) == 0);
133  fr_dcursor_append(out, vb);
134 
135  return XLAT_ACTION_DONE;
136 }
137 DIAG_ON(format-nonliteral)
138 
140  { .required = true, .single = true, .type = FR_TYPE_VOID },
142 };
143 
144 /** Get or convert time and date
145  *
146  * Using the format in the module instance configuration, get
147  * various timestamps, or convert strings to date format.
148  *
149  * When the request arrived:
150 @verbatim
151 %date(request)
152 @endverbatim
153  *
154  * Now:
155 @verbatim
156 %date(now)
157 @endverbatim
158  *
159  * Examples (Tmp-Integer-0 = 1506101100):
160 @verbatim
161 update request {
162  &Tmp-String-0 := %date(%{Tmp-Integer-0}) ("Fri 22 Sep 18:25:00 BST 2017")
163  &Tmp-Integer-1 := %date(%{Tmp-String-0}) (1506101100)
164 }
165 @endverbatim
166  *
167  * @ingroup xlat_functions
168  */
170  xlat_ctx_t const *xctx,
171  request_t *request, fr_value_box_list_t *in)
172 {
173  rlm_date_t const *inst = talloc_get_type_abort(xctx->mctx->inst->data, rlm_date_t);
174  struct tm tminfo;
175  fr_value_box_t *arg = fr_value_box_list_head(in);
176 
177  memset(&tminfo, 0, sizeof(tminfo));
178 
179  if (!arg) goto now;
180 
181  /*
182  * Certain strings have magical meanings.
183  */
184  if (arg->type == FR_TYPE_STRING) {
185  if (strcmp(arg->vb_strvalue, "request") == 0) {
186  return date_encode_strftime(ctx, out, inst, request, inst->fmt,
187  fr_time_to_sec(request->packet->timestamp));
188  }
189 
190  if (strcmp(arg->vb_strvalue, "now") == 0) {
191  now:
192  return date_encode_strftime(ctx, out, inst, request, inst->fmt, fr_time_to_sec(fr_time()));
193  }
194 
195  /*
196  * %date('+%A') == "Monday", to mirror the behavior of the `date` command.
197  */
198  if (arg->vb_strvalue[0] == '+') {
199  return date_encode_strftime(ctx, out, inst, request, arg->vb_strvalue + 1, fr_time_to_sec(fr_time()));
200  }
201  }
202 
203  switch (arg->type) {
204  /*
205  * These are 'to' types, i.e. we'll convert the integers
206  * to a time structure, and then output it in the specified
207  * format as a string.
208  */
209  case FR_TYPE_DATE:
210  return date_encode_strftime(ctx, out, inst, request, inst->fmt, fr_unix_time_to_sec(arg->vb_date));
211 
212  case FR_TYPE_UINT32:
213  return date_encode_strftime(ctx, out, inst, request, inst->fmt, (time_t) arg->vb_uint32);
214 
215  case FR_TYPE_UINT64:
216  return date_encode_strftime(ctx, out, inst, request, inst->fmt, (time_t) arg->vb_uint64);
217 
218  /*
219  * These are 'from' types, i.e. we'll convert the input string
220  * into a time structure, and then output it as an integer
221  * unix timestamp.
222  */
223  case FR_TYPE_STRING:
224  return date_convert_string(ctx, out, request, arg->vb_strvalue, inst);
225 
226  default:
227  REDEBUG("Can't convert type %s into date", fr_type_to_str(arg->type));
228  }
229 
230  return XLAT_ACTION_FAIL;
231 }
232 
233 static int mod_bootstrap(module_inst_ctx_t const *mctx)
234 {
235  rlm_date_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_date_t );
236  xlat_t *xlat;
237 
240 
241  return 0;
242 }
243 
244 extern module_rlm_t rlm_date;
246  .common = {
247  .magic = MODULE_MAGIC_INIT,
248  .name = "date",
249  .inst_size = sizeof(rlm_date_t),
251  .bootstrap = mod_bootstrap
252  }
253 };
static int const char * fmt
Definition: acutest.h:573
#define DIAG_ON(_x)
Definition: build.h:419
#define DIAG_OFF(_x)
Definition: build.h:418
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition: dcursor.h:405
static fr_slen_t in
Definition: dict.h:645
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
static xlat_action_t xlat_date_convert(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Get or convert time and date.
Definition: rlm_date.c:169
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
Definition: merged_model.c:111
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
@ FR_TYPE_UINT64
64 Bit unsigned integer.
Definition: merged_model.c:100
@ FR_TYPE_VOID
User data.
Definition: merged_model.c:127
struct tm * gmtime_r(time_t const *l_clock, struct tm *result)
Definition: missing.c:201
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition: missing.c:163
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
static const conf_parser_t config[]
Definition: base.c:188
#define REDEBUG(fmt,...)
Definition: radclient.h:52
char const * fmt
Definition: rlm_date.c:34
module_rlm_t rlm_date
Definition: rlm_date.c:245
static xlat_arg_parser_t const xlat_date_convert_args[]
Definition: rlm_date.c:139
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition: rlm_date.c:233
static xlat_action_t date_encode_strftime(TALLOC_CTX *ctx, fr_dcursor_t *out, rlm_date_t const *inst, request_t *request, char const *fmt, time_t date)
Definition: rlm_date.c:110
bool utc
Definition: rlm_date.c:35
static const conf_parser_t module_config[]
Definition: rlm_date.c:38
static xlat_action_t date_convert_string(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, const char *str, rlm_date_t const *inst)
Definition: rlm_date.c:45
static char buff[sizeof("18446744073709551615")+3]
Definition: size_tests.c:41
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition: state_test.c:8
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
Simple time functions.
static int64_t fr_time_to_sec(fr_time_t when)
Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time)
Definition: time.h:729
static int64_t fr_unix_time_to_sec(fr_unix_time_t delta)
Definition: time.h:504
static fr_unix_time_t fr_unix_time_from_sec(int64_t sec)
Definition: time.h:447
bool required
Argument must be present, and non-empty.
Definition: xlat.h:146
#define XLAT_ARG_PARSER_TERMINATOR
Definition: xlat.h:166
xlat_action_t
Definition: xlat.h:35
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition: xlat.h:42
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition: xlat.h:41
Definition for a single argument consumend by an xlat function.
Definition: xlat.h:145
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition: types.h:433
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition: value.c:3876
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition: value.h:608
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition: value.h:619
int format(printf, 5, 0))
static size_t char ** out
Definition: value.h:984
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition: xlat_ctx.h:45
An xlat calling ctx.
Definition: xlat_ctx.h:42
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition: xlat_func.c:360
xlat_t * xlat_func_register_module(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function for a module.
Definition: xlat_func.c:274