1 /* SAMHAIN file system integrity testing                                   */
2 /* Copyright (C) 2008 Rainer Wichmann                                      */
3 /*                                                                         */
4 /*  This program is free software; you can redistribute it                 */
5 /*  and/or modify                                                          */
6 /*  it under the terms of the GNU General Public License as                */
7 /*  published by                                                           */
8 /*  the Free Software Foundation; either version 2 of the License, or      */
9 /*  (at your option) any later version.                                    */
10 /*                                                                         */
11 /*  This program is distributed in the hope that it will be useful,        */
12 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
13 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
14 /*  GNU General Public License for more details.                           */
15 /*                                                                         */
16 /*  You should have received a copy of the GNU General Public License      */
17 /*  along with this program; if not, write to the Free Software            */
18 /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
19 
20 #include "config_xor.h"
21 
22 #if defined(HAVE_PTHREAD_MUTEX_RECURSIVE)
23 #define _XOPEN_SOURCE 500
24 #endif
25 
26 #if defined(GCC_VERSION_MAJOR) && !defined(__clang__)
27 #if (GCC_VERSION_MAJOR > 4) || ((GCC_VERSION_MAJOR == 4) && (GCC_VERSION_MINOR > 8))
28 #pragma GCC diagnostic ignored "-Wclobbered"
29 #endif
30 #endif
31 
32 #include <string.h>
33 #include <time.h>
34 
35 #if defined(SH_WITH_MAIL)
36 
37 #undef  FIL__
38 #define FIL__  _("sh_nmail.c")
39 
40 #include "samhain.h"
41 #include "sh_pthread.h"
42 #include "sh_mem.h"
43 #include "sh_mail.h"
44 #include "sh_tiger.h"
45 #include "sh_string.h"
46 #include "sh_utils.h"
47 #include "sh_fifo.h"
48 #include "sh_filter.h"
49 #include "sh_mail_int.h"
50 
51 SH_MUTEX_INIT(mutex_listall, PTHREAD_MUTEX_INITIALIZER);
52 SH_MUTEX_INIT(mutex_flush_l, PTHREAD_MUTEX_INITIALIZER);
53 
54 /* Pointer to last address */
55 
56 static struct alias * last = NULL;
57 
58 /* List of mail recipients */
59 
60 static struct alias * recipient_list = NULL;
61 
62 static struct alias * compiled_recipient_list = NULL;
63 static sh_filter_type compiled_mail_filter = SH_FILT_INIT;
64 
65 /* List of mail aliases */
66 
67 static struct alias * alias_list = NULL;
68 
69 /* List of all recipients */
70 
71 struct alias * all_recipients = NULL;
72 
73 /* Check if addr is in list. If list is all_recipients,
74  * must iterate over ->all_next instead of ->next
75  */
check_double(const char * str,struct alias * list,int isAll)76 static int check_double (const char * str, struct alias * list, int isAll)
77 {
78   if (str && list)
79     {
80       struct alias * item = list;
81 
82       while (item)
83 	{
84 	  if (0 == strcmp(sh_string_str(item->recipient), str))
85 	    return -1;
86 	  if (isAll)
87 	    item = item->all_next;
88 	  else
89 	    item = item->next;
90 	}
91     }
92   return 0;
93 }
94 
95 /* Add recipient to 'list' AND to all_recipients. If
96  * it already is in all_recipients, mark it as an alias
97  * (isAlias = 1).
98  */
add_recipient_intern(const char * str,struct alias * list)99 struct alias * add_recipient_intern(const char * str,
100 				    struct alias * list)
101 {
102   if (str)
103     {
104       struct alias * new  = SH_ALLOC(sizeof(struct alias));
105       new->next           = list;
106       new->mx_list        = NULL;
107       new->mail_filter    = NULL;
108       new->recipient_list = NULL;
109       new->severity       = (-1);
110       new->send_mail      = 0;
111       new->isAlias        = 0;
112       new->recipient      = sh_string_new_from_lchar(str, strlen(str));
113       list                = new;
114 
115       SH_MUTEX_LOCK_UNSAFE(mutex_listall);
116       if (0 != check_double(str, all_recipients, S_TRUE))
117 	{
118 	  new->isAlias    = 1;
119 	}
120       new->all_next       = all_recipients;
121       all_recipients      = new;
122       SH_MUTEX_UNLOCK_UNSAFE(mutex_listall);
123     }
124   return list;
125 }
126 
sh_nmail_close_recipient(const char * str)127 int sh_nmail_close_recipient(const char * str)
128 {
129   (void) str;
130 
131   if (last)
132     {
133       last = NULL;
134       return 0;
135     }
136   return -1;
137 }
138 
139 /* Add a single recipient. Must not be in in
140  * recipient_list already, and not in all_recipients.
141  */
sh_nmail_add_recipient(const char * str)142 int sh_nmail_add_recipient(const char * str)
143 {
144   /* return error if duplicate, or
145    * already defined within an alias list.
146    */
147   if (0 == check_double(str,  recipient_list, S_FALSE) &&
148       0 == check_double(str,  all_recipients, S_TRUE))
149     {
150       recipient_list = add_recipient_intern(str, recipient_list);
151       last           = recipient_list;
152       return 0;
153     }
154   return -1;
155 }
156 
157 /* Add a compiled-in address. These share the compiled_mail_filter
158  */
sh_nmail_add_compiled_recipient(const char * str)159 int sh_nmail_add_compiled_recipient(const char * str)
160 {
161   if (0 == check_double(str,  compiled_recipient_list, S_FALSE))
162     {
163       compiled_recipient_list =
164 	add_recipient_intern(str, compiled_recipient_list);
165       if (compiled_recipient_list)
166 	compiled_recipient_list->mail_filter = &compiled_mail_filter;
167       last           = compiled_recipient_list;
168       return 0;
169     }
170   return -1;
171 }
172 
173 /* Add an alias; format is name ":" comma-delimited_list_of_recipients
174  */
sh_nmail_add_alias(const char * str)175 int sh_nmail_add_alias(const char * str)
176 {
177 #define SH_ALIASES_RECP_NUM 256
178   size_t lengths[SH_ALIASES_RECP_NUM];
179   unsigned int    nfields = SH_ALIASES_RECP_NUM;
180   char * new = sh_util_strdup(str);
181   char * p   = strchr(new, ':');
182   char * q;
183 
184   if (p && strlen(p) > 1)
185     {
186       unsigned int     i;
187       char ** array;
188 
189       *p = '\0'; q = p; ++p;
190       if (strlen(new) > 0)
191 	{
192 	  /* strip trailing space
193 	   */
194 	  --q; while ((q != new) && *q == ' ') { *q = '\0'; --q; }
195 	}
196       else
197 	{
198 	  goto err;
199 	}
200 
201       if (0 == check_double(new, alias_list, S_FALSE))
202 	{
203 	  array = split_array_list(p, &nfields, lengths);
204 
205 	  if (array && nfields > 0)
206 	    {
207 	      struct alias * newalias = NULL;
208 
209 	      /* Enforce that all list members are defined already
210 	       */
211 	      int                nflag = 0;
212 
213 	      for (i = 0; i < nfields; ++i) {
214 		if (0 == check_double(array[i],  all_recipients, S_TRUE))
215 		  nflag = 1; /* not in all_recipients --> bad */
216 	      }
217 
218 	      if (nflag == 0)
219 		{
220 		  newalias                 = SH_ALLOC(sizeof(struct alias));
221 		  newalias->recipient_list = NULL;
222 		  newalias->mail_filter    = NULL;
223 		  newalias->mx_list        = NULL;
224 		  newalias->severity       = (-1);
225 
226 		  /* This is the alias */
227 		  newalias->recipient = sh_string_new_from_lchar(new, strlen(new));
228 
229 		  for (i = 0; i < nfields; ++i)
230 		    {
231 		      if (lengths[i] > 0 &&
232 			  0 == check_double(array[i], newalias->recipient_list, S_FALSE))
233 			{
234 			  newalias->recipient_list =
235 			    add_recipient_intern(array[i], newalias->recipient_list);
236 			}
237 		    }
238 		}
239 
240 	      SH_FREE(array);
241 
242 	      if (newalias == NULL || newalias->recipient_list == NULL)
243 		{
244 		  if (newalias)
245 		    SH_FREE(newalias);
246 		  goto err;
247 		}
248 
249 	      newalias->next = alias_list;
250 	      alias_list     = newalias;
251 	      last           = alias_list;
252 
253 	      SH_FREE(new);
254 	      return 0;
255 	    }
256 	}
257     }
258  err:
259   SH_FREE(new);
260   return -1;
261 }
262 
263 
264 /* <<<<<<<<<<<<<<< Recipient List >>>>>>>>>>>>>>>>>>>>>> */
265 
find_list(const char * alias,int * single)266 static struct alias * find_list (const char * alias, int * single)
267 {
268   struct alias * list   = NULL;
269 
270   *single = 0;
271 
272   if (!alias)
273     {
274       list = all_recipients;
275     }
276   else
277     {
278       struct alias * test = alias_list;
279 
280       while (test)
281 	{
282 	  if (0 == strcmp(alias, sh_string_str(test->recipient)))
283 	    {
284 	      list = test->recipient_list;
285 	      break;
286 	    }
287 	  test = test->next;
288 	}
289 
290       if (!list)
291 	{
292 	  test = recipient_list;
293 	  while (test)
294 	    {
295 	      if (0 == strcmp(alias, sh_string_str(test->recipient)))
296 		{
297 		  list   = test;
298 		  *single = 1;
299 		  break;
300 		}
301 	      test = test->next;
302 	    }
303 	}
304 
305       if (!list)
306 	{
307 	  test = compiled_recipient_list;
308 	  while (test)
309 	    {
310 	      if (0 == strcmp(alias, sh_string_str(test->recipient)))
311 		{
312 		  list   = test;
313 		  *single = 1;
314 		  break;
315 		}
316 	      test = test->next;
317 	    }
318 	}
319     }
320   return list;
321 }
322 
323 /* Returns zero (no) or one (yes). Used to tag messages that are
324  * valid for a given recipient (or mailing list alias).
325  */
sh_nmail_valid_message_for_alias(int level,const char * message,const char * alias,const void * rcv_info)326 int sh_nmail_valid_message_for_alias (int level,
327 				      const char * message,
328 				      const char * alias,
329 				      const void * rcv_info)
330 {
331   const struct alias * rcv = (const struct alias *) rcv_info;
332 
333   if (!alias || 0 == strcmp(alias, sh_string_str(rcv->recipient)))
334     {
335       if ((level & rcv->severity) == 0)
336 	{
337 	  return 0;
338 	}
339 
340       if (rcv->mail_filter)
341 	{
342 	  if (0 != sh_filter_filter(message, rcv->mail_filter))
343 	    {
344 	      return 0;
345 	    }
346 	}
347     }
348 
349   return 1;
350 }
351 
352 /* Returns number of recipients */
353 
354 static
sh_nmail_compute_recipients(int level,const char * message,const char * alias,int flagit)355 int sh_nmail_compute_recipients (int level, const char * message,
356 				 const char * alias, int flagit)
357 {
358   struct alias * list   = NULL;
359   int            single = 0;
360   int            retval = 0;
361 
362   if (flagit)
363     {
364       list = all_recipients;
365       while (list)
366 	{
367 	  list->send_mail = 0;
368 	  list = list->all_next;
369 	}
370       list = NULL;
371     }
372 
373   if (message)
374     {
375       int flag = 0;
376 
377       list = find_list (alias, &single);
378       if (list == all_recipients)
379 	flag = 1;
380 
381       while (list)
382 	{
383 	  /* Check severity
384 	   */
385 	  if ((list->severity & level) == 0)
386 	    {
387 	      if (single) break;
388 	      if (flag)
389 		list = list->all_next;
390 	      else
391 		list = list->next;
392 	      continue;
393 	    }
394 
395 	  /* Check filter
396 	   */
397 	  if (list->mail_filter &&
398 	      0 != sh_filter_filter(message, list->mail_filter))
399 	    {
400 	      if (single) break;
401 	      if (flag)
402 		list = list->all_next;
403 	      else
404 		list = list->next;
405 	      continue;
406 	    }
407 
408 	  /* Mark the entry
409 	   */
410 	  if (flag)
411 	    {
412 	      /* Don't mark aliases
413 	       */
414 	      if (flagit && list->isAlias == 0)
415 		{
416 		  list->send_mail = 1;
417 		}
418 	      list = list->all_next;
419 	    }
420 	  else
421 	    {
422 	      if (flagit)
423 		  list->send_mail = 1;
424 	      list = list->next;
425 	    }
426 	  ++retval;
427 	}
428     }
429   return retval;
430 }
431 
432 /* Is not called from same(recursively) or different thread
433  */
434 static
sh_nmail_flag_recipients(int level,const char * message,const char * alias)435 int sh_nmail_flag_recipients (int level, const char * message,
436 			      const char * alias)
437 {
438   int retval = 0;
439 
440   if (message)
441     {
442       SH_MUTEX_LOCK_UNSAFE(mutex_listall);
443       retval = sh_nmail_compute_recipients (level, message, alias, 1);
444       SH_MUTEX_UNLOCK_UNSAFE(mutex_listall);
445     }
446   return retval;
447 }
448 
449 /* Can be called from same thread with mutex_listall held via sh_nmail_flush()
450  */
451 static
sh_nmail_test_recipients(int level,const char * message,const char * alias)452 int sh_nmail_test_recipients (int level, const char * message,
453 			      const char * alias)
454 {
455   int retval = 0;
456 
457   if (message)
458     {
459       if (0 == SH_MUTEX_TRYLOCK_UNSAFE(mutex_flush_l))
460 	{
461 	  SH_MUTEX_LOCK_UNSAFE(mutex_listall);
462 	  retval = sh_nmail_compute_recipients (level, message, alias, 0);
463 	  SH_MUTEX_UNLOCK_UNSAFE(mutex_listall);
464 	  SH_MUTEX_UNLOCK_UNSAFE(mutex_flush_l);
465 	}
466     }
467   return retval;
468 }
469 
470 /* <<<<<<<<<<<<<<<<<<<  Mail the message  >>>>>>>>>>>>>>>>>>>>>> */
471 
472 SH_MUTEX_RECURSIVE(mutex_nmail_msg);
473 SH_MUTEX_STATIC(nmail_lock, PTHREAD_MUTEX_INITIALIZER);
474 
475 /*
476  * First test list of recipients, then call sh_mail_pushstack().
477  */
sh_nmail_pushstack(int level,const char * message,const char * alias)478 int sh_nmail_pushstack (int level, const char * message,
479 			const char * alias)
480 {
481   int retval = 0;
482 
483   if (0 != sh_nmail_test_recipients (level, message, alias))
484     {
485       retval = sh_mail_pushstack(level, message, alias);
486     }
487   return retval;
488 }
489 
490 static int nmail_count = 0;
491 
492 /*
493  * First mark list of recipients, then call sh_mail_msg().
494  */
sh_nmail_msg(int level,const char * message,const char * alias)495 int sh_nmail_msg (int level, const char * message,
496 		  const char * alias)
497 {
498   volatile int retval = 0;
499 
500   /* Need to:
501    *   -- wait if different thread, and
502    *   -- fail if same thread. */
503   SH_MUTEX_RECURSIVE_INIT(mutex_nmail_msg);
504   SH_MUTEX_RECURSIVE_LOCK(mutex_nmail_msg);
505 
506   /* Only same thread beyond this point. We fail
507    * if count > 0 already. */
508   if (0 == SH_MUTEX_TRYLOCK_UNSAFE(nmail_lock))
509     {
510       ++nmail_count;
511       if (nmail_count != 1)
512 	{
513 	  --nmail_count;
514 	  SH_MUTEX_UNLOCK_UNSAFE(nmail_lock);
515 	  goto cleanup;
516 	}
517       SH_MUTEX_UNLOCK_UNSAFE(nmail_lock);
518 
519       if (0 != sh_nmail_flag_recipients (level, message, alias))
520 	{
521 	  /* Need to keep info for sh_nmail_pushstack()
522 	   */
523 	  SH_MUTEX_LOCK(mutex_listall);
524 	  retval = sh_mail_msg(message);
525 	  SH_MUTEX_UNLOCK(mutex_listall);
526 
527 	  if (retval != 0)
528 	    {
529 	      sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
530 			       retval, MSG_E_SUBGEN,
531 			       _("could not mail immediately"),
532 			       _("sh_nmail_msg") );
533 	      sh_mail_pushstack(level, message, alias);
534 	    }
535 	}
536       SH_MUTEX_LOCK_UNSAFE(nmail_lock);
537       --nmail_count;
538       SH_MUTEX_UNLOCK_UNSAFE(nmail_lock);
539     }
540  cleanup:
541   ; /* label at end of compound statement */
542   SH_MUTEX_RECURSIVE_UNLOCK(mutex_nmail_msg);
543   return retval;
544 }
545 
546 static int sh_nmail_flush_int (void);
547 
sh_nmail_flush()548 int sh_nmail_flush ()
549 {
550   int                retval = 0;
551 
552   if (0 == SH_MUTEX_TRYLOCK_UNSAFE(nmail_lock))
553     {
554       ++nmail_count;
555       if (nmail_count != 1)
556 	{
557 	  --nmail_count;
558 	  SH_MUTEX_UNLOCK_UNSAFE(nmail_lock);
559 	  return retval;
560 	}
561       SH_MUTEX_UNLOCK_UNSAFE(nmail_lock);
562 
563       retval = sh_nmail_flush_int ();
564 
565       SH_MUTEX_LOCK_UNSAFE(nmail_lock);
566       --nmail_count;
567       SH_MUTEX_UNLOCK_UNSAFE(nmail_lock);
568     }
569   return retval;
570 }
571 
572 /* warning: variable ‘list’ might be clobbered by ‘longjmp’ or ‘vfork’*/
573 static struct alias ** list_dummy;
574 
575 /*
576  * Loop over all recipients in stack.
577  * For each distinct one, mark all messages for sending.
578  * Then call sh_mail_msg().
579  */
580 
sh_nmail_flush_int()581 static int sh_nmail_flush_int ()
582 {
583   int                retval = 0;
584   sh_string *        msg    = NULL;
585   sh_string *        smsg   = NULL;
586   struct alias     * list;
587   struct alias     * dlist;
588 
589   /* warning: variable ‘list’ might be clobbered by ‘longjmp’ or ‘vfork’*/
590   list_dummy = &list;
591 
592   SH_MUTEX_LOCK(mutex_listall);
593 
594   /* Reset recipient list
595    */
596   list = all_recipients;
597   while (list)
598     {
599       list->send_mail = 0;
600       list = list->all_next;
601     }
602 
603   /* Check (i) compiled recipients, (b) aliases, (c) single recipients.
604    * For each, tag all messages, then call sh_mail_msg with
605    *  appropriate address list.
606    */
607 
608   reset_list(fifo_mail);
609 
610   /* Compiled recipients. These share threshold and filter,
611    * hence only the first recipient needs to be tested.
612    */
613   list  = compiled_recipient_list;
614 
615   if (list)
616     {
617       msg   = tag_list(fifo_mail, sh_string_str(list->recipient),
618 		       sh_nmail_valid_message_for_alias, list, S_TRUE);
619     }
620 
621   if (msg)
622     {
623       while (list)
624 	{
625 	  list->send_mail = 1;
626 	  list = list->next;
627 	}
628 
629       list = compiled_recipient_list;
630 
631       SH_MUTEX_LOCK(mutex_flush_l);
632       (void) sh_mail_msg(sh_string_str(msg));
633       SH_MUTEX_UNLOCK(mutex_flush_l);
634 
635       sh_string_destroy(&msg);
636 
637       list = compiled_recipient_list;
638       while (list)
639 	{
640 	  list->send_mail = 0;
641 	  list = list->next;
642 	}
643     }
644 
645   /* Aliases
646    */
647   list  = alias_list;
648 
649   while (list) {
650 
651     /* Work through the recipient list. As smsg stores last msg,
652      * we send a batch whenever msg != smsg, and continue from
653      * that point in the recipient list.
654      */
655     struct alias     * lnew;
656 
657     while (list)
658       {
659 	msg   = tag_list(fifo_mail, sh_string_str(list->recipient),
660 			 sh_nmail_valid_message_for_alias, list, S_FALSE);
661 
662 	if (msg)
663 	  {
664 	    if (!smsg) /* init */
665 	      {
666 		smsg      = sh_string_copy(msg);
667 	      }
668 	    else
669 	      {
670 		if (0 != strcmp(sh_string_str(smsg), sh_string_str(msg)))
671 		  {
672 		    /*
673 		     * Don't set list = list->next here, since we want
674 		     * to continue with this recipient in the next batch.
675 		     */
676 		    sh_string_destroy(&msg);
677 		    break;
678 		  }
679 	      }
680 	    lnew = list->recipient_list;
681 	    while (lnew)
682 	      {
683 		lnew->send_mail = 1;
684 		lnew= lnew->next;
685 	      }
686 	    sh_string_destroy(&msg);
687 	  }
688 	list      = list->next;
689       }
690 
691     /* Continue here if smsg != msg */
692 
693     if (smsg)
694       {
695 	SH_MUTEX_LOCK(mutex_flush_l);
696 	(void) sh_mail_msg(sh_string_str(smsg));
697 	SH_MUTEX_UNLOCK(mutex_flush_l);
698 	sh_string_destroy(&smsg);
699       }
700 
701     /* Reset old list of recipients (up to current point in list)
702      * and then continue with list from current point on.
703      */
704     dlist  = alias_list;
705     while (dlist)
706       {
707 	lnew = dlist->recipient_list;
708 	while (lnew)
709 	  {
710 	    lnew->send_mail = 0;
711 	    lnew = lnew->next;
712 	  }
713 	dlist = dlist->next;
714       }
715   }
716 
717 
718   /* Single recipients
719    */
720   list  = recipient_list;
721 
722   while (list) {
723 
724     /* Work through the recipient list. As smsg stores last msg,
725      * we send a batch whenever msg != smsg, and continue from
726      * that point in the recipient list.
727      */
728 
729     while (list)
730       {
731 	msg   = tag_list(fifo_mail, sh_string_str(list->recipient),
732 			 sh_nmail_valid_message_for_alias, list, S_TRUE);
733 
734 	if (msg)
735 	  {
736 	    if (!smsg) /* init */
737 	      {
738 		smsg = sh_string_copy(msg);
739 	      }
740 	    else
741 	      {
742 		if (0 != strcmp(sh_string_str(smsg), sh_string_str(msg)))
743 		  {
744 		    /*
745 		     * Don't set list = list->next here, since we want
746 		     * to continue with this recipient in the next batch.
747 		     */
748 		    sh_string_destroy(&msg);
749 		    break;
750 		  }
751 	      }
752 	    list->send_mail = 1;
753 	    sh_string_destroy(&msg);
754 	  }
755 	list = list->next;
756       }
757 
758     /* Continue here if smsg != msg */
759 
760     if (smsg)
761       {
762 	SH_MUTEX_LOCK(mutex_flush_l);
763 	(void) sh_mail_msg(sh_string_str(smsg));
764 	SH_MUTEX_UNLOCK(mutex_flush_l);
765 	sh_string_destroy(&smsg);
766       }
767 
768     /* Reset old list of recipients (up to current point in list)
769      * and then continue with list from current point on.
770      */
771     dlist  = recipient_list;
772     while (dlist)
773       {
774 	dlist->send_mail = 0;
775 	dlist = dlist->next;
776       }
777   }
778 
779   /* Remove all mails for which no recipient failed
780    */
781 
782   sh.mailNum.alarm_last -= commit_list(fifo_mail);
783   SH_MUTEX_UNLOCK(mutex_listall);
784 
785   return retval;
786 }
787 
788 
789 
790 /* <<<<<<<<<<<<<<<<<<<  Severity  >>>>>>>>>>>>>>>>>>>>>> */
791 
792 /*
793  * -- set severity threshold for recipient or alias
794  */
sh_nmail_set_severity(const char * str)795 int sh_nmail_set_severity (const char * str)
796 {
797   if (last == recipient_list || last == alias_list)
798     {
799       if (0 == sh_error_set_level(str, &(last->severity)))
800 	{
801 	  /* All recipients in alias share the severity
802 	   */
803 	  if (last == alias_list)
804 	    {
805 	      struct alias * ptr = last->recipient_list;
806 
807 	      while (ptr)
808 		{
809 		  ptr->severity = last->severity;
810 		  ptr = ptr->next;
811 		}
812 	    }
813 	  return 0;
814 	}
815     }
816   return (-1);
817 }
818 
819 /* <<<<<<<<<<<<<<<<<<<  Filters >>>>>>>>>>>>>>>>>>>>>> */
820 
821 
sh_nmail_add_generic(const char * str,int flag)822 int sh_nmail_add_generic (const char * str, int flag)
823 {
824   if (last)
825     {
826       if (NULL == last->mail_filter)
827 	last->mail_filter = sh_filter_alloc();
828 
829       /* All recipients in alias share the mail filter
830        */
831       if (last == alias_list)
832 	{
833 	  struct alias * ptr = last->recipient_list;
834 
835 	  while (ptr)
836 	    {
837 	      ptr->mail_filter = last->mail_filter;
838 	      ptr = ptr->next;
839 	    }
840 	}
841 
842       return (sh_filter_add (str, last->mail_filter, flag));
843     }
844   return (-1);
845 }
846 
847 /*
848  * -- add keywords to the OR filter
849  */
sh_nmail_add_or(const char * str)850 int sh_nmail_add_or (const char * str)
851 {
852   return sh_nmail_add_generic(str, SH_FILT_OR);
853 }
854 
855 /*
856  * -- add keywords to the AND filter
857  */
sh_nmail_add_and(const char * str)858 int sh_nmail_add_and (const char * str)
859 {
860   return sh_nmail_add_generic(str, SH_FILT_AND);
861 }
862 
863 /*
864  * -- add keywords to the NOT filter
865  */
sh_nmail_add_not(const char * str)866 int sh_nmail_add_not (const char * str)
867 {
868   return sh_nmail_add_generic(str, SH_FILT_NOT);
869 }
870 
871 
872 /* <<<<<<<<<<<<<<<<<<<  Mailkey per Alias >>>>>>>>>>>>>>>>>>>>>>>>> */
873 
874 #if defined(HAVE_MLOCK) && !defined(HAVE_BROKEN_MLOCK)
875 #include <sys/mman.h>
876 #endif
877 
878 #include "zAVLTree.h"
879 
880 zAVLTree        * mailkeys = NULL;
881 
882 struct alias_mailkey {
883   char * alias;
884   unsigned int mailcount;
885   time_t       id_audit;
886   char   mailkey_old[KEY_LEN+1];
887   char   mailkey_new[KEY_LEN+1];
888 };
889 
sh_nmail_getkey(void const * item)890 static zAVLKey sh_nmail_getkey(void const *item)
891 {
892   const struct alias_mailkey * t = (const struct alias_mailkey *) item;
893   return (zAVLKey) t->alias;
894 }
895 
896 /* Return mailkey for alias. If there's no key yet, create it and
897  * store it in the AVL tree.
898  * This is called from sh_mail_msg,
899  *    which is called from sh_nmail_msg,
900  *        which is protected by a mutex.
901  */
sh_nmail_get_mailkey(const char * alias,char * buf,size_t bufsiz,time_t * id_audit)902 int sh_nmail_get_mailkey (const char * alias, char * buf, size_t bufsiz,
903 			  time_t * id_audit)
904 {
905   char hashbuf[KEYBUF_SIZE];
906 
907  start:
908 
909   if (mailkeys)
910     {
911       struct alias_mailkey * t;
912 
913       if (!alias)
914 	t = (struct alias_mailkey *) zAVLSearch (mailkeys, _("(null)"));
915       else
916 	t = (struct alias_mailkey *) zAVLSearch (mailkeys, alias);
917 
918       if (t)
919 	{
920 	  /* iterate the key
921 	   */
922 	  (void) sl_strlcpy(t->mailkey_new,
923 			    sh_tiger_hash (t->mailkey_old, TIGER_DATA, KEY_LEN,
924 					   hashbuf, sizeof(hashbuf)),
925 			    KEY_LEN+1);
926 	  (void) sl_strlcpy(buf, t->mailkey_new, bufsiz);
927 	  ++(t->mailcount);
928 	}
929       else
930 	{
931 	  t = SH_ALLOC(sizeof(struct alias_mailkey));
932 
933 	  MLOCK(t, sizeof(struct alias_mailkey));
934 
935 	  if (!alias)
936 	    t->alias = sh_util_strdup(_("(null)"));
937 	  else
938 	    t->alias = sh_util_strdup(alias);
939 
940 	  t->mailcount = 0;
941 	  t->id_audit  = time(NULL);
942 
943 	  BREAKEXIT(sh_util_keyinit);
944 	  (void) sh_util_keyinit (t->mailkey_old, KEY_LEN+1);
945 
946 	  /* iterate the key
947 	   */
948 	  (void) sl_strlcpy(t->mailkey_new,
949 			    sh_tiger_hash (t->mailkey_old, TIGER_DATA, KEY_LEN,
950 					   hashbuf, sizeof(hashbuf)),
951 			    KEY_LEN+1);
952 	  (void) sl_strlcpy(buf, t->mailkey_new, bufsiz);
953 	  (void) zAVLInsert(mailkeys, t);
954 	}
955 
956       /* X(n) -> X(n-1)
957        */
958       (void) sl_strlcpy (t->mailkey_old, t->mailkey_new, KEY_LEN+1);
959 
960       *id_audit = t->id_audit;
961 
962       return (t->mailcount);
963     }
964 
965   mailkeys = zAVLAllocTree (sh_nmail_getkey, zAVL_KEY_STRING);
966   goto start;
967 }
968 
969 /* <<<<<<<<<<<<<<<<<<<  Free for Reconfigure >>>>>>>>>>>>>>>>>>>>>> */
970 
971 
free_recipient_list(struct alias * list)972 static void free_recipient_list(struct alias * list)
973 {
974   struct alias * new;
975   sh_filter_type * p = NULL;
976 
977   while (list)
978     {
979       new  = list;
980       list = new->next;
981       if (new->mx_list)
982 	free_mx(new->mx_list);
983       if (new->mail_filter)
984 	{
985 	  sh_filter_free(new->mail_filter);
986 	  if (!p || p != new->mail_filter)
987 	    {
988 	      p = new->mail_filter;
989 	      SH_FREE(new->mail_filter);
990 	    }
991 	}
992       sh_string_destroy(&(new->recipient));
993       SH_FREE(new);
994     }
995 }
996 
997 /* Free everything to prepare for reconfigure
998  */
sh_nmail_free()999 void sh_nmail_free()
1000 {
1001   SH_MUTEX_LOCK_UNSAFE(mutex_listall);
1002   all_recipients = NULL;
1003   SH_MUTEX_UNLOCK_UNSAFE(mutex_listall);
1004 
1005   free_recipient_list(recipient_list);
1006   recipient_list = NULL;
1007 
1008   sh_filter_free(&compiled_mail_filter);
1009 
1010   while (alias_list)
1011     {
1012       struct alias * item = alias_list;
1013 
1014       alias_list = item->next;
1015 
1016       sh_string_destroy(&(item->recipient));
1017       free_recipient_list(item->recipient_list);
1018       if (item->mail_filter)
1019 	{
1020 	  sh_filter_free(item->mail_filter);
1021 	  /* SH_FREE(item->mail_filter); */
1022 	}
1023       SH_FREE(item);
1024     }
1025   alias_list = NULL;
1026 
1027   last = compiled_recipient_list;
1028   return;
1029 }
1030 
1031 /* defined(SH_WITH_MAIL) */
1032 #endif
1033