Coverage Report

Created: 2026-08-08 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-var-expand/expansion-program.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "array.h"
5
#include "str.h"
6
#include "strescape.h"
7
#include "hex-binary.h"
8
#include "var-expand-private.h"
9
#include "var-expand-parser-private.h"
10
#include "var-expand-parser.h"
11
#include "expansion.h"
12
13
0
#define MAX_PROGRAM_SIZE 8192
14
15
extern void var_expand_parser_lex_init_extra(void*, void*);
16
17
static const struct var_expand_params empty_params = {
18
};
19
20
int var_expand_program_create(const char *str,
21
            struct var_expand_program **program_r,
22
            const char **error_r)
23
0
{
24
0
  int ret;
25
0
  struct var_expand_parser_state state;
26
0
  i_zero(&state);
27
28
0
  if (strlen(str) > MAX_PROGRAM_SIZE) {
29
0
    *error_r = t_strdup_printf("Program size exceeds maximum of %d bytes",
30
0
             MAX_PROGRAM_SIZE);
31
0
    return -1;
32
0
  }
33
34
0
  pool_t pool =
35
0
    pool_alloconly_create(MEMPOOL_GROWING"var expand program", 1024);
36
0
  state.p = state.plist = p_new(pool, struct var_expand_program, 1);
37
0
  state.p->pool = pool;
38
0
  p_array_init(&state.variables, pool, 1);
39
40
0
  T_BEGIN {
41
0
    state.str = NULL;
42
0
    state.pool =
43
0
      pool_allocfree_create("var expand parser");
44
0
    p_array_init(&state.variables, pool, 1);
45
0
    state.input = str;
46
0
    state.left = strlen(str);
47
0
    var_expand_parser_lex_init_extra(&state, &state.scanner);
48
    /* 0 = OK, everything else = something went wrong */
49
0
    ret = var_expand_parser_parse(&state);
50
0
    state.error = t_strdup(state.error);
51
0
  } T_END_PASS_STR_IF(ret != 0, &state.error);
52
53
0
  array_append_space(&state.variables);
54
0
  state.plist->variables = array_front(&state.variables);
55
0
  i_assert(state.plist->variables != NULL);
56
0
  pool_unref(&state.pool);
57
58
0
  if (ret != 0) {
59
0
    *error_r = state.error;
60
0
    var_expand_program_free(&state.plist);
61
0
  } else {
62
0
    *program_r = state.plist;
63
0
  }
64
0
  i_assert(ret == 0 || *error_r != NULL);
65
66
0
  return ret == 0 ? 0 : -1;
67
0
}
68
69
void var_expand_program_dump(const struct var_expand_program *prog, string_t *dest)
70
0
{
71
0
  while (prog != NULL) {
72
0
    struct var_expand_statement *stmt = prog->first;
73
0
    while (stmt != NULL) {
74
0
      const char *or_var = "";
75
0
      if (stmt == prog->first && !prog->only_literal)
76
0
        or_var = " or variable";
77
0
      str_printfa(dest, "function%s %s\n", or_var, stmt->function);
78
0
      struct var_expand_parameter_iter_context *ctx =
79
0
        var_expand_parameter_iter_init(stmt);
80
0
      while (var_expand_parameter_iter_more(ctx)) {
81
0
        const struct var_expand_parameter *par =
82
0
          var_expand_parameter_iter_next(ctx);
83
0
        var_expand_parameter_dump(dest, par);
84
0
      }
85
0
      stmt = stmt->next;
86
0
    }
87
0
    prog = prog->next;
88
0
  }
89
0
}
90
91
static void prepare_state(const struct var_expand_params *params,
92
        struct var_expand_state *state_r)
