Coverage Report

Created: 2026-06-09 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/enotify/cmd-notify.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2
 */
3
4
#include "lib.h"
5
#include "str-sanitize.h"
6
7
#include "sieve-common.h"
8
#include "sieve-error.h"
9
#include "sieve-code.h"
10
#include "sieve-extensions.h"
11
#include "sieve-commands.h"
12
#include "sieve-actions.h"
13
#include "sieve-validator.h"
14
#include "sieve-generator.h"
15
#include "sieve-interpreter.h"
16
#include "sieve-dump.h"
17
#include "sieve-result.h"
18
19
#include "ext-enotify-common.h"
20
21
/*
22
 * Forward declarations
23
 */
24
25
static const struct sieve_argument_def notify_importance_tag;
26
static const struct sieve_argument_def notify_from_tag;
27
static const struct sieve_argument_def notify_options_tag;
28
static const struct sieve_argument_def notify_message_tag;
29
30
/*
31
 * Notify command
32
 *
33
 * Syntax:
34
 *    notify [":from" string]
35
 *           [":importance" <"1" / "2" / "3">]
36
 *           [":options" string-list]
37
 *           [":message" string]
38
 *           <method: string>
39
 */
40
41
static bool
42
cmd_notify_registered(struct sieve_validator *valdtr,
43
          const struct sieve_extension *ext,
44
          struct sieve_command_registration *cmd_reg);
45
static bool
46
cmd_notify_pre_validate(struct sieve_validator *validator,
47
      struct sieve_command *cmd);
48
static bool
49
cmd_notify_validate(struct sieve_validator *valdtr, struct sieve_command *cmd);
50
static bool
51
cmd_notify_generate(const struct sieve_codegen_env *cgenv,
52
        struct sieve_command *ctx);
53
54
const struct sieve_command_def notify_command = {
55
  .identifier = "notify",
56
  .type = SCT_COMMAND,
57
  .positional_args = 1,
58
  .subtests = 0,
59
  .block_allowed = FALSE,
60
  .block_required = FALSE,
61
  .registered = cmd_notify_registered,
62
  .pre_validate = cmd_notify_pre_validate,
63
  .validate = cmd_notify_validate,
64
  .generate = cmd_notify_generate,
65
};
66
67
/*
68
 * Notify command tags
69
 */
70
71
/* Forward declarations */
72
73
static bool
74
cmd_notify_validate_string_tag(struct sieve_validator *valdtr,
75
             struct sieve_ast_argument **arg,
76
             struct sieve_command *cmd);
77
static bool
78
cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr,
79
           struct sieve_ast_argument **arg,
80
           struct sieve_command *cmd);
81
static bool
82
cmd_notify_validate_importance_tag(struct sieve_validator *valdtr,
83
           struct sieve_ast_argument **arg,
84
           struct sieve_command *cmd);
85
86
/* Argument objects */
87
88
static const struct sieve_argument_def notify_from_tag = {
89
  .identifier = "from",
90
  .validate = cmd_notify_validate_string_tag,
91
};
92
93
static const struct sieve_argument_def notify_options_tag = {
94
  .identifier = "options",
95
  .validate = cmd_notify_validate_stringlist_tag,
96
};
97
98
static const struct sieve_argument_def notify_message_tag = {
99
  .identifier = "message",
100
  .validate = cmd_notify_validate_string_tag,
101
};
102
103
static const struct sieve_argument_def notify_importance_tag = {
104
  .identifier = "importance",
105
  .validate = cmd_notify_validate_importance_tag,
106
};
107
108
/*
109
 * Notify operation
110
 */
111
112
static bool
113
cmd_notify_operation_dump(const struct sieve_dumptime_env *denv,
114
        sieve_size_t *address);
115
static int
116
cmd_notify_operation_execute(const struct sieve_runtime_env *renv,
117
           sieve_size_t *address);
118
119
const struct sieve_operation_def notify_operation = {
120
  .mnemonic = "NOTIFY",
121
  .ext_def = &enotify_extension,
122
  .code = EXT_ENOTIFY_OPERATION_NOTIFY,
123
  .dump = cmd_notify_operation_dump,
124
  .execute = cmd_notify_operation_execute,
125
};
126
127
/*
128
 * Notify action
129
 */
130
131
/* Forward declarations */
132
133
static int
134
act_notify_check_duplicate(const struct sieve_runtime_env *renv,
135
         const struct sieve_action *act,
136
         const struct sieve_action *act_other);
