1 /*----------------------------------------------------------------------------*/
2 /* Xymon monitor library. */
3 /* */
4 /* This is a library module for Xymon, responsible for loading the */
5 /* alerts.cfg file which holds information about the Xymon alert */
6 /* configuration. */
7 /* */
8 /* Copyright (C) 2004-2011 Henrik Storner <henrik@hswn.dk> */
9 /* */
10 /* This program is released under the GNU General Public License (GPL), */
11 /* version 2. See the file "COPYING" for details. */
12 /* */
13 /*----------------------------------------------------------------------------*/
14
15 static char rcsid[] = "$Id: loadalerts.c 8069 2019-07-23 15:29:06Z jccleaver $";
16
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <ctype.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <time.h>
25 #include <limits.h>
26 #include <errno.h>
27
28 #include <pcre.h>
29
30 #include "libxymon.h"
31
32
33 /* token's are the pre-processor macros we expand while parsing the config file */
34 typedef struct token_t {
35 char *name;
36 char *value;
37 struct token_t *next;
38 } token_t;
39 static token_t *tokhead = NULL;
40
41 /* This defines a rule. Some general criteria, and a list of recipients. */
42 typedef struct rule_t {
43 int cfid;
44 criteria_t *criteria;
45 recip_t *recipients;
46 struct rule_t *next;
47 } rule_t;
48 static rule_t *rulehead = NULL;
49 static rule_t *ruletail = NULL;
50 static int cfid = 0;
51 static char cfline[256];
52 static int printmode = 0;
53 static rule_t *printrule = NULL;
54
55 static enum { P_NONE, P_RULE, P_RECIP } pstate = P_NONE;
56 static int defaultcolors = 0;
57 static int localalertmode = 0;
58
setup_criteria(rule_t ** currule,recip_t ** currcp)59 static criteria_t *setup_criteria(rule_t **currule, recip_t **currcp)
60 {
61 criteria_t *crit = NULL;
62
63 MEMDEFINE(cfline);
64
65 switch (pstate) {
66 case P_NONE:
67 *currule = (rule_t *)calloc(1, sizeof(rule_t));
68 (*currule)->cfid = cfid;
69 pstate = P_RULE;
70 /* Fall through */
71
72 case P_RULE:
73 if (!(*currule)->criteria)
74 (*currule)->criteria = (criteria_t *)calloc(1, sizeof(criteria_t));
75 crit = (*currule)->criteria;
76 crit->cfid = cfid;
77 if (crit->cfline == NULL) crit->cfline = strdup(cfline);
78 *currcp = NULL;
79 break;
80
81 case P_RECIP:
82 if (!(*currcp)->criteria) {
83 recip_t *rwalk;
84
85 (*currcp)->criteria = (criteria_t *)calloc(1, sizeof(criteria_t));
86
87 /* Make sure other recipients on the same rule also get these criteria */
88 for (rwalk = (*currule)->recipients; (rwalk); rwalk = rwalk->next) {
89 if (rwalk->cfid == cfid) rwalk->criteria = (*currcp)->criteria;
90 }
91 }
92 crit = (*currcp)->criteria;
93 crit->cfid = cfid;
94 if (crit->cfline == NULL) crit->cfline = strdup(cfline);
95 break;
96 }
97
98 MEMUNDEFINE(cfline);
99 return crit;
100 }
101
set_localalertmode(int localmode)102 void set_localalertmode(int localmode)
103 {
104 if (localmode) localalertmode = 1;
105 }
106
preprocess(char * buf)107 static char *preprocess(char *buf)
108 {
109 /* Expands config-file macros */
110 static strbuffer_t *result = NULL;
111 char *inp;
112
113 if (result == NULL) result = newstrbuffer(8192);
114 clearstrbuffer(result);
115
116 inp = buf;
117 while (inp) {
118 char *p;
119
120 p = strchr(inp, '$');
121 if (p == NULL) {
122 addtobuffer(result, inp);
123 inp = NULL;
124 }
125 else {
126 token_t *twalk;
127 char savech;
128 int n;
129
130 *p = '\0';
131 addtobuffer(result, inp);
132 p = (p+1);
133
134 n = strcspn(p, "\t $.,|%!()[]{}+?/&@:;*");
135 savech = *(p+n);
136 *(p+n) = '\0';
137 for (twalk = tokhead; (twalk && strcmp(p, twalk->name)); twalk = twalk->next) ;
138 *(p+n) = savech;
139
140 if (twalk) addtobuffer(result, twalk->value);
141 inp = p+n;
142 }
143 }
144
145 return STRBUF(result);
146 }
147
flush_rule(rule_t * currule)148 static void flush_rule(rule_t *currule)
149 {
150 if (currule == NULL) return;
151
152 currule->next = NULL;
153
154 if (rulehead == NULL) {
155 rulehead = ruletail = currule;
156 }
157 else {
158 ruletail->next = currule;
159 ruletail = currule;
160 }
161 }
162
free_criteria(criteria_t * crit)163 static void free_criteria(criteria_t *crit)
164 {
165 if (crit->cfline) xfree(crit->cfline);
166 if (crit->pagespec) xfree(crit->pagespec);
167 if (crit->pagespecre) pcre_free(crit->pagespecre);
168 if (crit->expagespec) xfree(crit->expagespec);
169 if (crit->expagespecre) pcre_free(crit->expagespecre);
170 if (crit->dgspec) xfree(crit->dgspec);
171 if (crit->dgspecre) pcre_free(crit->dgspecre);
172 if (crit->exdgspec) xfree(crit->exdgspec);
173 if (crit->exdgspecre) pcre_free(crit->exdgspecre);
174 if (crit->hostspec) xfree(crit->hostspec);
175 if (crit->hostspecre) pcre_free(crit->hostspecre);
176 if (crit->exhostspec) xfree(crit->exhostspec);
177 if (crit->exhostspecre) pcre_free(crit->exhostspecre);
178 if (crit->svcspec) xfree(crit->svcspec);
179 if (crit->svcspecre) pcre_free(crit->svcspecre);
180 if (crit->exsvcspec) xfree(crit->exsvcspec);
181 if (crit->exsvcspecre) pcre_free(crit->exsvcspecre);
182 if (crit->classspec) xfree(crit->classspec);
183 if (crit->classspecre) pcre_free(crit->classspecre);
184 if (crit->exclassspec) xfree(crit->exclassspec);
185 if (crit->exclassspecre) pcre_free(crit->exclassspecre);
186 if (crit->groupspec) xfree(crit->groupspec);
187 if (crit->groupspecre) pcre_free(crit->groupspecre);
188 if (crit->exgroupspec) xfree(crit->exgroupspec);
189 if (crit->exgroupspecre) pcre_free(crit->exgroupspecre);
190 if (crit->timespec) xfree(crit->timespec);
191 if (crit->extimespec) xfree(crit->extimespec);
192 }
193
load_alertconfig(char * configfn,int defcolors,int defaultinterval)194 int load_alertconfig(char *configfn, int defcolors, int defaultinterval)
195 {
196 /* (Re)load the configuration file without leaking memory */
197 static void *configfiles = NULL;
198 char fn[PATH_MAX];
199 FILE *fd;
200 strbuffer_t *inbuf;
201 char *p;
202 rule_t *currule = NULL;
203 recip_t *currcp = NULL, *rcptail = NULL;
204
205 MEMDEFINE(fn);
206
207 if (configfn) strncpy(fn, configfn, sizeof(fn)); else snprintf(fn, sizeof(fn), "%s/etc/alerts.cfg", xgetenv("XYMONHOME"));
208
209 /* First check if there were no modifications at all */
210 if (configfiles) {
211 if (!stackfmodified(configfiles)){
212 dbgprintf("No files modified, skipping reload of %s\n", fn);
213 MEMUNDEFINE(fn);
214 return 0;
215 }
216 else {
217 stackfclist(&configfiles);
218 configfiles = NULL;
219 }
220 }
221
222 fd = stackfopen(fn, "r", &configfiles);
223 if (!fd) {
224 errprintf("Cannot open configuration file %s: %s\n", fn, strerror(errno));
225 MEMUNDEFINE(fn);
226 return 0;
227 }
228
229 /* First, clean out the old rule set */
230 while (rulehead) {
231 rule_t *trule;
232
233 if (rulehead->criteria) {
234 free_criteria(rulehead->criteria);
235 xfree(rulehead->criteria);
236 }
237
238 while (rulehead->recipients) {
239 recip_t *trecip = rulehead->recipients;
240
241 if (trecip->criteria) {
242 recip_t *rwalk;
243
244 /* Clear out the duplicate criteria that may exist, to avoid double-free'ing them */
245 for (rwalk = trecip->next; (rwalk); rwalk = rwalk->next) {
246 if (rwalk->criteria == trecip->criteria) rwalk->criteria = NULL;
247 }
248
249 free_criteria(trecip->criteria);
250 xfree(trecip->criteria);
251 }
252
253 if (trecip->recipient) xfree(trecip->recipient);
254 if (trecip->scriptname) xfree(trecip->scriptname);
255 rulehead->recipients = rulehead->recipients->next;
256 xfree(trecip);
257 }
258 trule = rulehead;
259 rulehead = rulehead->next;
260 xfree(trule);
261 }
262
263 while (tokhead) {
264 token_t *ttok;
265
266 if (tokhead->name) xfree(tokhead->name);
267 if (tokhead->value) xfree(tokhead->value);
268 ttok = tokhead;
269 tokhead = tokhead->next;
270 xfree(ttok);
271 }
272
273 defaultcolors = defcolors;
274
275 MEMDEFINE(cfline);
276
277 cfid = 0;
278 inbuf = newstrbuffer(0);
279 while (stackfgets(inbuf, NULL)) {
280 int firsttoken = 1;
281 int mailcmdactive = 0, scriptcmdactive = 0;
282 recip_t *curlinerecips = NULL;
283
284 cfid++;
285 sanitize_input(inbuf, 1, 0);
286
287 /* Skip empty lines */
288 if (STRBUFLEN(inbuf) == 0) continue;
289
290 if ((*STRBUF(inbuf) == '$') && strchr(STRBUF(inbuf), '=')) {
291 /* Define a macro */
292 token_t *newtok = (token_t *) malloc(sizeof(token_t));
293 char *delim;
294
295 delim = strchr(STRBUF(inbuf), '=');
296 *delim = '\0';
297 newtok->name = strdup(STRBUF(inbuf)+1); /* Skip the '$' */
298 newtok->value = strdup(preprocess(delim+1));
299 newtok->next = tokhead;
300 tokhead = newtok;
301 continue;
302 }
303
304 strncpy(cfline, STRBUF(inbuf), (sizeof(cfline)-1));
305 cfline[sizeof(cfline)-1] = '\0';
306
307 /* Expand macros inside the line before parsing */
308 p = strtok(preprocess(STRBUF(inbuf)), " \t");
309 while (p) {
310 if ((strncasecmp(p, "PAGE=", 5) == 0) || (strncasecmp(p, "PAGES=", 6) == 0)) {
311 char *val;
312 criteria_t *crit;
313
314 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
315 val = strchr(p, '=')+1;
316 crit = setup_criteria(&currule, &currcp);
317 crit->pagespec = strdup(val);
318 if (*(crit->pagespec) == '%') crit->pagespecre = compileregex(crit->pagespec+1);
319 firsttoken = 0;
320 }
321 else if ((strncasecmp(p, "EXPAGE=", 7) == 0) || (strncasecmp(p, "EXPAGES=", 8) == 0)) {
322 char *val;
323 criteria_t *crit;
324
325 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
326 val = strchr(p, '=')+1;
327 crit = setup_criteria(&currule, &currcp);
328 crit->expagespec = strdup(val);
329 if (*(crit->expagespec) == '%') crit->expagespecre = compileregex(crit->expagespec+1);
330 firsttoken = 0;
331 }
332 else if ((strncasecmp(p, "DISPLAYGROUP=", 13) == 0) || (strncasecmp(p, "DISPLAYGROUPS=", 14) == 0)) {
333 char *val;
334 criteria_t *crit;
335
336 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
337 val = strchr(p, '=')+1;
338 crit = setup_criteria(&currule, &currcp);
339 crit->dgspec = strdup(val);
340 if (*(crit->dgspec) == '%') crit->dgspecre = compileregex(crit->dgspec+1);
341 firsttoken = 0;
342 }
343 else if ((strncasecmp(p, "EXDISPLAYGROUP=", 15) == 0) || (strncasecmp(p, "EXDISPLAYGROUPS=", 16) == 0)) {
344 char *val;
345 criteria_t *crit;
346
347 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
348 val = strchr(p, '=')+1;
349 crit = setup_criteria(&currule, &currcp);
350 crit->exdgspec = strdup(val);
351 if (*(crit->exdgspec) == '%') crit->exdgspecre = compileregex(crit->exdgspec+1);
352 firsttoken = 0;
353 }
354 else if ((strncasecmp(p, "HOST=", 5) == 0) || (strncasecmp(p, "HOSTS=", 6) == 0)) {
355 char *val;
356 criteria_t *crit;
357
358 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
359 val = strchr(p, '=')+1;
360 crit = setup_criteria(&currule, &currcp);
361 crit->hostspec = strdup(val);
362 if (*(crit->hostspec) == '%') crit->hostspecre = compileregex(crit->hostspec+1);
363 firsttoken = 0;
364 }
365 else if ((strncasecmp(p, "EXHOST=", 7) == 0) || (strncasecmp(p, "EXHOSTS=", 8) == 0)) {
366 char *val;
367 criteria_t *crit;
368
369 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
370 val = strchr(p, '=')+1;
371 crit = setup_criteria(&currule, &currcp);
372 crit->exhostspec = strdup(val);
373 if (*(crit->exhostspec) == '%') crit->exhostspecre = compileregex(crit->exhostspec+1);
374 firsttoken = 0;
375 }
376 else if ((strncasecmp(p, "SERVICE=", 8) == 0) || (strncasecmp(p, "SERVICES=", 9) == 0)) {
377 char *val;
378 criteria_t *crit;
379
380 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
381 val = strchr(p, '=')+1;
382 crit = setup_criteria(&currule, &currcp);
383 crit->svcspec = strdup(val);
384 if (*(crit->svcspec) == '%') crit->svcspecre = compileregex(crit->svcspec+1);
385 firsttoken = 0;
386 }
387 else if ((strncasecmp(p, "EXSERVICE=", 10) == 0) || (strncasecmp(p, "EXSERVICES=", 11) == 0)) {
388 char *val;
389 criteria_t *crit;
390
391 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
392 val = strchr(p, '=')+1;
393 crit = setup_criteria(&currule, &currcp);
394 crit->exsvcspec = strdup(val);
395 if (*(crit->exsvcspec) == '%') crit->exsvcspecre = compileregex(crit->exsvcspec+1);
396 firsttoken = 0;
397 }
398 else if (strncasecmp(p, "CLASS=", 6) == 0) {
399 char *val;
400 criteria_t *crit;
401
402 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
403 val = strchr(p, '=')+1;
404 crit = setup_criteria(&currule, &currcp);
405 crit->classspec = strdup(val);
406 if (*(crit->classspec) == '%') crit->classspecre = compileregex(crit->classspec+1);
407 firsttoken = 0;
408 }
409 else if (strncasecmp(p, "EXCLASS=", 8) == 0) {
410 char *val;
411 criteria_t *crit;
412
413 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
414 val = strchr(p, '=')+1;
415 crit = setup_criteria(&currule, &currcp);
416 crit->exclassspec = strdup(val);
417 if (*(crit->exclassspec) == '%') crit->exclassspecre = compileregex(crit->exclassspec+1);
418 firsttoken = 0;
419 }
420 else if (strncasecmp(p, "GROUP=", 6) == 0) {
421 char *val;
422 criteria_t *crit;
423
424 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
425 val = strchr(p, '=')+1;
426 crit = setup_criteria(&currule, &currcp);
427 crit->groupspec = strdup(val);
428 if (*(crit->groupspec) == '%') crit->groupspecre = compileregex(crit->groupspec+1);
429 firsttoken = 0;
430 }
431 else if (strncasecmp(p, "EXGROUP=", 8) == 0) {
432 char *val;
433 criteria_t *crit;
434
435 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
436 val = strchr(p, '=')+1;
437 crit = setup_criteria(&currule, &currcp);
438 crit->exgroupspec = strdup(val);
439 if (*(crit->exgroupspec) == '%') crit->exgroupspecre = compileregex(crit->exgroupspec+1);
440 firsttoken = 0;
441 }
442 else if ((strncasecmp(p, "COLOR=", 6) == 0) || (strncasecmp(p, "COLORS=", 7) == 0)) {
443 criteria_t *crit;
444 char *c1, *c2;
445 int cval, reverse = 0;
446
447 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
448 crit = setup_criteria(&currule, &currcp);
449
450 /* Put a value in crit->colors so we know there is an explicit color setting */
451 crit->colors = (1 << 30);
452 c1 = strchr(p, '=')+1;
453
454 /*
455 * If the first colorspec is "!color", then apply the default colors and
456 * subtract colors from that.
457 */
458 if (*c1 == '!') crit->colors |= defaultcolors;
459
460 do {
461 c2 = strchr(c1, ',');
462 if (c2) *c2 = '\0';
463
464 if (*c1 == '!') { reverse=1; c1++; }
465 cval = (1 << parse_color(c1));
466
467 if (reverse)
468 crit->colors &= (~cval);
469 else
470 crit->colors |= cval;
471
472 if (c2) c1 = (c2+1); else c1 = NULL;
473 } while (c1);
474 firsttoken = 0;
475 }
476 else if ((strncasecmp(p, "TIME=", 5) == 0) || (strncasecmp(p, "TIMES=", 6) == 0)) {
477 char *val;
478 criteria_t *crit;
479
480 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
481 val = strchr(p, '=')+1;
482 crit = setup_criteria(&currule, &currcp);
483 crit->timespec = strdup(val);
484 firsttoken = 0;
485 }
486 else if ((strncasecmp(p, "EXTIME=", 7) == 0) || (strncasecmp(p, "EXTIMES=", 8) == 0)) {
487 char *val;
488 criteria_t *crit;
489
490 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
491 val = strchr(p, '=')+1;
492 crit = setup_criteria(&currule, &currcp);
493 crit->extimespec = strdup(val);
494 firsttoken = 0;
495 }
496 else if (strncasecmp(p, "DURATION", 8) == 0) {
497 criteria_t *crit;
498
499 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
500 crit = setup_criteria(&currule, &currcp);
501 if (*(p+8) == '>') {
502 if (*(p+9) == '=')
503 crit->minduration = 60*durationvalue(p+10);
504 else
505 crit->minduration = 60*durationvalue(p+9) + 1;
506 }
507 else if (*(p+8) == '<') {
508 if (*(p+9) == '=')
509 crit->maxduration = 60*durationvalue(p+10);
510 else
511 crit->maxduration = 60*durationvalue(p+9) - 1;
512 }
513 else errprintf("Ignoring invalid DURATION at line %d: %s\n",cfid, p);
514 firsttoken = 0;
515 }
516 else if (strncasecmp(p, "RECOVERED", 9) == 0) {
517 criteria_t *crit;
518
519 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
520 crit = setup_criteria(&currule, &currcp);
521 crit->sendrecovered = SR_WANTED;
522 firsttoken = 0;
523 }
524 else if (strncasecmp(p, "NORECOVERED", 11) == 0) {
525 criteria_t *crit;
526
527 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
528 crit = setup_criteria(&currule, &currcp);
529 crit->sendrecovered = SR_NOTWANTED;
530 firsttoken = 0;
531 }
532 else if (strncasecmp(p, "NOTICE", 6) == 0) {
533 criteria_t *crit;
534
535 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
536 crit = setup_criteria(&currule, &currcp);
537 crit->sendnotice = SR_WANTED;
538 firsttoken = 0;
539 }
540 else if (strncasecmp(p, "NONOTICE", 8) == 0) {
541 criteria_t *crit;
542
543 if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
544 crit = setup_criteria(&currule, &currcp);
545 crit->sendnotice = SR_NOTWANTED;
546 firsttoken = 0;
547 }
548 else if ((pstate == P_RECIP) && (strncasecmp(p, "FORMAT=", 7) == 0)) {
549 if (!currcp) errprintf("FORMAT used without a recipient (line %d), ignored\n", cfid);
550 else if (strcasecmp(p+7, "TEXT") == 0) currcp->format = ALERTFORM_TEXT;
551 else if (strcasecmp(p+7, "PLAIN") == 0) currcp->format = ALERTFORM_PLAIN;
552 else if (strcasecmp(p+7, "SMS") == 0) currcp->format = ALERTFORM_SMS;
553 else if (strcasecmp(p+7, "PAGER") == 0) currcp->format = ALERTFORM_PAGER;
554 else if (strcasecmp(p+7, "SCRIPT") == 0) currcp->format = ALERTFORM_SCRIPT;
555 else errprintf("Unknown FORMAT setting '%s' ignored\n", p);
556 firsttoken = 0;
557 }
558 else if ((pstate == P_RECIP) && (strncasecmp(p, "REPEAT=", 7) == 0)) {
559 if (!currcp) errprintf("REPEAT used without a recipient (line %d), ignored\n", cfid);
560 else currcp->interval = 60*durationvalue(p+7);
561 firsttoken = 0;
562 }
563 else if ((pstate == P_RECIP) && (strcasecmp(p, "STOP") == 0)) {
564 if (!currcp) errprintf("STOP used without a recipient (line %d), ignored\n", cfid);
565 else currcp->stoprule = 1;
566 firsttoken = 0;
567 }
568 else if ((pstate == P_RECIP) && (strcasecmp(p, "UNMATCHED") == 0)) {
569 if (!currcp) errprintf("UNMATCHED used without a recipient (line %d), ignored\n", cfid);
570 else currcp->unmatchedonly = 1;
571 firsttoken = 0;
572 }
573 else if ((pstate == P_RECIP) && (strncasecmp(p, "NOALERT", 7) == 0)) {
574 if (!currcp) errprintf("NOALERT used without a recipient (line %d), ignored\n", cfid);
575 else currcp->noalerts = 1;
576 firsttoken = 0;
577 }
578 else if (currule && ((strncasecmp(p, "MAIL", 4) == 0) || mailcmdactive) ) {
579 recip_t *newrcp;
580
581 mailcmdactive = 1;
582 newrcp = (recip_t *)calloc(1, sizeof(recip_t));
583 newrcp->cfid = cfid;
584 newrcp->method = M_MAIL;
585 newrcp->format = ALERTFORM_TEXT;
586
587 if (strncasecmp(p, "MAIL=", 5) == 0) {
588 p += 5;
589 }
590 else if (strcasecmp(p, "MAIL") == 0) {
591 p = strtok(NULL, " \t");
592 }
593 else {
594 /* Second recipient on a rule - do nothing */
595 }
596
597 if (p) {
598 newrcp->recipient = strdup(p);
599 newrcp->interval = defaultinterval;
600 currcp = newrcp;
601 if (curlinerecips == NULL) curlinerecips = newrcp;
602 pstate = P_RECIP;
603
604 if (currule->recipients == NULL)
605 currule->recipients = rcptail = newrcp;
606 else {
607 rcptail->next = newrcp;
608 rcptail = newrcp;
609 }
610 }
611 else {
612 errprintf("Ignoring MAIL with no recipient at line %d\n", cfid);
613 xfree(newrcp);
614 }
615 firsttoken = 0;
616 }
617 else if (currule && ((strncasecmp(p, "SCRIPT", 6) == 0) || scriptcmdactive)) {
618 recip_t *newrcp;
619
620 scriptcmdactive = 1;
621 newrcp = (recip_t *)calloc(1, sizeof(recip_t));
622 newrcp->cfid = cfid;
623 newrcp->method = M_SCRIPT;
624 newrcp->format = ALERTFORM_SCRIPT;
625
626 if (strncasecmp(p, "SCRIPT=", 7) == 0) {
627 p += 7;
628 newrcp->scriptname = strdup(p);
629 p = strtok(NULL, " \t");
630 }
631 else if (strcasecmp(p, "SCRIPT") == 0) {
632 p = strtok(NULL, " \t");
633 if (p) {
634 newrcp->scriptname = strdup(p);
635 p = strtok(NULL, " \t");
636 }
637 else {
638 errprintf("Invalid SCRIPT command at line %d\n", cfid);
639 }
640 }
641 else {
642 /* A second recipient for the same script as the previous one */
643 newrcp->scriptname = strdup(currcp->scriptname);
644 }
645
646 if (p) {
647 newrcp->recipient = strdup(p);
648 newrcp->interval = defaultinterval;
649 currcp = newrcp;
650 if (curlinerecips == NULL) curlinerecips = newrcp;
651 pstate = P_RECIP;
652
653 if (currule->recipients == NULL)
654 currule->recipients = rcptail = newrcp;
655 else {
656 rcptail->next = newrcp;
657 rcptail = newrcp;
658 }
659 }
660 else {
661 errprintf("Ignoring SCRIPT with no recipient at line %d\n", cfid);
662 if (newrcp->scriptname) xfree(newrcp->scriptname);
663 xfree(newrcp);
664 }
665 firsttoken = 0;
666 }
667 else if (currule && (strncasecmp(p, "IGNORE", 6) == 0)) {
668 recip_t *newrcp;
669
670 newrcp = (recip_t *)calloc(1, sizeof(recip_t));
671 newrcp->cfid = cfid;
672 newrcp->method = M_IGNORE;
673 newrcp->format = ALERTFORM_NONE;
674 newrcp->interval = defaultinterval;
675 newrcp->stoprule = 1;
676 currcp = newrcp;
677 if (curlinerecips == NULL) curlinerecips = newrcp;
678 pstate = P_RECIP;
679
680 if (currule->recipients == NULL)
681 currule->recipients = rcptail = newrcp;
682 else {
683 rcptail->next = newrcp;
684 rcptail = newrcp;
685 }
686
687 firsttoken = 0;
688 }
689 else {
690 errprintf("Ignored unknown/unexpected token '%s' at line %d\n", p, cfid);
691 }
692
693 if (p) p = strtok(NULL, " \t");
694 }
695
696 if (curlinerecips && currcp && (curlinerecips != currcp)) {
697 /* We have multiple recipients on one line. Make sure criteria etc. get copied */
698 recip_t *rwalk;
699
700 /* All criteria etc. have been set on the last recipient (currcp) */
701 for (rwalk = curlinerecips; (rwalk != currcp); rwalk = rwalk->next) {
702 rwalk->format = currcp->format;
703 rwalk->interval = currcp->interval;
704 rwalk->criteria = currcp->criteria;
705 rwalk->noalerts = currcp->noalerts;
706 }
707 }
708 }
709
710 flush_rule(currule);
711 stackfclose(fd);
712 freestrbuffer(inbuf);
713
714 MEMUNDEFINE(cfline);
715 MEMUNDEFINE(fn);
716
717 return 1;
718 }
719
dump_criteria(criteria_t * crit,int isrecip)720 static void dump_criteria(criteria_t *crit, int isrecip)
721 {
722 if (crit->pagespec) printf("PAGE=%s ", crit->pagespec);
723 if (crit->expagespec) printf("EXPAGE=%s ", crit->expagespec);
724 if (crit->dgspec) printf("DISPLAYGROUP=%s ", crit->dgspec);
725 if (crit->exdgspec) printf("EXDISPLAYGROUP=%s ", crit->exdgspec);
726 if (crit->hostspec) printf("HOST=%s ", crit->hostspec);
727 if (crit->exhostspec) printf("EXHOST=%s ", crit->exhostspec);
728 if (crit->svcspec) printf("SERVICE=%s ", crit->svcspec);
729 if (crit->exsvcspec) printf("EXSERVICE=%s ", crit->exsvcspec);
730 if (crit->classspec) printf("CLASS=%s ", crit->classspec);
731 if (crit->exclassspec) printf("EXCLASS=%s ", crit->exclassspec);
732 if (crit->groupspec) printf("GROUP=%s ", crit->groupspec);
733 if (crit->exgroupspec) printf("EXGROUP=%s ", crit->exgroupspec);
734 if (crit->colors) {
735 int i, first = 1;
736
737 printf("COLOR=");
738 for (i = 0; (i < COL_COUNT); i++) {
739 if ((1 << i) & crit->colors) {
740 dbgprintf("first=%d, i=%d\n", first, i);
741 printf("%s%s", (first ? "" : ","), colorname(i));
742 first = 0;
743 }
744 }
745 printf(" ");
746 }
747
748 if (crit->timespec) printf("TIME=%s ", crit->timespec);
749 if (crit->extimespec) printf("EXTIME=%s ", crit->extimespec);
750 if (crit->minduration) printf("DURATION>%d ", (crit->minduration / 60));
751 if (crit->maxduration) printf("DURATION<%d ", (crit->maxduration / 60));
752 if (isrecip) {
753 switch (crit->sendrecovered) {
754 case SR_UNKNOWN: break;
755 case SR_WANTED: printf("RECOVERED "); break;
756 case SR_NOTWANTED: printf("NORECOVERED "); break;
757 }
758 switch (crit->sendnotice) {
759 case SR_UNKNOWN: break;
760 case SR_WANTED: printf("NOTICE "); break;
761 case SR_NOTWANTED: printf("NONOTICE "); break;
762 }
763 }
764 }
765
dump_alertconfig(int showlines)766 void dump_alertconfig(int showlines)
767 {
768 rule_t *rulewalk;
769 recip_t *recipwalk;
770
771 for (rulewalk = rulehead; (rulewalk); rulewalk = rulewalk->next) {
772 if (showlines) printf("%5d\t", rulewalk->cfid);
773
774 dump_criteria(rulewalk->criteria, 0);
775 printf("\n");
776
777 for (recipwalk = rulewalk->recipients; (recipwalk); recipwalk = recipwalk->next) {
778 printf("\t");
779 switch (recipwalk->method) {
780 case M_MAIL : printf("MAIL %s ", recipwalk->recipient); break;
781 case M_SCRIPT : printf("SCRIPT %s %s ", recipwalk->scriptname, recipwalk->recipient); break;
782 case M_IGNORE : printf("IGNORE "); break;
783 }
784 switch (recipwalk->format) {
785 case ALERTFORM_TEXT : printf("FORMAT=TEXT "); break;
786 case ALERTFORM_PLAIN : printf("FORMAT=PLAIN "); break;
787 case ALERTFORM_SMS : printf("FORMAT=SMS "); break;
788 case ALERTFORM_PAGER : printf("FORMAT=PAGER "); break;
789 case ALERTFORM_SCRIPT: printf("FORMAT=SCRIPT "); break;
790 case ALERTFORM_NONE : break;
791 }
792 printf("REPEAT=%d ", (int)(recipwalk->interval / 60));
793 if (recipwalk->criteria) dump_criteria(recipwalk->criteria, 1);
794 if (recipwalk->unmatchedonly) printf("UNMATCHED ");
795 if (recipwalk->stoprule) printf("STOP ");
796 if (recipwalk->noalerts) printf("NOALERT ");
797 printf("\n");
798 }
799 printf("\n");
800 }
801 }
802
803 int stoprulefound = 0;
804
criteriamatch(activealerts_t * alert,criteria_t * crit,criteria_t * rulecrit,int * anymatch,time_t * nexttime)805 static int criteriamatch(activealerts_t *alert, criteria_t *crit, criteria_t *rulecrit, int *anymatch, time_t *nexttime)
806 {
807 /*
808 * See if the "crit" matches the "alert".
809 * Match on pagespec, dgspec, hostspec, svcspec, classspec, groupspec, colors, timespec, extimespec, minduration, maxduration, sendrecovered
810 */
811
812 static char *pgnames = NULL;
813 const char *dgname = NULL;
814 int pgmatchres, pgexclres;
815 time_t duration = (getcurrenttime(NULL) - alert->eventstart);
816 int result, cfid = 0;
817 char *pgtok, *cfline = NULL;
818 void *hinfo = hostinfo(alert->hostname);
819
820 if (!hinfo) {
821 logprintf("Checking criteria for host '%s', which is not yet defined; some alerts may not immediately fire\n", alert->hostname);
822 if (localalertmode) hinfo = localhostinfo(alert->hostname);
823 };
824
825 /* The top-level page needs a name - cannot match against an empty string */
826 if (pgnames) xfree(pgnames);
827 pgnames = strdup((*alert->location == '\0') ? "/" : alert->location);
828 dgname = hinfo ? textornull(xmh_item(hinfo, XMH_DGNAME)) : strdup("");
829
830 if (crit) { cfid = crit->cfid; cfline = crit->cfline; }
831 if (!cfid && rulecrit) cfid = rulecrit->cfid;
832 if (!cfline && rulecrit) cfline = rulecrit->cfline;
833 if (!cfline) cfline = "<undefined>";
834
835 traceprintf("Matching host:service:dgroup:page '%s:%s:%s:%s' against rule line %d\n",
836 alert->hostname, alert->testname, dgname, alert->location, cfid);
837
838 if (alert->state == A_PAGING) {
839 /* Check max-duration now - it's fast and easy. */
840 if (crit && crit->maxduration && (duration > crit->maxduration)) {
841 traceprintf("Failed '%s' (max. duration %d>%d)\n", cfline, duration, crit->maxduration);
842 if (!printmode) return 0;
843 }
844 }
845
846 if (crit && crit->classspec && !namematch(alert->classname, crit->classspec, crit->classspecre)) {
847 traceprintf("Failed '%s' (class not in include list)\n", cfline);
848 return 0;
849 }
850 if (crit && crit->exclassspec && namematch(alert->classname, crit->exclassspec, crit->exclassspecre)) {
851 traceprintf("Failed '%s' (class excluded)\n", cfline);
852 return 0;
853 }
854
855 /* alert->groups is a comma-separated list of groups, so it needs some special handling */
856 /*
857 * NB: Don't check groups when RECOVERED - the group list for recovery messages is always empty.
858 * It doesn't matter if we match a recipient who was not in the group that originally
859 * got the alert - we will later check who has received the alert, and only those that
860 * have will get the recovery message.
861 */
862 if (crit && (crit->groupspec || crit->exgroupspec) && (alert->state != A_RECOVERED)) {
863 SBUF_DEFINE(grouplist);
864 char *tokptr;
865
866 if ((alert->groups && (*(alert->groups)))) {
867 SBUF_MALLOC(grouplist, strlen(alert->groups));
868 strncpy(grouplist, alert->groups, grouplist_buflen);
869 }
870
871 if (crit->groupspec) {
872 char *onegroup;
873 int iswanted = 0;
874
875 if (grouplist) {
876 /* There is a group label on the alert, so it must match */
877 onegroup = strtok_r(grouplist, ",", &tokptr);
878 while (onegroup && !iswanted) {
879 iswanted = (namematch(onegroup, crit->groupspec, crit->groupspecre));
880 onegroup = strtok_r(NULL, ",", &tokptr);
881 }
882 }
883
884 if (!iswanted) {
885 /*
886 * Either the alert had a group list that didn't match, or
887 * there was no group list and the rule listed one.
888 * In both cases, it's a failed match.
889 */
890 traceprintf("Failed '%s' (group not in include list)\n", cfline);
891 if (grouplist) xfree(grouplist);
892 return 0;
893 }
894 }
895
896 if (crit->exgroupspec && grouplist) {
897 char *onegroup;
898
899 /* Excluded groups are only handled when the alert does have a group list */
900
901 strncpy(grouplist, alert->groups, grouplist_buflen); /* Might have been used in the include list */
902 onegroup = strtok_r(grouplist, ",", &tokptr);
903 while (onegroup) {
904 if (namematch(onegroup, crit->exgroupspec, crit->exgroupspecre)) {
905 traceprintf("Failed '%s' (group excluded)\n", cfline);
906 xfree(grouplist);
907 return 0;
908 }
909 onegroup = strtok_r(NULL, ",", &tokptr);
910 }
911 }
912
913 if (grouplist) xfree(grouplist);
914 }
915
916 pgmatchres = pgexclres = -1;
917 pgtok = strtok(pgnames, ",");
918 while (pgtok) {
919 if (crit && crit->pagespec && (pgmatchres != 1))
920 pgmatchres = (namematch(pgtok, crit->pagespec, crit->pagespecre) ? 1 : 0);
921
922 if (crit && crit->expagespec && (pgexclres != 1))
923 pgexclres = (namematch(pgtok, crit->expagespec, crit->expagespecre) ? 1 : 0);
924
925 pgtok = strtok(NULL, ",");
926 }
927 if (pgexclres == 1) {
928 traceprintf("Failed '%s' (pagename excluded)\n", cfline);
929 return 0;
930 }
931 if (pgmatchres == 0) {
932 traceprintf("Failed '%s' (pagename not in include list)\n", cfline);
933 return 0;
934 }
935
936 if (crit && crit->dgspec && !namematch(dgname, crit->dgspec, crit->dgspecre)) {
937 traceprintf("Failed '%s' (displaygroup not in include list)\n", cfline);
938 return 0;
939 }
940 if (crit && crit->exdgspec && namematch(dgname, crit->exdgspec, crit->exdgspecre)) {
941 traceprintf("Failed '%s' (displaygroup excluded)\n", cfline);
942 return 0;
943 }
944
945 if (crit && crit->hostspec && !namematch(alert->hostname, crit->hostspec, crit->hostspecre)) {
946 traceprintf("Failed '%s' (hostname not in include list)\n", cfline);
947 return 0;
948 }
949 if (crit && crit->exhostspec && namematch(alert->hostname, crit->exhostspec, crit->exhostspecre)) {
950 traceprintf("Failed '%s' (hostname excluded)\n", cfline);
951 return 0;
952 }
953
954 if (crit && crit->svcspec && !namematch(alert->testname, crit->svcspec, crit->svcspecre)) {
955 traceprintf("Failed '%s' (service not in include list)\n", cfline);
956 return 0;
957 }
958 if (crit && crit->exsvcspec && namematch(alert->testname, crit->exsvcspec, crit->exsvcspecre)) {
959 traceprintf("Failed '%s' (service excluded)\n", cfline);
960 return 0;
961 }
962
963 if (alert->state == A_NOTIFY) {
964 /*
965 * Don't do the check until we are checking individual recipients (rulecrit is set).
966 * You don't need to have NOTICE on the top-level rule, it's enough if a recipient
967 * has it set. However, we do want to allow there to be a default defined in the
968 * rule; but it doesn't take effect until we start checking the recipients.
969 */
970 if (rulecrit) {
971 int n = (crit ? crit->sendnotice : -1);
972 traceprintf("Checking NOTICE setting %d (rule:%d)\n", n, rulecrit->sendnotice);
973 if (crit && (crit->sendnotice == SR_NOTWANTED)) result = 0; /* Explicit NONOTICE */
974 else if (crit && (crit->sendnotice == SR_WANTED)) result = 1; /* Explicit NOTICE */
975 else result = (rulecrit->sendnotice == SR_WANTED); /* Not set, but rule has NOTICE */
976 }
977 else {
978 result = 1;
979 }
980
981 if (!result) traceprintf("Failed '%s' (notice not wanted)\n", cfline);
982 return result;
983 }
984
985 /* At this point, we know the configuration may result in an alert. */
986 if (anymatch) (*anymatch)++;
987
988 /*
989 * Duration checks should be done on real paging messages only.
990 * Not on recovery- or notify-messages.
991 */
992 if (alert->state == A_PAGING) {
993 if (crit && crit->minduration && (duration < crit->minduration)) {
994 if (nexttime) {
995 time_t mynext = alert->eventstart + crit->minduration;
996
997 if ((*nexttime == -1) || (*nexttime > mynext)) *nexttime = mynext;
998 }
999 traceprintf("Failed '%s' (min. duration %d<%d)\n", cfline, duration, crit->minduration);
1000 if (!printmode) return 0;
1001 }
1002 }
1003
1004 /*
1005 * Time restrictions apply to ALL messages.
1006 * Before 4.2, these were only applied to ALERT messages,
1007 * not RECOVERED and NOTIFY messages. This caused some
1008 * unfortunate alerts in the middle of the night because
1009 * some random system recovered ... not good. So apply
1010 * this check to all messages.
1011 */
1012 if (crit && ((!hinfo) || ( (crit->timespec && !timematch(xmh_item(hinfo, XMH_HOLIDAYS), crit->timespec)) ||
1013 (crit->extimespec && timematch(xmh_item(hinfo, XMH_HOLIDAYS), crit->extimespec)) ) ) ) {
1014 /* Try again in a minute */
1015 if (nexttime) *nexttime = getcurrenttime(NULL) + 60;
1016 traceprintf("Failed '%s' (time/extime criteria)\n", cfline);
1017 if (!printmode) return 0;
1018 }
1019
1020 /* Check color. For RECOVERED messages, this holds the color of the alert, not the recovery state */
1021 if (crit && crit->colors) {
1022 result = (((1 << alert->color) & crit->colors) != 0);
1023 if (printmode) return 1;
1024 }
1025 else {
1026 result = (((1 << alert->color) & defaultcolors) != 0);
1027 if (printmode) return 1;
1028 }
1029 if (!result) {
1030 traceprintf("Failed '%s' (color)\n", cfline);
1031 return result;
1032 }
1033
1034 if ((alert->state == A_RECOVERED) || (alert->state == A_DISABLED)) {
1035 /*
1036 * Don't do the check until we are checking individual recipients (rulecrit is set).
1037 * You don't need to have RECOVERED on the top-level rule, it's enough if a recipient
1038 * has it set. However, we do want to allow there to be a default defined in the
1039 * rule; but it doesn't take effect until we start checking the recipients.
1040 */
1041 if (rulecrit) {
1042 int n = (crit ? crit->sendrecovered : -1);
1043 traceprintf("Checking recovered setting %d (rule:%d)\n", n, rulecrit->sendrecovered);
1044 if (crit && (crit->sendrecovered == SR_NOTWANTED)) result = 0; /* Explicit NORECOVERED */
1045 else if (crit && (crit->sendrecovered == SR_WANTED)) result = 1; /* Explicit RECOVERED */
1046 else result = (rulecrit->sendrecovered == SR_WANTED); /* Not set, but rule has RECOVERED */
1047 }
1048 else {
1049 result = 1;
1050 }
1051
1052 if (printmode) return result;
1053 }
1054
1055 if (result) {
1056 traceprintf("*** Match with '%s' ***\n", cfline);
1057 }
1058
1059 return result;
1060 }
1061
next_recipient(activealerts_t * alert,int * first,int * anymatch,time_t * nexttime)1062 recip_t *next_recipient(activealerts_t *alert, int *first, int *anymatch, time_t *nexttime)
1063 {
1064 static rule_t *rulewalk = NULL;
1065 static recip_t *recipwalk = NULL;
1066
1067 if (anymatch) *anymatch = 0;
1068
1069 do {
1070 if (*first) {
1071 /* Start at beginning of rules-list and find the first matching rule. */
1072 *first = 0;
1073 rulewalk = rulehead;
1074 while (rulewalk && !criteriamatch(alert, rulewalk->criteria, NULL, NULL, NULL)) rulewalk = rulewalk->next;
1075 if (rulewalk) {
1076 /* Point recipwalk at the list of possible candidates */
1077 dbgprintf("Found a first matching rule\n");
1078 recipwalk = rulewalk->recipients;
1079 }
1080 else {
1081 /* No matching rules */
1082 dbgprintf("Found no first matching rule\n");
1083 recipwalk = NULL;
1084 }
1085 }
1086 else {
1087 if (!recipwalk) {
1088 /* Should not happen! */
1089 }
1090 else if (recipwalk->next) {
1091 /* Check the next recipient in the current rule */
1092 recipwalk = recipwalk->next;
1093 }
1094 else {
1095 /* End of recipients in current rule. Go to the next matching rule */
1096 do {
1097 rulewalk = rulewalk->next;
1098 } while (rulewalk && !criteriamatch(alert, rulewalk->criteria, NULL, NULL, NULL));
1099
1100 if (rulewalk) {
1101 /* Point recipwalk at the list of possible candidates */
1102 dbgprintf("Found a secondary matching rule\n");
1103 recipwalk = rulewalk->recipients;
1104 }
1105 else {
1106 /* No matching rules */
1107 dbgprintf("No more secondary matching rule\n");
1108 recipwalk = NULL;
1109 }
1110 }
1111 }
1112 } while (rulewalk && recipwalk && !criteriamatch(alert, recipwalk->criteria, rulewalk->criteria, anymatch, nexttime));
1113
1114 stoprulefound = (recipwalk && recipwalk->stoprule);
1115
1116 printrule = rulewalk;
1117 return recipwalk;
1118 }
1119
1120
have_recipient(activealerts_t * alert,int * anymatch)1121 int have_recipient(activealerts_t *alert, int *anymatch)
1122 {
1123 int first = 1;
1124
1125 return (next_recipient(alert, &first, anymatch, NULL) != NULL);
1126 }
1127
1128
alert_printmode(int on)1129 void alert_printmode(int on)
1130 {
1131 printmode = on;
1132 }
1133
print_alert_recipients(activealerts_t * alert,strbuffer_t * buf)1134 void print_alert_recipients(activealerts_t *alert, strbuffer_t *buf)
1135 {
1136 char *normalfont = "COLOR=\"#FFFFCC\" FACE=\"Tahoma, Arial, Helvetica\"";
1137 char *stopfont = "COLOR=\"#33ebf4\" FACE=\"Tahoma, Arial, Helvetica\"";
1138
1139 int first = 1;
1140 recip_t *recip;
1141 char l[4096];
1142 int count = 0;
1143 char *p, *fontspec;
1144 char codes[25];
1145 unsigned int codes_bytesleft;
1146
1147 MEMDEFINE(l);
1148 MEMDEFINE(codes);
1149
1150 if (printmode == 2) {
1151 /* For print-out usage - e.g. confreport.cgi */
1152 normalfont = "COLOR=\"#000000\" FACE=\"Tahoma, Arial, Helvetica\"";
1153 stopfont = "COLOR=\"#FF0000\" FACE=\"Tahoma, Arial, Helvetica\"";
1154 }
1155
1156 fontspec = normalfont;
1157 stoprulefound = 0;
1158 while ((recip = next_recipient(alert, &first, NULL, NULL)) != NULL) {
1159 int mindur = 0, maxdur = INT_MAX;
1160 char *timespec = NULL; char *extimespec = NULL;
1161 int colors = defaultcolors;
1162 int i, firstcolor = 1;
1163 int recovered = 0, notice = 0;
1164
1165 count++;
1166
1167 addtobuffer(buf, "<tr>");
1168 if (count == 1) {
1169 snprintf(l, sizeof(l), "<td valign=top rowspan=###>%s</td>", alert->testname);
1170 addtobuffer(buf, l);
1171 }
1172
1173 /*
1174 * The min/max duration of an alert can be controlled by both the actual rule,
1175 * and by the recipient specification.
1176 * The rule must be fulfilled before the recipient even gets into play, so
1177 * if there is a min/max duration on the rule then this becomes the default
1178 * and recipient-specific settings can only increase the minduration/decrease
1179 * the maxduration.
1180 * On the other hand, if there is no rule-setting then the recipient-specific
1181 * settings determine everything.
1182 */
1183 if (printrule->criteria && printrule->criteria->minduration) mindur = printrule->criteria->minduration;
1184 if (recip->criteria && recip->criteria->minduration && (recip->criteria->minduration > mindur)) mindur = recip->criteria->minduration;
1185 if (printrule->criteria && printrule->criteria->maxduration) maxdur = printrule->criteria->maxduration;
1186 if (recip->criteria && recip->criteria->maxduration && (recip->criteria->maxduration < maxdur)) maxdur = recip->criteria->maxduration;
1187 if (printrule->criteria && printrule->criteria->timespec) timespec = printrule->criteria->timespec;
1188 if (printrule->criteria && printrule->criteria->extimespec) extimespec = printrule->criteria->extimespec;
1189 if (recip->criteria && recip->criteria->timespec) {
1190 if (timespec == NULL) timespec = recip->criteria->timespec;
1191 else errprintf("Cannot handle nested timespecs yet\n");
1192 }
1193 if (recip->criteria && recip->criteria->extimespec) {
1194 if (extimespec == NULL) extimespec = recip->criteria->extimespec;
1195 else errprintf("Cannot handle nested extimespecs yet\n");
1196 }
1197
1198 if (printrule->criteria && printrule->criteria->colors) colors = (colors & printrule->criteria->colors);
1199 if (recip->criteria && recip->criteria->colors) colors = (colors & recip->criteria->colors);
1200
1201 /*
1202 * Recoveries are sent if
1203 * - there are no recipient criteria, and the rule says yes;
1204 * - the recipient criteria does not have a RECOVERED setting, and the rule says yes;
1205 * - the recipient criteria says yes.
1206 */
1207 if ( (!recip->criteria && printrule->criteria && (printrule->criteria->sendrecovered == SR_WANTED)) ||
1208 (recip->criteria && (printrule->criteria->sendrecovered == SR_WANTED) && (recip->criteria->sendrecovered == SR_UNKNOWN)) ||
1209 (recip->criteria && (recip->criteria->sendrecovered == SR_WANTED)) ) recovered = 1;
1210
1211 if ( (!recip->criteria && printrule->criteria && (printrule->criteria->sendnotice == SR_WANTED)) ||
1212 (recip->criteria && (printrule->criteria->sendnotice == SR_WANTED) && (recip->criteria->sendnotice == SR_UNKNOWN)) ||
1213 (recip->criteria && (recip->criteria->sendnotice == SR_WANTED)) ) notice = 1;
1214
1215 *codes = '\0';
1216 codes_bytesleft = sizeof(codes);
1217 if (recip->method == M_IGNORE) {
1218 recip->recipient = "-- ignored --";
1219 }
1220 if (recip->noalerts) { if (*codes) strncat(codes, ",A", codes_bytesleft); else strncat(codes, "-A", codes_bytesleft); codes_bytesleft -= 2; }
1221 if (recovered && !recip->noalerts) { if (*codes) strncat(codes, ",R", codes_bytesleft); else strncat(codes, "R", codes_bytesleft); codes_bytesleft -= 2; }
1222 if (notice) { if (*codes) strncat(codes, ",N", codes_bytesleft); else strncat(codes, "N", codes_bytesleft); codes_bytesleft -= 2; }
1223 if (recip->stoprule) { if (*codes) strncat(codes, ",S", codes_bytesleft); else strncat(codes, "S", codes_bytesleft); codes_bytesleft -= 2; }
1224 if (recip->unmatchedonly) { if (*codes) strncat(codes, ",U", codes_bytesleft); else strncat(codes, "U", codes_bytesleft); codes_bytesleft -= 2; }
1225
1226 if (strlen(codes) == 0)
1227 snprintf(l, sizeof(l), "<td><font %s>%s</font></td>", fontspec, recip->recipient);
1228 else
1229 snprintf(l, sizeof(l), "<td><font %s>%s (%s)</font></td>", fontspec, recip->recipient, codes);
1230 addtobuffer(buf, l);
1231
1232 snprintf(l, sizeof(l), "<td align=center>%s</td>", durationstring(mindur));
1233 addtobuffer(buf, l);
1234
1235 /* maxdur=INT_MAX means "no max duration". So set it to 0 for durationstring() to do the right thing */
1236 if (maxdur == INT_MAX) maxdur = 0;
1237 snprintf(l, sizeof(l), "<td align=center>%s</td>", durationstring(maxdur));
1238 addtobuffer(buf, l);
1239
1240 snprintf(l, sizeof(l), "<td align=center>%s</td>", durationstring(recip->interval));
1241 addtobuffer(buf, l);
1242
1243 if (timespec && extimespec) snprintf(l, sizeof(l), "<td align=center>%s, except %s</td>", timespec, extimespec);
1244 else if (timespec) snprintf(l, sizeof(l), "<td align=center>%s</td>", timespec);
1245 else if (extimespec) snprintf(l, sizeof(l), "<td align=center>all, except %s</td>", extimespec);
1246 else strncpy(l, "<td align=center>-</td>", sizeof(l));
1247 addtobuffer(buf, l);
1248
1249 addtobuffer(buf, "<td>");
1250 for (i = 0; (i < COL_COUNT); i++) {
1251 if ((1 << i) & colors) {
1252 snprintf(l, sizeof(l), "%s%s", (firstcolor ? "" : ","), colorname(i));
1253 addtobuffer(buf, l);
1254 firstcolor = 0;
1255 }
1256 }
1257 addtobuffer(buf, "</td>");
1258
1259 if (stoprulefound) fontspec = stopfont;
1260
1261 addtobuffer(buf, "</tr>\n");
1262 }
1263
1264 /* This is hackish - patch up the "rowspan" value, so it matches the number of recipient lines */
1265 snprintf(l, sizeof(l), "%d ", count);
1266 p = strstr(STRBUF(buf), "rowspan=###");
1267 if (p) { p += strlen("rowspan="); memcpy(p, l, 3); }
1268
1269 MEMUNDEFINE(l);
1270 MEMUNDEFINE(codes);
1271 }
1272
1273