93
0
{
94
0
  i_zero(state_r);
95
0
  i_assert(params != NULL);
96
97
0
  i_assert((params->table == NULL && params->tables_arr == NULL) ||
98
0
     (params->table != NULL && params->tables_arr == NULL) ||
99
0
     (params->table == NULL && params->tables_arr != NULL));
100
101
0
  i_assert((params->providers == NULL && params->providers_arr == NULL) ||
102
0
     (params->providers != NULL && params->providers_arr == NULL) ||
103
0
     (params->providers == NULL && params->providers_arr != NULL));
104
105
0
  size_t num_tables = 0;
106
0
  if (params->tables_arr != NULL) {
107
0
    while (params->tables_arr[num_tables] != NULL)
108
0
      num_tables++;
109
0
  }
110
0
  size_t num_providers = 0;
111
0
  if (params->providers_arr != NULL) {
112
0
    while (params->providers_arr[num_providers] != NULL)
113
0
         num_providers++;
114
0
  }
115
0
  size_t num_contexts = I_MAX(num_tables, num_providers);
116
117
  /* ensure contexts are properly terminated. */
118
0
  i_assert(params->contexts == NULL ||
119
0
     params->contexts[num_contexts] == var_expand_contexts_end);
120
121
0
  state_r->params = params;
122
0
  state_r->result = str_new(default_pool, 32);
123
0
  state_r->transfer = str_new(default_pool, 32);
124
0
}
125
126
static int
127
var_expand_program_execute_one_real(const struct var_expand_program *program,
128
            const struct var_expand_params *params,
129
            struct var_expand_state *state,
130
            const char **error_r)
131
0
{
132
0
  int ret = 0;
133
0
  const struct var_expand_statement *stmt = program->first;
134
0
  if (stmt == NULL) {
135
    /* skip empty programs */
136
0
    return 0;
137
0
  }
138
0
  T_BEGIN {
139
0
    while (stmt != NULL) {
140
0
      bool first = stmt == program->first;
141
0
      if (!var_expand_execute_stmt(state, stmt,
142
0
                 first, error_r)) {
143
0
        ret = -1;
144
0
        break;
145
0
      }
146
0
      stmt = stmt->next;
147
0
    }
148
0
  } T_END_PASS_STR_IF(ret < 0, error_r);
149
0
  if (ret < 0)
150
0
    return ret;
151
0
  if (state->transfer_binary)
152
0
    var_expand_state_set_transfer(state,
153
0
        binary_to_hex(state->transfer->data, state->transfer->used));
154
0
  if (state->transfer_set) {
155
0
    if (!program->only_literal && params->escape_func != NULL &&
156
0
        !state->transfer_safe) {
157
0
      str_append(state->result, params->escape_func(str_c(state->transfer),
158
0
                      params->escape_context));
159
0
      } else
160
0
        str_append_str(state->result, state->transfer);
161
0
  } else {
162
0
    *error_r = t_strdup(state->delayed_error);
163
0
    ret = -1;
164
0
  }
165
0
  var_expand_state_unset_transfer(state);
166
0
  return ret;
167
0
}
168
169
int var_expand_program_execute_one(string_t *dest, const struct var_expand_program *program,
170
           const struct var_expand_params *params, const char **error_r)
171
0
{
172
0
  int ret = 0;
173
0
  struct var_expand_state state;
174
0
  if (params == NULL)
175
0
    params = &empty_params;
176
0
  prepare_state(params, &state);
177
178
0
  *error_r = NULL;
179
180
0
  ret = var_expand_program_execute_one_real(program, params, &state, error_r);
181
182
0
  if (state.delayed_error != NULL) {
183
0
    *error_r = t_strdup(state.delayed_error);
184
0
    ret = -1;
185
0
  }
186
0
  str_free(&state.transfer);
187
0
  i_free(state.delayed_error);
188
  /* only write to dest on success */
189
0
  if (ret == 0)
190
0
    str_append_str(dest, state.result);
191
0
  str_free(&state.result);
192
0
  i_assert(ret == 0 || *error_r != NULL);
193
194
0
  return ret;
195
0
}
196
197
int var_expand_program_execute(string_t *dest, const struct var_expand_program *program,
198
             const struct var_expand_params *params, const char **error_r)