137
static void
138
act_notify_print(const struct sieve_action *action,
139
     const struct sieve_result_print_env *rpenv,
140
     bool *keep);
141
static int
142
act_notify_commit(const struct sieve_action_exec_env *aenv, void *tr_context);
143
144
/* Action object */
145
146
const struct sieve_action_def act_notify = {
147
  .name = "notify",
148
  .check_duplicate =act_notify_check_duplicate,
149
  .print = act_notify_print,
150
  .commit = act_notify_commit,
151
};
152
153
/*
154
 * Command validation context
155
 */
156
157
struct cmd_notify_context_data {
158
  struct sieve_ast_argument *from;
159
  struct sieve_ast_argument *message;
160
  struct sieve_ast_argument *options;
161
};
162
163
/*
164
 * Tag validation
165
 */
166
167
static bool
168
cmd_notify_validate_string_tag(struct sieve_validator *valdtr,
169
             struct sieve_ast_argument **arg,
170
             struct sieve_command *cmd)
171
0
{
172
0
  struct sieve_ast_argument *tag = *arg;
173
0
  struct cmd_notify_context_data *ctx_data =
174
0
    (struct cmd_notify_context_data *)cmd->data;
175
176
  /* Detach the tag itself */
177
0
  *arg = sieve_ast_arguments_detach(*arg,1);
178
179
  /* Check syntax:
180
       :from <string>
181
       :message <string>
182
   */
183
0
  if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
184
0
            SAAT_STRING, FALSE))
185
0
    return FALSE;
186
187
0
  if (sieve_argument_is(tag, notify_from_tag)) {
188
0
    ctx_data->from = *arg;
189
190
    /* Skip parameter */
191
0
    *arg = sieve_ast_argument_next(*arg);
192
0
  } else if (sieve_argument_is(tag, notify_message_tag)) {
193
0
    ctx_data->message = *arg;
194
195
    /* Skip parameter */
196
0
    *arg = sieve_ast_argument_next(*arg);
197
0
  }
198
0
  return TRUE;
199
0
}
200
201
static bool
202
cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr,
203
           struct sieve_ast_argument **arg,
204
           struct sieve_command *cmd)
205
0
{
206
0
  struct sieve_ast_argument *tag = *arg;
207
0
  struct cmd_notify_context_data *ctx_data =
208
0
    (struct cmd_notify_context_data *)cmd->data;
209
210
  /* Detach the tag itself */
211
0
  *arg = sieve_ast_arguments_detach(*arg,1);
212
213
  /* Check syntax:
214
       :options string-list
215
   */
216
0
  if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
217
0
            SAAT_STRING_LIST, FALSE))
218
0
    return FALSE;
219
220
  /* Assign context */
221
0
  ctx_data->options = *arg;
222
223
  /* Skip parameter */
224
0
  *arg = sieve_ast_argument_next(*arg);
225
226
0
  return TRUE;
227
0
}
228
229
static bool
230
cmd_notify_validate_importance_tag(struct sieve_validator *valdtr,
231
           struct sieve_ast_argument **arg,
232
           struct sieve_command *cmd ATTR_UNUSED)
233
0
{
234
0
  const struct sieve_ast_argument *tag = *arg;
235
0
  const char *impstr;
236
237
  /* Detach the tag itself */
238
0
  *arg = sieve_ast_arguments_detach(*arg,1);
239
240
  /* Check syntax:
241
       :importance <"1" / "2" / "3">
242
   */
243
0
  if (*arg == NULL) {
244
0
    sieve_argument_validate_error(
245
0
      valdtr, tag,
246
0
      "the :importance tag for the notify command requires a string parameter, "
247
0
      "but no parameters were found");
248
0
    return FALSE;
249
0
  }
250
0
  if (sieve_ast_argument_type(*arg) != SAAT_STRING) {
251
    /* Not a string */
252
0
    sieve_argument_validate_error(
253
0
      valdtr, *arg,
254
0
      "the :importance tag for the notify command requires a string parameter, "
255
0
      "but %s was found", sieve_ast_argument_name(*arg));
256
0
    return FALSE;
257
0
  }
258
259
0
  impstr = sieve_ast_argument_strc(*arg);
260
0
  if (impstr[0] < '1' || impstr[0]  > '3' || impstr[1] != '\0') {
261
    /* Invalid importance */
262
0
    sieve_argument_validate_error(
263
0
      valdtr, *arg,
264
0
      "invalid :importance value for notify command: %s",
265
0
      impstr);
266
0
    return FALSE;
267
0
  }
268
269
0
  sieve_ast_argument_number_substitute(*arg, impstr[0] - '0');
270
0
  (*arg)->argument = sieve_argument_create((*arg)->ast, &number_argument,
271
0
             tag->argument->ext,
272
0
             tag->argument->id_code);
273
274
  /* Skip parameter */
275
0
  *arg = sieve_ast_argument_next(*arg);
276
277
0
  return TRUE;
278
0
}
279
280
/*
281
 * Command registration
282
 */
