@@ -279,15 +279,19 @@ static inline void xlat_debug_log_result(request_t *request, xlat_exp_t const *n
279279 * @param[in] request currently being processed
280280 * @param[in] name of the function being called
281281 * @param[in] arg specification of current argument
282+ * @param[in] node expansion for the current argument
282283 * @param[in] arg_num number of current argument in the argument specifications
283284 * @return
284285 * - XLAT_ACTION_DONE on success.
285286 * - XLAT_ACTION_FAIL on failure.
286287 */
287288static xlat_action_t xlat_process_arg_list (TALLOC_CTX * ctx , fr_value_box_list_t * list , request_t * request ,
288- char const * name , xlat_arg_parser_t const * arg , unsigned int arg_num )
289+ char const * name , xlat_arg_parser_t const * arg , xlat_exp_t const * node , unsigned int arg_num )
289290{
290291 fr_value_box_t * vb ;
292+ bool concat = false;
293+ bool quoted = false;
294+ fr_type_t type ;
291295
292296 /*
293297 * The funtion may be URI or SQL, which have different sub-types. So we call the function if it
@@ -311,36 +315,84 @@ do { \
311315 } \
312316} while (0)
313317
318+ /*
319+ * See if we have to concatenate the results.
320+ *
321+ * If the input xlat is more complicated expression, it's going to be a function, e.g.
322+ *
323+ * 1+2 --> %op_add(1,2).
324+ *
325+ * And then we can't do escaping. Note that this is also the case for
326+ *
327+ * "foo" + User-Name --> %op_add("foo", User-Name)
328+ *
329+ * Arguably, we DO want to escape User-Name, but not Foo. Because "+" here is a special case. :(
330+ */
331+ if ((fr_dlist_num_elements (& node -> group -> dlist ) == 1 ) && (xlat_exp_head (node -> group )-> quote != T_BARE_WORD )) {
332+ quoted = concat = true;
333+ type = FR_TYPE_STRING ;
334+
335+ } else {
336+ concat = arg -> concat ;
337+ type = arg -> type ;
338+ }
339+
314340 if (fr_value_box_list_empty (list )) {
341+ /*
342+ * The expansion resulted in no data, BUT the admin wants a string. So we create an
343+ * empty string.
344+ *
345+ * i.e. If attribute 'foo' doesn't exist, then we have:
346+ *
347+ * %{foo} --> nothing, because 'foo' doesn't exist
348+ * "%{foo}" --> "", because we want a string, therefore the contents of the string are nothing.
349+ */
350+ if (quoted ) {
351+ MEM (vb = fr_value_box_alloc (ctx , FR_TYPE_STRING , NULL ));
352+ fr_value_box_strdup (vb , vb , NULL , "" , false);
353+ fr_value_box_list_insert_tail (list , vb );
354+
355+ return XLAT_ACTION_DONE ;
356+ }
357+
315358 if (arg -> required ) {
316359 REDEBUG ("Function \"%s\" is missing required argument %u" , name , arg_num );
317360 return XLAT_ACTION_FAIL ;
318361 }
362+
319363 return XLAT_ACTION_DONE ;
320364 }
321365
322366 vb = fr_value_box_list_head (list );
367+ fr_assert (node -> type == XLAT_GROUP );
323368
324369 /*
325370 * Concatenate child boxes, casting to desired type,
326371 * then replace group vb with first child vb
327372 */
328- if (arg -> concat ) {
373+ if (concat ) {
329374 if (arg -> func ) {
330375 do ESCAPE (arg , vb , arg_num ); while ((vb = fr_value_box_list_next (list , vb )));
331376
332377 vb = fr_value_box_list_head (list ); /* Reset */
333378 }
334379
335380 if (fr_value_box_list_concat_in_place (ctx ,
336- vb , list , arg -> type ,
381+ vb , list , type ,
337382 FR_VALUE_BOX_LIST_FREE , true,
338383 SIZE_MAX ) < 0 ) {
339- RPEDEBUG ("Function \"%s\" failed concatenating arguments to type %s" , name , fr_type_to_str (arg -> type ));
384+ RPEDEBUG ("Function \"%s\" failed concatenating arguments to type %s" , name , fr_type_to_str (type ));
340385 return XLAT_ACTION_FAIL ;
341386 }
342387 fr_assert (fr_value_box_list_num_elements (list ) <= 1 );
343388
389+ /*
390+ * Cast to the type we actually want.
391+ */
392+ if ((arg -> type != FR_TYPE_VOID ) && (arg -> type != type )) {
393+ goto do_cast ;
394+ }
395+
344396 return XLAT_ACTION_DONE ;
345397 }
346398
@@ -361,9 +413,10 @@ do { \
361413 ESCAPE (arg , vb , arg_num );
362414
363415 if ((arg -> type != FR_TYPE_VOID ) && (vb -> type != arg -> type )) {
364- cast_error :
416+ do_cast :
365417 if (fr_value_box_cast_in_place (ctx , vb ,
366418 arg -> type , NULL ) < 0 ) {
419+ cast_error :
367420 RPEDEBUG ("Function \"%s\" failed to cast argument %u to type %s" , name , arg_num , fr_type_to_str (arg -> type ));
368421 return XLAT_ACTION_FAIL ;
369422 }
@@ -385,7 +438,7 @@ do { \
385438 } while ((vb = fr_value_box_list_next (list , vb )));
386439
387440 /*
388- * If it's not a void type we still need to escape the values
441+ * If it's a void type we still need to escape the values
389442 */
390443 } else if (arg -> func ) {
391444 do ESCAPE (arg , vb , arg_num ); while ((vb = fr_value_box_list_next (list , vb )));
@@ -408,15 +461,17 @@ do { \
408461 * List will be modified in accordance to rules
409462 * provided in the args array.
410463 * @param[in] request being processed.
411- * @param[in] func to call
464+ * @param[in] node which is a function
412465 */
413466static inline CC_HINT (always_inline )
414467xlat_action_t xlat_process_args (TALLOC_CTX * ctx , fr_value_box_list_t * list ,
415- request_t * request , xlat_t const * func )
468+ request_t * request , xlat_exp_t const * node )
416469{
470+ xlat_t const * func = node -> call .func ;
417471 xlat_arg_parser_t const * arg_p = func -> args ;
472+ xlat_exp_t * arg , * arg_next ;
418473 xlat_action_t xa ;
419- fr_value_box_t * vb , * next ;
474+ fr_value_box_t * vb , * vb_next ;
420475
421476 /*
422477 * No args registered for this xlat
@@ -435,6 +490,8 @@ xlat_action_t xlat_process_args(TALLOC_CTX *ctx, fr_value_box_list_t *list,
435490 */
436491 case XLAT_INPUT_ARGS :
437492 vb = fr_value_box_list_head (list );
493+ arg = xlat_exp_head (node -> call .args );
494+
438495 while (arg_p -> type != FR_TYPE_NULL ) {
439496 /*
440497 * Separate check to see if the group
@@ -467,8 +524,10 @@ xlat_action_t xlat_process_args(TALLOC_CTX *ctx, fr_value_box_list_t *list,
467524 * pre-advance, in case the vb is replaced
468525 * during processing.
469526 */
470- next = fr_value_box_list_next (list , vb );
471- xa = xlat_process_arg_list (ctx , & vb -> vb_group , request , func -> name , arg_p ,
527+ vb_next = fr_value_box_list_next (list , vb );
528+ arg_next = xlat_exp_next (node -> call .args , arg );
529+
530+ xa = xlat_process_arg_list (ctx , & vb -> vb_group , request , func -> name , arg_p , arg ,
472531 (unsigned int )((arg_p - func -> args ) + 1 ));
473532 if (xa != XLAT_ACTION_DONE ) return xa ;
474533
@@ -550,11 +609,12 @@ xlat_action_t xlat_process_args(TALLOC_CTX *ctx, fr_value_box_list_t *list,
550609
551610 do_next :
552611 if (arg_p -> variadic ) {
553- if (!next ) break ;
612+ if (!vb_next ) break ;
554613 } else {
555614 arg_p ++ ;
615+ arg = arg_next ;
556616 }
557- vb = next ;
617+ vb = vb_next ;
558618 }
559619 break ;
560620 }
@@ -983,7 +1043,7 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_dcursor_t *out,
9831043 * the async function mucks with it.
9841044 */
9851045 if (RDEBUG_ENABLED2 ) fr_value_box_list_acopy (unlang_interpret_frame_talloc_ctx (request ), & result_copy , result );
986- xa = xlat_process_args (ctx , result , request , node -> call . func );
1046+ xa = xlat_process_args (ctx , result , request , node );
9871047 if (xa == XLAT_ACTION_FAIL ) {
9881048 fr_value_box_list_talloc_free (& result_copy );
9891049 return xa ;
@@ -1053,9 +1113,6 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_dcursor_t *out,
10531113 fr_value_box_list_move (& arg -> vb_group , result );
10541114 }
10551115
1056- // xlat_debug_log_expansion(request, *in, NULL, __LINE__);
1057- // xlat_debug_log_result(request, *in, arg);
1058-
10591116 VALUE_BOX_VERIFY (arg );
10601117
10611118 fr_dcursor_insert (out , arg );
0 commit comments