199
0
 {
200
0
  int ret = 0;
201
0
  struct var_expand_state state;
202
0
  if (params == NULL)
203
0
    params = &empty_params;
204
0
  prepare_state(params, &state);
205
206
0
  *error_r = NULL;
207
208
0
  while (program != NULL) {
209
0
    ret = var_expand_program_execute_one_real(program, params, &state, error_r);
210
0
    if (ret == -1)
211
0
      break;
212
0
    program = program->next;
213
0
  };
214
0
  if (state.delayed_error != NULL) {
215
0
    *error_r = t_strdup(state.delayed_error);
216
0
    ret = -1;
217
0
  }
218
0
  str_free(&state.transfer);
219
0
  i_free(state.delayed_error);
220
  /* only write to dest on success */
221
0
  if (ret == 0)
222
0
    str_append_str(dest, state.result);
223
0
  str_free(&state.result);
224
0
  i_assert(ret == 0 || *error_r != NULL);
225
226
0
  return ret;
227
0
}
228
229
const char *const *
230
var_expand_program_variables(const struct var_expand_program *program)
231
0
{
232
0
  return program->variables;
233
0
}
234
235
bool var_expand_program_has_variable(const struct var_expand_program *program,
236
             const char *variable, bool first_program_only)
237
0
{
238
0
  if (!first_program_only)
239
0
    return str_array_find(var_expand_program_variables(program), variable);
240
241
0
  const struct var_expand_statement *stmt = program->first;
242
0
  if (stmt->params == NULL && strcmp(stmt->function, variable) == 0)
243
0
    return TRUE;
244
0
  while (stmt != NULL) {
245
0
    const struct var_expand_parameter *par = stmt->params;
246
0
    while (par != NULL) {
247
0
      if (par->value_type == VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE &&
248
0
          strcmp(par->value.str, variable) == 0)
249
0
        return TRUE;
250
0
      par = par->next;
251
0
    }
252
0
    stmt = stmt->next;
253
0
  }
254
0
  return FALSE;
255
0
}
256
257
void var_expand_program_free(struct var_expand_program **_program)
258
0
{
259
0
  struct var_expand_program *program = *_program;
260
0
  if (program == NULL)
261
0
    return;
262
0
  *_program = NULL;
263
264
0
  pool_unref(&program->pool);
265
0
}
266
267
/* Export code */
268
269
/* Encodes numbers in 7-bit bytes, using 8th to indicate
270
   that the number continues. Uses little endian encoding
271
   to allow this. */
272
static void export_number(string_t *dest, intmax_t number)
273
0
{
274
0
  unsigned char b;
275
276
  /* fast path - store any non-negative number that's smaller than
277
     127 as number + 1, including 0. */
278
0
  if (number >= 0 && number < 0x7f) {
279
0
    b = number + 1;
280
0
    str_append_c(dest, b);
281
0
    return;
282
0
  }
283
284
  /* Store sign with 0x80, so we can differentiate
285
     from fast path. */
286
0
  if (number < 0) {
287
0
    str_append_c(dest, 0x80 | '-');
288
0
    number = -number;
289
0
  } else
290
0
    str_append_c(dest, 0x80 | '+');
291
292
  /* Store the number in 7 byte chunks
293
     so we can use the 8th bit for indicating
294
     whether the number continues. */
295
0
  while (number > 0) {
296
0
    if (number > 0x7f)
297
0
      b = 0x80;
298
0
    else
299
0
      b = 0x0;
300
0
    b |= number & 0x7f;
301
0
    number >>= 7;
302
0
    str_append_c(dest, b);
303
0
  }
304
0
}
305
306
static void var_expand_program_export_one(const struct var_expand_program *program,
307
            string_t *dest)