283
284
static bool
285
cmd_notify_registered(struct sieve_validator *valdtr,
286
          const struct sieve_extension *ext,
287
          struct sieve_command_registration *cmd_reg)
288
0
{
289
0
  sieve_validator_register_tag(valdtr, cmd_reg, ext,
290
0
             &notify_importance_tag,
291
0
             CMD_NOTIFY_OPT_IMPORTANCE);
292
0
  sieve_validator_register_tag(valdtr, cmd_reg, ext,
293
0
             &notify_from_tag, CMD_NOTIFY_OPT_FROM);
294
0
  sieve_validator_register_tag(valdtr, cmd_reg, ext,
295
0
             &notify_options_tag,
296
0
             CMD_NOTIFY_OPT_OPTIONS);
297
0
  sieve_validator_register_tag(valdtr, cmd_reg, ext,
298
0
             &notify_message_tag,
299
0
             CMD_NOTIFY_OPT_MESSAGE);
300
0
  return TRUE;
301
0
}
302
303
/*
304
 * Command validation
305
 */
306
307
static bool
308
cmd_notify_pre_validate(struct sieve_validator *validator ATTR_UNUSED,
309
      struct sieve_command *cmd)
310
0
{
311
0
  struct cmd_notify_context_data *ctx_data;
312
313
  /* Assign context */
314
0
  ctx_data = p_new(sieve_command_pool(cmd),
315
0
       struct cmd_notify_context_data, 1);
316
0
  cmd->data = ctx_data;
317
318
0
  return TRUE;
319
0
}
320
321
static bool
322
cmd_notify_validate(struct sieve_validator *valdtr, struct sieve_command *cmd)
323
0
{
324
0
  struct sieve_ast_argument *arg = cmd->first_positional;
325
0
  struct cmd_notify_context_data *ctx_data =
326
0
    (struct cmd_notify_context_data *)cmd->data;
327
328
0
  if (!sieve_validate_positional_argument(valdtr, cmd, arg, "method", 1,
329
0
            SAAT_STRING))
330
0
    return FALSE;
331
332
0
  if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE))
333
0
    return FALSE;
334
335
0
  return ext_enotify_compile_check_arguments(
336
0
    valdtr, cmd, arg, ctx_data->message, ctx_data->from,
337
0
    ctx_data->options);
338
0
}
339
340
/*
341
 * Code generation
342
 */
343
344
static bool
345
cmd_notify_generate(const struct sieve_codegen_env *cgenv,
346
        struct sieve_command *cmd)
347
0
{
348
0
  sieve_operation_emit(cgenv->sblock, cmd->ext, &notify_operation);
349
350
  /* Generate arguments */
351
0
  return sieve_generate_arguments(cgenv, cmd, NULL);
352
0
}
353
354
/*
355
 * Code dump
356
 */
357
358
static bool
359
cmd_notify_operation_dump(const struct sieve_dumptime_env *denv,
360
        sieve_size_t *address)
361
0
{
362
0
  int opt_code = 0;
363
364
0
  sieve_code_dumpf(denv, "NOTIFY");
365
0
  sieve_code_descend(denv);
366
367
  /* Dump optional operands */
368
369
0
  for (;;) {
370
0
    int opt;
371
0
    bool opok = TRUE;
372
373
0
    if ((opt = sieve_opr_optional_dump(denv, address,
374
0
               &opt_code)) < 0)
375
0
      return FALSE;
376
377
0
    if (opt == 0)
378
0
      break;
379
380
0
    switch (opt_code) {
381
0
    case CMD_NOTIFY_OPT_IMPORTANCE:
382
0
      opok = sieve_opr_number_dump(denv, address,
383
0
                 "importance");
384
0
      break;
385
0
    case CMD_NOTIFY_OPT_FROM:
386
0
      opok = sieve_opr_string_dump(denv, address, "from");
387
0
      break;
388
0
    case CMD_NOTIFY_OPT_OPTIONS:
389
0
      opok = sieve_opr_stringlist_dump(denv, address,
390
0
               "options");
391
0
      break;
392
0
    case CMD_NOTIFY_OPT_MESSAGE:
393
0
      opok = sieve_opr_string_dump(denv, address, "message");
394
0
      break;
395
0
    default:
396
0
      return FALSE;
397
0
    }
398
399
0
    if (!opok)
400
0
      return FALSE;
401
0
  }
402
403
  /* Dump method operand */
404
0
  return sieve_opr_string_dump(denv, address, "method");
405
0
}
406
407
/*
408
 * Code execution
409
 */
