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 <time.h>
31 
32 typedef struct {
33  char const *fmt;
34  bool utc;
35 } rlm_date_t;
36 
37 static const conf_parser_t module_config[] = {
38  { FR_CONF_OFFSET("format", rlm_date_t, fmt), .dflt = "%b %e %Y %H:%M:%S %Z" },
39  { FR_CONF_OFFSET("utc", rlm_date_t, utc), .dflt = "no" },
41 };
42 
43 DIAG_OFF(format-nonliteral)
44 static xlat_action_t date_convert_string(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request,
45  const char *str, rlm_date_t const *inst)
46 {
47  struct tm tminfo;
48  time_t date = 0;
49  fr_value_box_t *vb;
50  bool utc = inst->utc;
51 
52 #ifdef __APPLE__
53  /*
54  * OSX "man strptime" says it only accepts the local time zone, and GMT.
55  *
56  * However, when printing dates via strftime(), it prints
57  * "UTC" instead of "GMT". So... we have to fix it up
58  * for stupid nonsense.
59  */
60  char const *tz = strstr(str, "UTC");
61  if (tz) {
62  char *my_str;
63  char *p;
64 
65  /*
66  *
67  */
68  MEM(my_str = talloc_strdup(ctx, str));
69  p = my_str + (tz - str);
70  memcpy(p, "GMT", 3);
71 
72  p = strptime(my_str, inst->fmt, &tminfo);
73  if (!p) {
74  REDEBUG("Failed to parse time string \"%s\" as format '%s'", my_str, inst->fmt);
75  talloc_free(my_str);
76  return XLAT_ACTION_FAIL;
77  }
78  talloc_free(my_str);
79 
80  /*
81  * The output is converted to the local time zone, so
82  * we can't use UTC.
83  */
84  utc = false;
85  } else
86 #endif
87 
88  if (strptime(str, inst->fmt, &tminfo) == NULL) {
89  REDEBUG("Failed to parse time string \"%s\" as format '%s'", str, inst->fmt);
90  return XLAT_ACTION_FAIL;
91  }
92 
93  if (utc) {
94  date = timegm(&tminfo);
95  } else {
96  date = mktime(&tminfo);
97  }
98  if (date < 0) {
99  REDEBUG("Failed converting parsed time into unix time");
100  return XLAT_ACTION_FAIL;
101  }
102 
103  MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL));
104  vb->vb_date = fr_unix_time_from_sec(date);
105  fr_dcursor_append(out, vb);
106  return XLAT_ACTION_DONE;
107 }
108 
110  request_t *request, char const *fmt, time_t date)
111 {
112  struct tm tminfo;
113  char buff[64];
114  fr_value_box_t *vb;
115 
116  if (inst->utc) {
117  if (gmtime_r(&date, &tminfo) == NULL) {
118  REDEBUG("Failed converting time string to gmtime: %s", fr_syserror(errno));
119  return XLAT_ACTION_FAIL;
120  }
121  } else {
122  if (localtime_r(&date, &tminfo) == NULL) {
123  REDEBUG("Failed converting time string to localtime: %s", fr_syserror(errno));
124  return XLAT_ACTION_FAIL;
125  }
126  }
127 
128  if (strftime(buff, sizeof(buff), fmt, &tminfo) == 0) return XLAT_ACTION_FAIL;
129 
130  MEM(vb = fr_value_box_alloc_null(ctx));
131  MEM(fr_value_box_strdup(ctx, vb, NULL, buff, false) == 0);
132  fr_dcursor_append(out, vb);
133 
134  return XLAT_ACTION_DONE;
135 }
136 DIAG_ON(format-nonliteral)
137 
139  { .required = true, .single = true, .type = FR_TYPE_VOID },
141 };
142 
143 /** Get or convert time and date
144  *
145  * Using the format in the module instance configuration, get
146  * various timestamps, or convert strings to date format.
147  *
148  * When the request arrived:
149 @verbatim
150 %date(request)
151 @endverbatim
152  *
153  * Now:
154 @verbatim
155 %date(now)
156 @endverbatim
157  *
158  * Examples (Tmp-Integer-0 = 1506101100):
159 @verbatim
160 update request {
161  &Tmp-String-0 := %date(%{Tmp-Integer-0}) ("Fri 22 Sep 18:25:00 BST 2017")
162  &Tmp-Integer-1 := %date(%{Tmp-String-0}) (1506101100)
163 }
164 @endverbatim
165  *
166  * @ingroup xlat_functions
167  */
169  xlat_ctx_t const *xctx,
170  request_t *request, fr_value_box_list_t *in)
171 {
172  rlm_date_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_date_t);
173  struct tm tminfo;
174  fr_value_box_t *arg = fr_value_box_list_head(in);
175 
176  memset(&tminfo, 0, sizeof(tminfo));
177 
178  if (!arg) goto now;
179 
180  /*
181  * Certain strings have magical meanings.
182  */
183  if (arg->type == FR_TYPE_STRING) {
184  if (strcmp(arg->vb_strvalue, "request") == 0) {
185  return date_encode_strftime(ctx, out, inst, request, inst->fmt,
186  fr_time_to_sec(request->packet->timestamp));
187  }
188 
189  if (strcmp(arg->vb_strvalue, "now") == 0) {
190  now:
191  return date_encode_strftime(ctx, out, inst, request, inst->fmt, fr_time_to_sec(fr_time()));
192  }
193 
194  /*
195  * %date('+%A') == "Monday", to mirror the behavior of the `date` command.
196  */
197  if (arg->vb_strvalue[0] == '+') {
198  return date_encode_strftime(ctx, out, inst, request, arg->vb_strvalue + 1, fr_time_to_sec(fr_time()));
199  }
200  }
201 
202  switch (arg->type) {
203  /*
204  * These are 'to' types, i.e. we'll convert the integers
205  * to a time structure, and then output it in the specified
206  * format as a string.
207  */
208  case FR_TYPE_DATE:
209  return date_encode_strftime(ctx, out, inst, request, inst->fmt, fr_unix_time_to_sec(arg->vb_date));
210 
211  case FR_TYPE_UINT32:
212  return date_encode_strftime(ctx, out, inst, request, inst->fmt, (time_t) arg->vb_uint32);
213 
214  case FR_TYPE_UINT64:
215  return date_encode_strftime(ctx, out, inst, request, inst->fmt, (time_t) arg->vb_uint64);
216 
217  /*
218  * These are 'from' types, i.e. we'll convert the input string
219  * into a time structure, and then output it as an integer
220  * unix timestamp.
221  */
222  case FR_TYPE_STRING:
223  return date_convert_string(ctx, out, request, arg->vb_strvalue, inst);
224 
225  default:
226  REDEBUG("Can't convert type %s into date", fr_type_to_str(arg->type));
227  }
228 
229  return XLAT_ACTION_FAIL;
230 }
231 
232 static int mod_bootstrap(module_inst_ctx_t const *mctx)
233 {
234  xlat_t *xlat;
235 
236  xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, xlat_date_convert, FR_TYPE_VOID);
238 
239  return 0;
240 }
241 
242 extern module_rlm_t rlm_date;
244  .common = {
245  .magic = MODULE_MAGIC_INIT,
246  .name = "date",
247  .inst_size = sizeof(rlm_date_t),
249  .bootstrap = mod_bootstrap
250  }
251 };
static int const char * fmt
Definition: acutest.h:573
#define DIAG_ON(_x)
Definition: build.h:421
#define DIAG_OFF(_x)
Definition: build.h:420
#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
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
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:168
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
module_instance_t const * mi
Instance of the module being instantiated.
Definition: module_ctx.h:42
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition: module_rlm.c:257
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:39
static const conf_parser_t config[]
Definition: base.c:188
#define REDEBUG(fmt,...)
Definition: radclient.h:52
char const * fmt
Definition: rlm_date.c:33
module_rlm_t rlm_date
Definition: rlm_date.c:243
static xlat_arg_parser_t const xlat_date_convert_args[]
Definition: rlm_date.c:138
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition: rlm_date.c:232
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:109
bool utc
Definition: rlm_date.c:34
static const conf_parser_t module_config[]
Definition: rlm_date.c:37
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:44
void * data
Module's instance data.
Definition: module.h:271
void * boot
Data allocated during the boostrap phase.
Definition: module.h:274
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:731
static int64_t fr_unix_time_to_sec(fr_unix_time_t delta)
Definition: time.h:506
static fr_unix_time_t fr_unix_time_from_sec(int64_t sec)
Definition: time.h:449
bool required
Argument must be present, and non-empty.
Definition: xlat.h:145
#define XLAT_ARG_PARSER_TERMINATOR
Definition: xlat.h:165
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:144
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:52
An xlat calling ctx.
Definition: xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition: xlat_func.c:365