308
0
{
309
0
  const struct var_expand_statement *stmt = program->first;
310
0
  while (stmt != NULL) {
311
0
    str_append(dest, stmt->function);
312
0
    str_append_c(dest, '\1');
313
0
    const struct var_expand_parameter *param = stmt->params;
314
0
    while (param != NULL) {
315
0
      if (param->key != NULL)
316
0
        str_append(dest, param->key);
317
0
      str_append_c(dest, '\1');
318
0
      switch (param->value_type) {
319
0
      case VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING:
320
0
        str_append_c(dest, 's');
321
0
        str_append_tabescaped(dest, param->value.str);
322
0
        str_append_c(dest, '\r');
323
0
        break;
324
0
      case VAR_EXPAND_PARAMETER_VALUE_TYPE_INT:
325
0
        str_append_c(dest, 'i');
326
0
        export_number(dest, param->value.num);
327
0
        break;
328
0
      case VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE:
329
0
        str_append_c(dest, 'v');
330
0
        str_append_tabescaped(dest, param->value.str);
331
0
        str_append_c(dest, '\r');
332
0
        break;
333
0
      default:
334
0
        i_unreached();
335
0
      }
336
0
      param = param->next;
337
0
      if (param != NULL)
338
0
        str_append_c(dest, '\1');
339
0
    }
340
0
    str_append_c(dest, '\t');
341
0
    stmt = stmt->next;
342
0
    if (stmt != NULL)
343
0
      str_append_c(dest, '\1');
344
0
    else
345
0
      str_append_c(dest, '\t');
346
0
  }
347
0
  const char *const *vars = program->variables;
348
349
0
  for (; vars != NULL && *vars != NULL; vars++) {
350
    /* ensure variable has no \1 in name */
351
0
    i_assert(strchr(*vars, '\1') == NULL);
352
0
    str_append(dest, *vars);
353
0
    str_append_c(dest, '\1');
354
0
  }
355
0
  str_append_c(dest, '\t');
356
0
}
357
358
void var_expand_program_export_append(string_t *dest,
359
              const struct var_expand_program *program)
360
0
{
361
0
  i_assert(program != NULL);
362
0
  i_assert(dest != NULL);
363
364
0
  while (program != NULL) {
365
0
    if (program->only_literal) {
366
0
      i_assert(program->first->params->value_type ==
367
0
         VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING);
368
0
      str_append_c(dest, '\1');
369
0
      str_append_tabescaped(dest, program->first->params->value.str);
370
0
      str_append_c(dest, '\r');
371
0
    } else {
372
0
      str_append_c(dest, '\2');
373
0
      var_expand_program_export_one(program, dest);
374
0
    }
375
376
0
    program = program->next;
377
0
  }
378
0
}
379
380
const char *var_expand_program_export(const struct var_expand_program *program)
381
0
{
382
0
  string_t *dest = t_str_new(64);
383
0
  var_expand_program_export_append(dest, program);
384
0
  return str_c(dest);
385
0
}
386
387
static void
388
var_expand_program_to_string_calculate(const struct var_expand_statement *stmt,
389
               string_t *dest)
390
0
{
391
0
  if (str_len(dest) > 0)
392
0
    str_append_c(dest, ' ');
393
0
  switch (stmt->params->value.num) {
394
0
  case VAR_EXPAND_STATEMENT_OPER_PLUS:
395
0
    str_append_c(dest, '+');
396
0
    break;
397
0
  case VAR_EXPAND_STATEMENT_OPER_MINUS:
398
0
    str_append_c(dest, '-');
399
0
    break;
400
0
  case VAR_EXPAND_STATEMENT_OPER_STAR:
401
0
    str_append_c(dest, '*');
402
0
    break;
403
0
  case VAR_EXPAND_STATEMENT_OPER_SLASH:
404
0
    str_append_c(dest, '/');
405
0
    break;
406
0
  case VAR_EXPAND_STATEMENT_OPER_MODULO:
407
0
    str_append_c(dest, '%');
408
0
    break;
409
0
  case VAR_EXPAND_STATEMENT_OPER_COUNT:
410
0
  default:
411
0
    i_unreached();
412
0
  }
413
0
  str_append_c(dest, ' ');
414
0
  str_printfa(dest, "%jd", stmt->params->next->value.num);
415
0
}
416
417
void
418
var_expand_program_to_string_append_one(string_t *dest,
419
          const struct var_expand_program *program)