410
411
static int
412
cmd_notify_operation_execute(const struct sieve_runtime_env *renv,
413
           sieve_size_t *address)
414
0
{
415
0
  const struct sieve_extension *this_ext = renv->oprtn->ext;
416
0
  struct sieve_side_effects_list *slist = NULL;
417
0
  struct sieve_enotify_action *act;
418
0
  void *method_context;
419
0
  pool_t pool;
420
0
  int opt_code = 0;
421
0
  sieve_number_t importance = 2;
422
0
  struct sieve_stringlist *options = NULL;
423
0
  const struct sieve_enotify_method *method;
424
0
  string_t *method_uri, *message = NULL, *from = NULL;
425
0
  int ret;
426
427
  /*
428
   * Read operands
429
   */
430
431
  /* Optional operands */
432
433
0
  for (;;) {
434
0
    int opt;
435
436
0
    if ((opt = sieve_opr_optional_read(renv, address,
437
0
               &opt_code)) < 0)
438
0
      return SIEVE_EXEC_BIN_CORRUPT;
439
440
0
    if (opt == 0) break;
441
442
0
    switch (opt_code) {
443
0
    case CMD_NOTIFY_OPT_IMPORTANCE:
444
0
      ret = sieve_opr_number_read(renv, address, "importance",
445
0
                &importance);
446
0
      break;
447
0
    case CMD_NOTIFY_OPT_FROM:
448
0
      ret = sieve_opr_string_read(renv, address, "from",
449
0
                &from);
450
0
      break;
451
0
    case CMD_NOTIFY_OPT_MESSAGE:
452
0
      ret = sieve_opr_string_read(renv, address, "message",
453
0
                &message);
454
0
      break;
455
0
    case CMD_NOTIFY_OPT_OPTIONS:
456
0
      ret = sieve_opr_stringlist_read(renv, address,
457
0
              "options", &options);
458
0
      break;
459
0
    default:
460
0
      sieve_runtime_trace_error(
461
0
        renv, "unknown optional operand");
462
0
      return SIEVE_EXEC_BIN_CORRUPT;
463
0
    }
464
465
0
    if (ret <= 0)
466
0
      return ret;
467
0
  }
468
469
  /* Method operand */
470
471
0
  if ((ret = sieve_opr_string_read(renv, address, "method",
472
0
           &method_uri)) <= 0)
473
0
    return ret;
474
475
  /*
476
   * Perform operation
477
   */
478
479
  /* Enforce 0 < importance < 4 (just to be sure) */
480
481
0
  if (importance < 1)
482
0
    importance = 1;
483
0
  else if (importance > 3)
484
0
    importance = 3;
485
486
  /* Trace */
487
488
0
  if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) {
489
0
    sieve_runtime_trace(renv, 0, "notify action");
490
0
    sieve_runtime_trace_descend(renv);
491
0
    sieve_runtime_trace(renv, 0, "notify with uri '%s'",
492
0
            str_sanitize(str_c(method_uri), 80));
493
0
  }
494
495
  /* Check operands */
496
497
0
  if ((ret = ext_enotify_runtime_check_operands(renv, method_uri, message,
498
0
                  from, options, &method,
499
0
                  &method_context)) > 0)
500
0
  {
501
    /* Add notify action to the result */
502
0
    pool = sieve_result_pool(renv->result);
503
0
    act = p_new(pool, struct sieve_enotify_action, 1);
504
0
    act->method = method;
505
0
    act->method_context = method_context;
506
0
    act->importance = importance;
507
0
    if (message != NULL)
508
0
      act->message = p_strdup(pool, str_c(message));
509
0
    if (from != NULL)
510
0
      act->from = p_strdup(pool, str_c(from));
511
512
0
    if (sieve_result_add_action(renv, this_ext, "notify",
513
0
              &act_notify, slist,
514
0
              act, 0, FALSE) < 0)
515
0
      return SIEVE_EXEC_FAILURE;
516
517
0
    return SIEVE_EXEC_OK;
518
0
  }
519
0
  return ret;
520
0
}
521
522
/*
523
 * Action
524
 */
525
526
/* Runtime verification */
527
528
static int
529
act_notify_check_duplicate(const struct sieve_runtime_env *renv,
530
         const struct sieve_action *act,
531
         const struct sieve_action *act_other)
532
0
{
533
0
  const struct sieve_execute_env *eenv = renv->exec_env;
534
0
  const struct sieve_enotify_action *nact, *nact_other;
535
0
  const struct sieve_enotify_method_def *nmth_def;
536
0
  struct sieve_enotify_env nenv;
537
0
  int result;
538
539
0
  if (act->context == NULL || act_other->context == NULL)
540
0
    return 0;
541
542
0
  nact = (const struct sieve_enotify_action *)act->context;
543
0
  nact_other = (const struct sieve_enotify_action *)act_other->context;
544
545
0
  if (nact->method == NULL || nact->method->def == NULL)
546
0
    return 0;
547
548
0
  nmth_def = nact->method->def;
549
0
  if (nmth_def->action_check_duplicates == NULL)
550
0
    return 0;
551
552
0
  i_zero(&nenv);
553
0
  nenv.svinst = eenv->svinst;
554
0
  nenv.method = nact->method;
555
0
  nenv.ehandler = renv->ehandler;
556
0
  nenv.location = act->location;
557
0
  nenv.event = event_create(nenv.svinst->event);
558
0
  event_set_append_log_prefix(nenv.event, "notify: ");
559
560
0
  result = nmth_def->action_check_duplicates(&nenv, nact, nact_other);
561
562
0
  event_unref(&nenv.event);
563
564
0
  return result;
565
0
}
566
567
/* Result printing */
568
569
static void
570
act_notify_print(const struct sieve_action *action,
571
     const struct sieve_result_print_env *rpenv,
572
     bool *keep ATTR_UNUSED)
573
0
{
574
0
  const struct sieve_enotify_action *act =
575
0
    (const struct sieve_enotify_action *)action->context;
576
0
  const struct sieve_enotify_method *method;
577
578
0
  method = act->method;
579
580
0
  if (method->def != NULL) {
581
0
    sieve_result_action_printf(
582
0
      rpenv, "send notification with method '%s:':",
583
0
      method->def->identifier);
584
585
0
    if (method->def->action_print != NULL) {
586
0
      struct sieve_enotify_print_env penv;
587
588
0
      i_zero(&penv);
589
0
      penv.result_penv = rpenv;
590
591
0
      method->def->action_print(&penv, act);
592
0
    }
593
0
  }
594
0
}
595
596
/* Result execution */
597
598
static int
599
act_notify_commit(const struct sieve_action_exec_env *aenv,
600
      void *tr_context ATTR_UNUSED)
601
0
{
602
0
  const struct sieve_execute_env *eenv = aenv->exec_env;
603
0
  const struct sieve_enotify_action *act =
604
0
    (const struct sieve_enotify_action *)aenv->action->context;
605
0
  const struct sieve_enotify_method *method = act->method;
606
0
  struct sieve_enotify_exec_env nenv;
607
0
  int ret = 0;
608
609
0
  if (method->def != NULL && method->def->action_execute != NULL) {
610
    /* Compose log structure */
611
0
    i_zero(&nenv);
612
0
    nenv.svinst = eenv->svinst;
613
0
    nenv.flags = eenv->flags;
614
0
    nenv.method = method;
615
0
    nenv.scriptenv = eenv->scriptenv;
616
0
    nenv.msgdata = eenv->msgdata;
617
0
    nenv.msgctx = aenv->msgctx;
618
619
0
    nenv.ehandler = aenv->ehandler;
620
0
    nenv.event = aenv->event;
621
622
0
    ret = method->def->action_execute(&nenv, act);
623
0
    if (ret >= 0)
624
0
      eenv->exec_status->significant_action_executed = TRUE;
625
0
  }
626
627
0
  return (ret >= 0 ? SIEVE_EXEC_OK : SIEVE_EXEC_TEMP_FAILURE);
628
0
}