420
0
{
421
0
  if (program->only_literal) {
422
0
    i_assert(program->first->params->value_type ==
423
0
       VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING);
424
0
    str_append(dest, program->first->params->value.str);
425
0
    return;
426
0
  }
427
0
  str_append(dest, "%{");
428
0
  const struct var_expand_statement *stmt = program->first;
429
0
  while (stmt != NULL) {
430
0
    bool braces = FALSE;
431
0
    if (strcmp(stmt->function, "calculate") == 0) {
432
0
      var_expand_program_to_string_calculate(stmt, dest);
433
0
      goto next;
434
0
    }
435
0
    str_append(dest, stmt->function);
436
0
    const struct var_expand_parameter *param = stmt->params;
437
0
    if (param != NULL) {
438
0
      str_append_c(dest, '(');
439
0
      braces = TRUE;
440
0
    }
441
0
    while (param != NULL) {
442
0
      if (param->key != NULL) {
443
0
        str_append(dest, param->key);
444
0
        str_append_c(dest, '=');
445
0
      }
446
0
      switch (param->value_type) {
447
0
      case VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING:
448
0
        str_append_c(dest, '\'');
449
0
        str_append_escaped(dest, param->value.str,
450
0
               strlen(param->value.str));
451
0
        str_append_c(dest, '\'');
452
0
        break;
453
0
      case VAR_EXPAND_PARAMETER_VALUE_TYPE_INT:
454
0
        str_printfa(dest, "%jd", param->value.num);
455
0
        break;
456
0
      case VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE:
457
0
        str_append(dest, param->value.str);
458
0
        break;
459
0
      default:
460
0
        i_unreached();
461
0
      }
462
0
      param = param->next;
463
0
      if (param != NULL)
464
0
        str_append(dest, ", ");
465
0
    }
466
0
    if (braces)
467
0
      str_append_c(dest, ')');
468
0
next:   stmt = stmt->next;
469
0
    if (stmt != NULL && strcmp(stmt->function, "calculate") != 0)
470
0
      str_append(dest, " | ");
471
0
  }
472
0
  str_append_c(dest, '}');
473
0
}
474
475
void var_expand_program_to_string_append(string_t *dest,
476
           const struct var_expand_program *program)
477
0
{
478
0
  i_assert(program != NULL);
479
0
  i_assert(dest != NULL);
480
481
0
  while (program != NULL) {
482
0
    var_expand_program_to_string_append_one(dest, program);
483
0
    program = program->next;
484
0
  }
485
0
}
486
487
const char *var_expand_program_to_string_one(const struct var_expand_program *program)
488
0
{
489
0
  string_t *dest = t_str_new(64);
490
0
  var_expand_program_to_string_append_one(dest, program);
491
0
  return str_c(dest);
492
0
}
493
494
const char *var_expand_program_to_string(const struct var_expand_program *program)
495
0
{
496
0
  string_t *dest = t_str_new(64);
497
0
  var_expand_program_to_string_append(dest, program);
498
0
  return str_c(dest);
499
0
}
500
501
/* Import code */
502
503
static int extract_name(pool_t pool, const char *data, size_t size,
504
      const char **value_r, const char **error_r)
505
0
{
506
0
  if (size == 0) {
507
0
    *error_r = "Missing end of name";
508
0
    return -1;
509
0
  }
510
511
0
  const char *ptr = memchr(data, '\1', size);
512
0
  if (ptr == NULL) {
513
0
    *error_r = "Missing end of name";
514
0
    return -1;
515
0
  }
516
0
  size_t len = ptr - data;
517
0
  if (len == 0) {
518
0
    *value_r = NULL;
519
0
    return 1;
520
0
  }
521
0
  *value_r = p_strdup_until(pool, data, ptr);
522
0
  return len + 1;
523
0
}
524
525
static int extract_value(pool_t pool, const char *data, size_t size,
526
       const char **value_r, const char **error_r)
527
0
{
528
0
  if (size == 0) {
529
0
    *error_r = "Missing end of string";
530
0
    return -1;
531
0
  }
532
533
0
  const char *ptr = memchr(data, '\r', size);
534
0
  if (ptr == NULL) {
535
0
    *error_r = "Missing end of string";
536
0
    return -1;
537
0
  }
538
0
  size_t len = ptr - data;
539
0
  string_t *unescaped = str_new(pool, len);
540
0
  str_append_tabunescaped(unescaped, data, len);
541
0
  *value_r = str_c(unescaped);
542
  /* make sure we end up in right place. */
543
0
  return len + 1;
544
0
}
545
546
static int extract_number(const char *data, size_t size, intmax_t *value_r,
547
        const char **error_r)
548
0
{
549
0
  if (size == 0) {
550
0
    *error_r = "Too short number";
551
0
    return -1;
552
0
  }
553
554
0
  const unsigned char *ptr = (const unsigned char*)data;
555
0
  bool negative;
556
0
  size_t len = 1;
557
558
0
  if ((*ptr & 0x80) == 0) {
559
    /* fast path for small positive number */
560
0
    intmax_t number = *ptr;
561
0
    *value_r = number - 1;
562
0
    return 1;
563
0
  }
564
565
0
  if (size < 2) {
566
0
    *error_r = "Too short number";
567
0
    return -1;
568
0
  }
569
570
0
  const char sign = *ptr - 0x80;
571
0
  if (sign == '+') {
572
0
    negative = FALSE;
573
0
  } else if (sign == '-') {
574
0
    negative = TRUE;
575
0
  } else {
576
0
    *error_r = "Unknown number";
577
0
    return -1;
578
0
  }
579
0
  size--;
580
0
  ptr++;
581
582
0
  intmax_t value = 0;
583
0
  intmax_t shift = 0;
584
0
  size_t max_size = I_MIN(size, 9);
585
586
  /* a number can be at most 9 bytes */
587
0
  for (size_t i = 0; i < max_size; i++) {
588
0
    len++;
589
0
    value |= ((*(ptr) & 0x7fLL) << shift);
590
    /* if high byte is set, the number continues */
591
0
    if ((*ptr & 0x80) == 0)
592
0
      break;
593
0
    shift += 7;
594
0
    ptr++;
595
0
    size--;
596
0
  }
597
598
0
  if (size > 0 && (*ptr & 0x80) != 0) {
599
0
    *error_r = "Unfinished number";
600
0
    return -1;
601
0
  }
602
603
0
  if (negative)
604
0
    value = -value;
605
606
0
  *value_r = value;
607
608
0
  return len;
609
0
}
610
611
#define ADVANCE_INPUT(count) \
612
0
  if (unlikely(size < (size_t)count)) { \
613
0
    *error_r = "Premature end of data"; \
614
0
    return -1; \
615
0
  }\
616
0
  data = data + (count); \
617
0
  size = size - (size_t)(count);
618
619
static int var_expand_program_import_stmt(const char *data, size_t size,
620
            struct var_expand_program *program,
621
            const char **error_r)
622
0
{
623
0
  const char *name;
624
0
  const char *value;
625
0
  size_t orig_size = size;
626
627
  /* normal program, starts with filter name */
628
0
  int ret = extract_name(program->pool, data, size, &name, error_r);
629
0
  if (ret < 0)
630
0
    return -1;
631
0
  if (name == NULL) {
632
0
    *error_r = "missing function name";
633
0
    return -1;
634
0
  }
635
0
  ADVANCE_INPUT(ret);
636
637
0
  struct var_expand_statement *stmt =
638
0
    p_new(program->pool, struct var_expand_statement, 1);
639
640
0
  if (program->first == NULL)
641
0
    program->first = stmt;
642
0
  else {
643
0
     struct var_expand_statement *ptr = program->first;
644
0
     while (ptr->next != NULL) ptr = ptr->next;
645
0
     ptr->next = stmt;
646
0
  }
647
648
0
  stmt->function = name;
649
0
  struct var_expand_parameter *prev = NULL;
650
0
  int idx = -1;
651
652
0
  while (size > 0 && *data != '\t') {
653
0
    struct var_expand_parameter *param =
654
0
      p_new(program->pool, struct var_expand_parameter, 1);
655
    /* check if it's named parameter */
656
0
    if (*data == '\1') {
657
0
      param->idx = ++idx;
658
0
      ADVANCE_INPUT(1);
659
0
    } else {
660
0
      ret = extract_name(program->pool, data, size,
661
0
             &name, error_r);
662
0
      if (ret < 0)
663
0
        return -1;
664
0
      ADVANCE_INPUT(ret);
665
0
      param->key = name;
666
0
    }
667
668
0
    if (size < 1) {
669
0
      *error_r = "Premature end of data";
670
0
      return -1;
671
0
    }
672
673
    /* check the parameter type */
674
0
    switch (*data) {
675
0
    case 's':
676
0
      param->value_type =
677
0
        VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING;
678
0
      break;
679
0
    case 'i':
680
0
      param->value_type =
681
0
        VAR_EXPAND_PARAMETER_VALUE_TYPE_INT;
682
0
      break;
683
0
    case 'v':
684
0
      param->value_type =
685
0
        VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE;
686
0
      break;
687
0
    default:
688
0
      *error_r = "Unsupported parameter type";
689
0
      return -1;
690
0
    }
691
0
    ADVANCE_INPUT(1);
692
693
0
    if (size == 0) {
694
0
      *error_r = "Premature end of data";
695
0
      return -1;
696
0
    }
697
698
0
    if (param->value_type == VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING ||
699
0
        param->value_type == VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE) {
700
0
      ret = extract_value(program->pool, data, size,
701
0
              &value, error_r);
702
0
      if (ret < 0)
703
0
        return -1;
704
0
      ADVANCE_INPUT(ret);
705
0
      param->value.str = value;
706
0
    } else if (param->value_type == VAR_EXPAND_PARAMETER_VALUE_TYPE_INT) {
707
0
      ret = extract_number(data, size, &param->value.num,
708
0
               error_r);
709
0
      if (ret < 0)
710
0
        return -1;
711
712
0
      ADVANCE_INPUT(ret);
713
0
    } else {
714
0
      *error_r = "Unsupported value type";
715
0
      return -1;
716
0
    }
717
718
0
    if (prev == NULL)
719
0
      stmt->params = param;
720
0
    else
721
0
      prev->next = param;
722
0
    prev = param;
723
724
0
    if (size > 0 && *data == '\t') {
725
0
      break;
726
0
    } else if (size > 0 && *data == '\1') {
727
0
      ADVANCE_INPUT(1);
728
0
    } else {
729
0
      *error_r = "Missing parameter end";
730
0
      return -1;
731
0
    }
732
0
  }
733
734
0
  if (size < 1 || *data != '\t')
735
0
    *error_r = "Missing parameter statement end";
736
737
0
  ADVANCE_INPUT(1);
738
739
0
  return orig_size - size;
740
0
}
741
742
static int var_expand_program_import_one(const char **_data, size_t *_size,
743
           struct var_expand_program *program,
744
           const char **error_r)
745
0
{
746
0
  const char *data = *_data;
747
0
  size_t size = *_size;
748
0
  const char *value;
749
0
  int ret;
750
751
  /* Only literal */
752
0
  if (*data == '\1') {
753
0
    ADVANCE_INPUT(1);
754
0
    ret = extract_value(program->pool, data, size, &value, error_r);
755
0
    if (ret < 0)
756
0
      return -1;
757
0
    ADVANCE_INPUT(ret);
758
759
    /* just literal data */
760
0
    struct var_expand_statement *stmt =
761
0
      p_new(program->pool, struct var_expand_statement, 1);
762
0
    struct var_expand_parameter *param =
763
0
      p_new(program->pool, struct var_expand_parameter, 1);
764
0
    param->idx = 0;
765
0
    param->value_type = VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING;
766
0
    param->value.str = value;
767
0
    stmt->params = param;
768
0
    stmt->function = "literal";
769
0
    program->first = stmt;
770
0
    program->only_literal = TRUE;
771
  /* A full program */
772
0
  } else if (*data == '\2') {
773
0
    ADVANCE_INPUT(1);
774
0
    while (size > 0 && *data != '\t') {
775
0
      int ret = var_expand_program_import_stmt(data, size, program, error_r);
776
0
      if (ret < 0)
777
0
        return -1;
778
0
      ADVANCE_INPUT(ret);
779
0
      if (size > 0 && *data == '\t') {
780
0
        ADVANCE_INPUT(1);
781
0
        break;
782
0
      } else if (size == 0 || *data != '\1') {
783
0
        *error_r = "Missing statement end";
784
0
        return -1;
785
0
      }
786
0
      ADVANCE_INPUT(1);
787
0
    }
788
    /* And finally there should be variables */
789
0
    if (size > 0 && *data != '\t') {
790
0
      const char *ptr = memchr(data, '\t', size);
791
0
      if (ptr == NULL) {
792
0
        *error_r = "Missing variables end";
793
0
        return -1;
794
0
      }
795
0
      size_t len = ptr - data;
796
      /* ensure data is properly terminated, since
797
         p_strsplit() expects a NUL terminated string. */
798
0
      const char *variables = t_strdup_until(data, ptr);
799
0
      program->variables = (const char *const *)
800
0
        p_strsplit(program->pool, variables, "\1");
801
0
      ADVANCE_INPUT(len + 1);
802
0
    } else {
803
0
      ADVANCE_INPUT(1);
804
0
    }
805
0
  } else {
806
0
    *error_r = "Unknown input";
807
0
    return -1;
808
0
  }
809
0
  *_data = data;
810
0
  *_size = size;
811
812
0
  return 0;
813
0
}
814
815
int var_expand_program_import_sized(const char *data, size_t size,
816
            struct var_expand_program **program_r,
817
            const char **error_r)
818
0
{
819
0
  i_assert(data != NULL);
820
821
  /* The absolute minimum program is \2 \t or \1 \r. */
822
0
  if (size < 2) {
823
0
    *error_r = "Too short";
824
0
    return -1;
825
0
  }
826
827
0
  pool_t pool = pool_alloconly_create(MEMPOOL_GROWING"var expand program", size);
828
0
  struct var_expand_program *prev = NULL;
829
0
  struct var_expand_program *first = NULL;
830
0
  int ret;
831
832
0
  while (size > 0) {
833
0
    struct var_expand_program *program =
834
0
      p_new(pool, struct var_expand_program, 1);
835
0
    program->pool = pool;
836
0
    T_BEGIN {
837
0
      ret = var_expand_program_import_one(&data, &size,
838
0
                  program, error_r);
839
0
    } T_END;
840
0
    if (ret < 0)
841
0
      break;
842
0
    if (first == NULL)
843
0
      first = program;
844
0
    if (prev != NULL)
845
0
      prev->next = program;
846
0
    prev = program;
847
0
  }
848
849
0
  if (ret < 0)
850
0
    pool_unref(&pool);
851
0
  else
852
0
    *program_r = first;
853
854
0
  return ret;
855
0
}
856
857
int var_expand_program_import(const char *data,
858
            struct var_expand_program **program_r,
859
            const char **error_r)
860
0
{
861
0
  i_assert(data != NULL);
862
0
  return var_expand_program_import_sized(data, strlen(data), program_r,
863
0
                 error_r);
864
0
}