Index: /trunk/Makefile.in
===================================================================
--- /trunk/Makefile.in	(revision 264)
+++ /trunk/Makefile.in	(revision 265)
@@ -123,5 +123,6 @@
 	sh_mounts.h sh_userfiles.h sh_static.h sh_prelink.h \
 	sh_processcheck.h sh_portcheck.h sh_pthread.h sh_string.h \
-	sh_log_check.h sh_log_evalrule.h sh_inotify.h
+	sh_log_check.h sh_log_evalrule.h sh_log_correlate.h \
+	sh_log_mark.h sh_log_repeat.h sh_inotify.h
 
 
@@ -161,6 +162,7 @@
 	$(srcsrc)/sh_log_parse_samba.c \
 	$(srcsrc)/sh_log_parse_apache.c $(srcsrc)/sh_log_evalrule.c \
+	$(srcsrc)/sh_log_correlate.c $(srcsrc)/sh_log_mark.c \
 	$(srcsrc)/sh_log_check.c $(srcsrc)/dnmalloc.c \
-	$(srcsrc)/sh_inotify.c \
+	$(srcsrc)/sh_inotify.c $(srcsrc)/sh_log_repeat.c \
 	$(srcsrc)/t-test1.c
 
@@ -179,4 +181,5 @@
 	sh_log_parse_syslog.o sh_log_parse_pacct.o sh_log_parse_apache.o \
 	sh_log_parse_samba.o sh_log_evalrule.o sh_log_check.o \
+	sh_log_correlate.o sh_log_mark.o sh_log_repeat.o \
 	sh_pthread.o sh_string.o sh_inotify.o dnmalloc.o
 
@@ -1665,5 +1668,5 @@
 sh_files.o: $(srcsrc)/sh_files.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_error.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_files.h $(srcinc)/sh_tiger.h $(srcinc)/sh_hash.h $(srcinc)/sh_ignore.h $(srcinc)/zAVLTree.h 
 sh_getopt.o: $(srcsrc)/sh_getopt.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_error.h $(srcinc)/sh_getopt.h $(srcinc)/sh_files.h $(srcinc)/sh_utils.h $(srcinc)/sh_mail.h $(srcinc)/sh_forward.h $(srcinc)/sh_hash.h $(srcinc)/sh_extern.h 
-sh_readconf.o: $(srcsrc)/sh_readconf.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_error.h $(srcinc)/sh_database.h $(srcinc)/sh_unix.h $(srcinc)/sh_utils.h $(srcinc)/sh_files.h $(srcinc)/sh_mail.h $(srcinc)/sh_nmail.h $(srcinc)/sh_calls.h $(srcinc)/sh_tiger.h $(srcinc)/sh_forward.h $(srcinc)/sh_modules.h $(srcinc)/sh_gpg.h $(srcinc)/sh_hash.h $(srcinc)/sh_ignore.h $(srcinc)/sh_prelink.h $(srcinc)/sh_extern.h $(srcinc)/sh_tools.h $(srcinc)/sh_database.h $(srcinc)/sh_prelude.h 
+sh_readconf.o: $(srcsrc)/sh_readconf.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_calls.h $(srcinc)/sh_error.h $(srcinc)/sh_extern.h $(srcinc)/sh_files.h $(srcinc)/sh_forward.h $(srcinc)/sh_gpg.h $(srcinc)/sh_hash.h $(srcinc)/sh_ignore.h $(srcinc)/sh_database.h $(srcinc)/sh_mail.h $(srcinc)/sh_modules.h $(srcinc)/sh_nmail.h $(srcinc)/sh_prelink.h $(srcinc)/sh_prelude.h $(srcinc)/sh_tiger.h $(srcinc)/sh_tools.h $(srcinc)/sh_unix.h $(srcinc)/sh_utils.h 
 sh_tiger0.o: $(srcsrc)/sh_tiger0.c Makefile config_xor.h $(srcinc)/sh_tiger.h $(srcinc)/sh_unix.h $(srcinc)/sh_error.h $(srcinc)/sh_utils.h $(srcinc)/sh_pthread.h $(srcinc)/sh_string.h 
 sh_tiger1.o: $(srcsrc)/sh_tiger1.c Makefile config_xor.h 
@@ -1728,8 +1731,11 @@
 sh_log_parse_pacct.o: $(srcsrc)/sh_log_parse_pacct.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
 sh_log_parse_apache.o: $(srcsrc)/sh_log_parse_apache.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
-sh_log_evalrule.o: $(srcsrc)/sh_log_evalrule.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/zAVLTree.h 
-sh_log_check.o: $(srcsrc)/sh_log_check.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/sh_modules.h 
+sh_log_evalrule.o: $(srcsrc)/sh_log_evalrule.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/sh_log_correlate.h $(srcinc)/sh_log_mark.h $(srcinc)/sh_log_repeat.h $(srcinc)/zAVLTree.h 
+sh_log_check.o: $(srcsrc)/sh_log_check.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/sh_log_correlate.h $(srcinc)/sh_log_mark.h $(srcinc)/sh_log_repeat.h $(srcinc)/sh_modules.h 
 sh_log_parse_samba.o: $(srcsrc)/sh_log_parse_samba.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_string.h 
 sh_nmail.o: $(srcsrc)/sh_nmail.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_mem.h $(srcinc)/sh_mail.h $(srcinc)/sh_tiger.h $(srcinc)/sh_string.h $(srcinc)/sh_utils.h $(srcinc)/sh_fifo.h $(srcinc)/sh_filter.h $(srcinc)/sh_mail_int.h $(srcinc)/zAVLTree.h 
 sh_filter.o: $(srcsrc)/sh_filter.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_mem.h $(srcinc)/sh_filter.h 
 sh_inotify.o: $(srcsrc)/sh_inotify.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_calls.h $(srcinc)/sh_inotify.h $(srcinc)/sh_mem.h $(srcinc)/slib.h $(srcinc)/sh_calls.h 
+sh_log_correlate.o: $(srcsrc)/sh_log_correlate.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h 
+sh_log_mark.o: $(srcsrc)/sh_log_mark.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_mem.h $(srcinc)/sh_string.h $(srcinc)/sh_error_min.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/zAVLTree.h 
+sh_log_repeat.o: $(srcsrc)/sh_log_repeat.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h 
Index: /trunk/acconfig.h
===================================================================
--- /trunk/acconfig.h	(revision 264)
+++ /trunk/acconfig.h	(revision 265)
@@ -342,4 +342,7 @@
 /* Define if UINT64 is 32 bits.                 */
 #undef UINT64_IS_32
+
+/* Define if you have uint64_t.               */
+#undef HAVE_UINT16_T
 
 /* Define if you have uint64_t.               */
Index: /trunk/configure.ac
===================================================================
--- /trunk/configure.ac	(revision 264)
+++ /trunk/configure.ac	(revision 265)
@@ -495,4 +495,5 @@
 AC_C_LONG_DOUBLE
 SH_CHECK_TYPEDEF(long long, HAVE_LONG_LONG)
+SH_CHECK_TYPEDEF(uint16_t, HAVE_UINT16_T)
 SH_CHECK_TYPEDEF(uint64_t, HAVE_UINT64_T)
 if test "$sh_HAVE_LONG_LONG" = "yes"; then
Index: /trunk/depend.dep
===================================================================
--- /trunk/depend.dep	(revision 264)
+++ /trunk/depend.dep	(revision 265)
@@ -7,5 +7,5 @@
 sh_files.o: $(srcsrc)/sh_files.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_error.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_files.h $(srcinc)/sh_tiger.h $(srcinc)/sh_hash.h $(srcinc)/sh_ignore.h $(srcinc)/zAVLTree.h 
 sh_getopt.o: $(srcsrc)/sh_getopt.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_error.h $(srcinc)/sh_getopt.h $(srcinc)/sh_files.h $(srcinc)/sh_utils.h $(srcinc)/sh_mail.h $(srcinc)/sh_forward.h $(srcinc)/sh_hash.h $(srcinc)/sh_extern.h 
-sh_readconf.o: $(srcsrc)/sh_readconf.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_error.h $(srcinc)/sh_database.h $(srcinc)/sh_unix.h $(srcinc)/sh_utils.h $(srcinc)/sh_files.h $(srcinc)/sh_mail.h $(srcinc)/sh_nmail.h $(srcinc)/sh_calls.h $(srcinc)/sh_tiger.h $(srcinc)/sh_forward.h $(srcinc)/sh_modules.h $(srcinc)/sh_gpg.h $(srcinc)/sh_hash.h $(srcinc)/sh_ignore.h $(srcinc)/sh_prelink.h $(srcinc)/sh_extern.h $(srcinc)/sh_tools.h $(srcinc)/sh_database.h $(srcinc)/sh_prelude.h 
+sh_readconf.o: $(srcsrc)/sh_readconf.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_calls.h $(srcinc)/sh_error.h $(srcinc)/sh_extern.h $(srcinc)/sh_files.h $(srcinc)/sh_forward.h $(srcinc)/sh_gpg.h $(srcinc)/sh_hash.h $(srcinc)/sh_ignore.h $(srcinc)/sh_database.h $(srcinc)/sh_mail.h $(srcinc)/sh_modules.h $(srcinc)/sh_nmail.h $(srcinc)/sh_prelink.h $(srcinc)/sh_prelude.h $(srcinc)/sh_tiger.h $(srcinc)/sh_tools.h $(srcinc)/sh_unix.h $(srcinc)/sh_utils.h 
 sh_tiger0.o: $(srcsrc)/sh_tiger0.c Makefile config_xor.h $(srcinc)/sh_tiger.h $(srcinc)/sh_unix.h $(srcinc)/sh_error.h $(srcinc)/sh_utils.h $(srcinc)/sh_pthread.h $(srcinc)/sh_string.h 
 sh_tiger1.o: $(srcsrc)/sh_tiger1.c Makefile config_xor.h 
@@ -72,8 +72,11 @@
 sh_log_parse_pacct.o: $(srcsrc)/sh_log_parse_pacct.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
 sh_log_parse_apache.o: $(srcsrc)/sh_log_parse_apache.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
-sh_log_evalrule.o: $(srcsrc)/sh_log_evalrule.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/zAVLTree.h 
-sh_log_check.o: $(srcsrc)/sh_log_check.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/sh_modules.h 
+sh_log_evalrule.o: $(srcsrc)/sh_log_evalrule.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/sh_log_correlate.h $(srcinc)/sh_log_mark.h $(srcinc)/sh_log_repeat.h $(srcinc)/zAVLTree.h 
+sh_log_check.o: $(srcsrc)/sh_log_check.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/sh_log_correlate.h $(srcinc)/sh_log_mark.h $(srcinc)/sh_log_repeat.h $(srcinc)/sh_modules.h 
 sh_log_parse_samba.o: $(srcsrc)/sh_log_parse_samba.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_string.h 
 sh_nmail.o: $(srcsrc)/sh_nmail.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_mem.h $(srcinc)/sh_mail.h $(srcinc)/sh_tiger.h $(srcinc)/sh_string.h $(srcinc)/sh_utils.h $(srcinc)/sh_fifo.h $(srcinc)/sh_filter.h $(srcinc)/sh_mail_int.h $(srcinc)/zAVLTree.h 
 sh_filter.o: $(srcsrc)/sh_filter.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_mem.h $(srcinc)/sh_filter.h 
 sh_inotify.o: $(srcsrc)/sh_inotify.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_calls.h $(srcinc)/sh_inotify.h $(srcinc)/sh_mem.h $(srcinc)/slib.h $(srcinc)/sh_calls.h 
+sh_log_correlate.o: $(srcsrc)/sh_log_correlate.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h 
+sh_log_mark.o: $(srcsrc)/sh_log_mark.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_mem.h $(srcinc)/sh_string.h $(srcinc)/sh_error_min.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/zAVLTree.h 
+sh_log_repeat.o: $(srcsrc)/sh_log_repeat.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h 
Index: /trunk/depend.sum
===================================================================
--- /trunk/depend.sum	(revision 264)
+++ /trunk/depend.sum	(revision 265)
@@ -1,1 +1,1 @@
-188553029
+968635974
Index: /trunk/docs/Changelog
===================================================================
--- /trunk/docs/Changelog	(revision 264)
+++ /trunk/docs/Changelog	(revision 265)
@@ -1,3 +1,10 @@
-2.6.1:
+2.6.1 (21-12-2009):
+	* add a routine to log monitoring module to guess the proper year 
+	  for timestamps without year (standard syslog)
+	* add feature to automatically detect and report bursts of
+	  similar messages in log monitoring module
+	* add feature to check for missing heartbeat messages in
+	  log monitoring module
+	* cache UIDs/GIDs to reduce the number of lookups
 	* use inotify to track login/logout (sh_inotify.c, sh_utmp.c)
 	* support event correlation in log monitoring module
@@ -7,5 +14,5 @@
 	  sh_unix_count_mlock() (reported by Remco Landegge).
 	
-2.6.0:
+2.6.0 (01-11-2009):
 	* don't use statvfs() for process checking on FreeBSD
 	* fix bug with parallel compilation of cutest in Makefile 
Index: /trunk/include/samhain.h
===================================================================
--- /trunk/include/samhain.h	(revision 264)
+++ /trunk/include/samhain.h	(revision 265)
@@ -106,4 +106,10 @@
 #ifdef HAVE_STDINT_H
 #include <stdint.h>
+#endif
+
+#if !defined(HAVE_UINT16_T)
+#define UINT16 unsigned short
+#else
+#define UINT16 uint16_t
 #endif
 
Index: /trunk/include/sh_cat.h
===================================================================
--- /trunk/include/sh_cat.h	(revision 264)
+++ /trunk/include/sh_cat.h	(revision 265)
@@ -168,4 +168,6 @@
  MSG_LOGMON_SUM,
  MSG_LOGMON_COR,
+ MSG_LOGMON_MARK,
+ MSG_LOGMON_BURST,
 #endif
 
Index: /trunk/include/sh_error.h
===================================================================
--- /trunk/include/sh_error.h	(revision 264)
+++ /trunk/include/sh_error.h	(revision 265)
@@ -109,8 +109,4 @@
 void sh_error_fixup(void);
 
-/* convert a string to a numeric priority
- */ 
-int sh_error_convert_level (const char * str_s);
-
 /* only to stderr (GOOD/BAD)
  */
Index: /trunk/include/sh_error_min.h
===================================================================
--- /trunk/include/sh_error_min.h	(revision 264)
+++ /trunk/include/sh_error_min.h	(revision 265)
@@ -25,3 +25,7 @@
 		      long errnum, unsigned long  msg_index, ...);
 
+/* convert a string to a numeric priority
+ */ 
+int sh_error_convert_level (const char * str_s);
+
 #endif
Index: /trunk/include/sh_log_correlate.h
===================================================================
--- /trunk/include/sh_log_correlate.h	(revision 265)
+++ /trunk/include/sh_log_correlate.h	(revision 265)
@@ -0,0 +1,24 @@
+#ifndef SH_LOG_CORRELATE_H
+#define SH_LOG_CORRELATE_H
+
+/* Clean up everything.
+ */
+void sh_keep_destroy();
+
+/* Add an event 
+ */
+int sh_keep_add(sh_string * label, unsigned long delay, time_t last);
+
+/* Add an event sequence matching rule 
+ */
+int sh_keep_match_add(const char * str, const char * queue, const char * pattern);
+
+/* Delete the list of event sequence matching rules
+ */
+void sh_keep_match_del();
+
+/* Try to find correlated events
+ */
+void sh_keep_match();
+
+#endif
Index: /trunk/include/sh_log_evalrule.h
===================================================================
--- /trunk/include/sh_log_evalrule.h	(revision 264)
+++ /trunk/include/sh_log_evalrule.h	(revision 265)
@@ -39,7 +39,21 @@
 int sh_eval_process_msg(struct sh_logrecord * record);
 
-/* Match correlated rules
- */
-void sh_keep_match();
+enum policies {
+  EVAL_REPORT,
+  EVAL_SUM
+};
+
+struct sh_qeval  /* Queue with definitions */
+{
+  sh_string       * label;
+  enum policies     policy;
+  int               severity;
+  time_t            interval;        /* if EVAL_SUM, interval   */ 
+  struct sh_qeval * next;
+};
+
+struct sh_qeval * sh_log_find_queue(const char * str);
+
+int sh_log_lookup_severity(const char * str);
 
 #endif
Index: /trunk/include/sh_log_mark.h
===================================================================
--- /trunk/include/sh_log_mark.h	(revision 265)
+++ /trunk/include/sh_log_mark.h	(revision 265)
@@ -0,0 +1,14 @@
+#ifndef SH_LOG_MARK_H
+#define SH_LOG_MARK_H
+
+void sh_log_mark_destroy();
+
+int sh_log_mark_add (const char * label, time_t interval, const char * qlabel);
+
+void sh_log_mark_update (sh_string * label, time_t timestamp);
+
+void sh_log_mark_check();
+
+int sh_log_set_mark_severity (const char * str);
+
+#endif
Index: /trunk/include/sh_log_repeat.h
===================================================================
--- /trunk/include/sh_log_repeat.h	(revision 265)
+++ /trunk/include/sh_log_repeat.h	(revision 265)
@@ -0,0 +1,14 @@
+#ifndef SH_LOG_REPEAT_H
+#define SH_LOG_REPEAT_H
+
+int sh_repeat_set_trigger (const char * str);
+
+int sh_repeat_set_queue (const char * str);
+
+int sh_repeat_set_cron (const char * str);
+
+int sh_repeat_message_check (const sh_string * host, 
+			     const sh_string * msg, 
+			     time_t ltime);
+
+#endif
Index: /trunk/include/sh_string.h
===================================================================
--- /trunk/include/sh_string.h	(revision 264)
+++ /trunk/include/sh_string.h	(revision 265)
@@ -2,4 +2,5 @@
 #define SH_STRING_H
 
+#include <stdio.h>
 
 /* String definition and utility functions.
@@ -84,4 +85,10 @@
 char ** split_array_list(char *line, unsigned int * nfields, size_t * lengths);
 
+/* Same as above, but split on delimiter list (token)
+ */ 
+char ** split_array_token (char *line, 
+			   unsigned int * nfields, size_t * lengths,
+			   const char * token);
+
 /* Return a split_array_list() of a list contained in 'PREFIX\s*( list ).*'
  */
Index: /trunk/include/sh_unix.h
===================================================================
--- /trunk/include/sh_unix.h	(revision 264)
+++ /trunk/include/sh_unix.h	(revision 265)
@@ -219,4 +219,7 @@
 void sh_unix_closeall (int fd, int except, int inchild);
 
+/* Check whether directory for pid file exists
+ */
+int sh_unix_check_piddir (char * pidpath);
 
 /* write lock for filename
Index: /trunk/src/samhain.c
===================================================================
--- /trunk/src/samhain.c	(revision 264)
+++ /trunk/src/samhain.c	(revision 265)
@@ -753,4 +753,5 @@
 #endif
   delete_cache();
+  sh_userid_destroy ();
   sh_mem_stat();
 #endif
Index: /trunk/src/sh_cat.c
===================================================================
--- /trunk/src/sh_cat.c	(revision 264)
+++ /trunk/src/sh_cat.c	(revision 265)
@@ -159,5 +159,7 @@
   { MSG_LOGMON_REP,  SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [Logfile] %s\" time=\"%s\" host=\"%s\" path=\"%s\"") },
   { MSG_LOGMON_SUM,  SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [Logfile] %s\" host=\"%s\" path=\"%s\"") },
-  { MSG_LOGMON_COR,  SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [Logfile] Correlated events %s\"") },
+  { MSG_LOGMON_COR,  SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [Logfile] Correlation event %s occured %d time(s)\"") },
+  { MSG_LOGMON_MARK, SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [Logfile] Event %s missing for %lu seconds\"") },
+  { MSG_LOGMON_BURST, SH_ERR_SEVERE, EVENT, N_("msg=\"POLICY [Logfile] Repeated %d times: %s\" host=\"%s\"") },
 #endif
 
@@ -487,7 +489,9 @@
   { MSG_LOGMON_EOPEN,SH_ERR_ERR,     RUN,   N_("msg=<Cannot open logfile %s>") },
   { MSG_LOGMON_EREAD,SH_ERR_ERR,     RUN,   N_("msg=<Error while reading logfile %s>") },
-  { MSG_LOGMON_REP,  SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Logfile] %s> time=<%s> host=<%s> path=<%s>") },
+  { MSG_LOGMON_REP,  SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Logfile] %s> time=<%s>, host=<%s>, path=<%s>") },
   { MSG_LOGMON_SUM,  SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Logfile] %s> host=<%s> path=<%s>") },
-  { MSG_LOGMON_COR,  SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Logfile] Correlated events %s>") },
+  { MSG_LOGMON_COR,  SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Logfile] Correlation event %s occured %d time(s)>") },
+  { MSG_LOGMON_MARK, SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Logfile] Event %s missing for %lu seconds>") },
+  { MSG_LOGMON_BURST, SH_ERR_SEVERE, EVENT, N_("msg=<POLICY [Logfile] Repeated %d times: %s>, host=<%s> ") },
 #endif
 
Index: /trunk/src/sh_inotify.c
===================================================================
--- /trunk/src/sh_inotify.c	(revision 264)
+++ /trunk/src/sh_inotify.c	(revision 265)
@@ -304,6 +304,4 @@
       } while (len < 0 || errno == EINTR);
 
-      fprintf (stderr, "FIXME buflen=%ld\n", (long int) len);
-
       if (len > 0)
 	{
@@ -317,22 +315,8 @@
 	    event = (struct inotify_event *) &buffer[i];
 
-	    fprintf (stderr, "FIXME wd=%d mask=%u cookie=%u len=%u\n",
-		    event->wd, event->mask,
-		    event->cookie, event->len);
-
-	    if (event->len > 0)
-	      fprintf (stderr, "FIXME name=%s\n", event->name);
-
 	    for (j = 0; j < watches->count; ++j)
 	      {
-		fprintf (stderr, "FIXME %d watch=%d file=%s\n", 
-			 j, watches->watch[j], watches->file[j]);
-
 		if (watches->watch[j] == event->wd)
 		  {
-		    
-		    fprintf (stderr, "FIXME wd=%d mask=%u\n",
-			     event->wd, event->mask);
-
 		    if (event->mask & IN_MODIFY)
 		      {
Index: /trunk/src/sh_log_check.c
===================================================================
--- /trunk/src/sh_log_check.c	(revision 264)
+++ /trunk/src/sh_log_check.c	(revision 265)
@@ -23,7 +23,11 @@
 #include "sh_pthread.h"
 #include "sh_utils.h"
+#include "sh_unix.h"
 #include "sh_string.h"
 #include "sh_log_check.h"
 #include "sh_log_evalrule.h"
+#include "sh_log_correlate.h"
+#include "sh_log_mark.h"
+#include "sh_log_repeat.h"
 
 /* List of supported logfile types, format is
@@ -52,8 +56,7 @@
   ino_t  inode;
   fpos_t offset;
-  /* FIXME include filename hash */
 };
 
-const char * save_dir = NULL;
+static char * save_dir = NULL;
 
 static void * sh_dummy_path = NULL;
@@ -105,4 +108,10 @@
   if (path)
     {
+      if (0 != sh_unix_check_piddir (path))
+	{
+	  SH_FREE(path);
+	  return;
+	}
+
       fd = fopen(path, "wb");
       if (fd)
@@ -771,4 +780,29 @@
  **********************************************************/
 
+/* Return current year, unless that would result
+ * in a date far in the future. If that happens,
+ * return last year.
+ */
+static int year_guess (struct tm * btime)
+{
+  int           year;
+  struct tm     ts;
+  time_t        now    = time(NULL);
+  time_t        check;
+
+  memcpy(&ts, localtime(&now), sizeof(struct tm));
+  year = ts.tm_year;
+
+  /* Check result to detect year wrap
+   * (logfile entry from last year).
+   */
+  btime->tm_year = year;
+  check = mktime(btime);
+  if (check > (now + (86400*30)))
+    --year;
+
+  return year;
+}
+
 time_t conv_timestamp (struct tm * btime, 
 		       struct tm * old_tm, time_t * old_time)
@@ -776,4 +810,5 @@
   time_t timestamp;
   long   offtime;
+
 
   /* timestamp - mktime is slooow, thus cache result
@@ -788,4 +823,5 @@
 	(btime->tm_min  - old_tm->tm_min)  * 60   +
 	(btime->tm_sec  - old_tm->tm_sec);
+
       *old_time += offtime;
       memcpy(old_tm, btime, sizeof(struct tm));
@@ -794,5 +830,11 @@
   else
     {
+      int year_btime = btime->tm_year;
+
+      if (btime->tm_year == 0)
+	btime->tm_year = year_guess(btime);
       timestamp = mktime(btime);
+      btime->tm_year = year_btime;
+
       *old_time  = timestamp;
       memcpy(old_tm, btime, sizeof(struct tm));
@@ -866,4 +908,5 @@
       sh_check_watches();
       sh_keep_match();
+      sh_log_mark_check();
     }
   SH_MUTEX_UNLOCK(mutex_logmon_check);
@@ -892,4 +935,5 @@
 int sh_log_check_cleanup(void) 
 {
+  sh_log_mark_destroy();
   return sh_log_check_reconf();
 }
@@ -907,4 +951,5 @@
 static int sh_logmon_add_rule  (const char * str);
 extern int sh_set_hidepid(const char *s);
+static int sh_logmon_set_save_dir(const char *s);
 
 sh_rconf sh_log_check_table[] = {
@@ -948,4 +993,24 @@
         N_("logmonhidepid"),
         sh_set_hidepid,
+    },
+    {
+        N_("logmonsavedir"),
+        sh_logmon_set_save_dir,
+    },
+    {
+        N_("logmonmarkseverity"),
+        sh_logmon_set_save_dir,
+    },
+    {
+        N_("logmonburstthreshold"),
+        sh_repeat_set_trigger,
+    },
+    {
+        N_("logmonburstqueue"),
+        sh_repeat_set_queue,
+    },
+    {
+        N_("logmonburstcron"),
+        sh_repeat_set_cron,
     },
     {
@@ -966,4 +1031,24 @@
 
   SL_RETURN((value), _("sh_logmon_set_active"));
+}
+
+static int sh_logmon_set_save_dir(const char *str) 
+{
+  int retval = -1;
+    
+  SL_ENTER(_("sh_logmon_set_save_dir"));
+
+  if (str && str[0] == '/')
+    {
+      if (save_dir)
+	{
+	  SH_FREE(save_dir);
+	  save_dir = NULL;
+	}
+      save_dir = sh_util_strdup(str);
+      retval = 0;
+    }
+
+  SL_RETURN((retval), _("sh_logmon_set_save_dir"));
 }
 
Index: /trunk/src/sh_log_correlate.c
===================================================================
--- /trunk/src/sh_log_correlate.c	(revision 265)
+++ /trunk/src/sh_log_correlate.c	(revision 265)
@@ -0,0 +1,298 @@
+#include "config_xor.h"
+
+#ifdef USE_LOGFILE_MONITOR
+
+#undef  FIL__
+#define FIL__  _("sh_log_correlate.c")
+
+#include <string.h>
+#include <time.h>
+
+/* Debian/Ubuntu: libpcre3-dev */
+#ifdef HAVE_PCRE_PCRE_H
+#include <pcre/pcre.h>
+#else
+#include <pcre.h>
+#endif
+
+#ifndef PCRE_NO_AUTO_CAPTURE
+#define PCRE_NO_AUTO_CAPTURE 0
+#endif
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_utils.h"
+#include "sh_string.h"
+#include "sh_log_check.h"
+#include "sh_log_evalrule.h"
+
+extern int flag_err_debug;
+
+/*--------------------------------------------------------------
+ *
+ *   Event correlation
+ *
+ *--------------------------------------------------------------*/
+
+/* For each even to be correlated, we keep a label in a list. We
+ * then build a string from the (time-sorted) list of labels, and
+ * match this string against a regular expression.
+ */
+
+/* -- The list of labels kept in memory ----------------------- */
+
+struct sh_keep
+{
+  sh_string       * label;           /* label of keep rule      */
+  unsigned long     delay;           /* valid delay             */
+  time_t            last;            /* seen at                 */
+  struct sh_keep *  next; 
+};
+
+static struct sh_keep * keeplist  = NULL;
+static struct sh_keep * keeplast  = NULL;
+static unsigned long    keepcount = 0;
+
+static void sh_keep_free(void * item)
+{
+  struct sh_keep * keep = (struct sh_keep *) item;
+  if (!keep)
+    return;
+  sh_string_destroy(&(keep->label));
+  SH_FREE(keep);
+}
+
+void sh_keep_destroy()
+{
+  struct sh_keep * keep;
+
+  while (keeplist)
+    {
+      keep = keeplist;
+      keeplist = keep->next;
+      sh_keep_free(keep);
+      --keepcount;
+    }
+  keeplist  = NULL;
+  keeplast  = NULL;
+  keepcount = 0;
+}
+
+int sh_keep_add(sh_string * label, unsigned long delay, time_t last)
+{
+  struct sh_keep * keep = SH_ALLOC(sizeof(struct sh_keep));
+
+  keep->label = sh_string_copy(label);
+  keep->delay = delay;
+  keep->last  = last;
+  keep->next  = NULL;
+
+  if (keeplast && keeplist)
+    {
+      keeplast->next = keep;
+      keeplast       = keep;
+    }
+  else
+    {
+      keeplist = keep;
+      keeplast = keeplist;
+    }
+  ++keepcount;
+  return 0;
+}
+
+int sh_keep_comp(const void * a, const void * b)
+{
+  return ( (int)(((struct sh_keep *)a)->last) - 
+	   (int)(((struct sh_keep *)b)->last) );
+}
+
+/* -- Sort the kept labels and build a string ----------------- */
+
+static sh_string * sh_keep_eval()
+{
+  unsigned long count   = 0;
+  sh_string * res       = NULL;
+  time_t now            = time(NULL);
+  struct sh_keep * keep = keeplist;
+  struct sh_keep * prev = keeplist;
+  struct sh_keep * arr;
+
+  if (keepcount > 0)
+    {
+      arr = SH_ALLOC (keepcount * sizeof(struct sh_keep));
+
+      while (count < keepcount && keep)
+	{
+	  if ((now > keep->last) && 
+	      ((unsigned long)(now - keep->last) <= keep->delay))
+	    {
+	      memcpy(&(arr[count]), keep, sizeof(struct sh_keep));
+	      ++count;
+	      prev = keep;
+	      keep = keep->next;
+	    }
+	  else /* Too old or in future, delete it */
+	    {
+	      if (keep != keeplist)
+		{
+		  prev->next = keep->next;
+		  sh_keep_free(keep);
+		  keep = prev->next;
+		  --keepcount;
+		}
+	      else /* list head */
+		{
+		  keeplist = keep->next;
+		  prev     = keeplist;
+		  sh_keep_free(keep);
+		  keep     = keeplist;
+		  --keepcount;
+		}
+	    }
+	}
+
+      if (count > 0)
+	{
+	  unsigned long i;
+	  qsort(arr, count, sizeof(struct sh_keep), sh_keep_comp);
+	  res = sh_string_copy(arr[0].label);
+	  for (i = 1; i < count; ++i)
+	    res = sh_string_add(res, arr[i].label);
+	}
+      SH_FREE(arr);
+    }
+  return res;
+}
+
+/* -- Match the string against correlation rules -------------- */
+
+struct sh_mkeep
+{
+  sh_string       * label;           /* label of match rule     */
+  pcre            * rule;            /* compiled regex for rule */
+  struct sh_qeval * queue;           /* assigned queue          */
+  struct sh_mkeep * next; 
+};
+
+struct sh_mkeep * mkeep_list = NULL;
+
+
+int sh_keep_match_add(const char * str, const char * queue, 
+		      const char * pattern)
+{
+  unsigned int nfields = 1; /* seconds:label */
+  size_t       lengths[1];
+  char *       new    = sh_util_strdup(str);
+  char **      splits = split_array_braced(new, _("CORRELATE"), 
+					   &nfields, lengths);
+
+  if (nfields == 1 && lengths[0] > 0)
+    {
+      struct sh_mkeep * mkeep = SH_ALLOC(sizeof(struct sh_mkeep));
+      const char * error;
+      int          erroffset;
+      struct sh_qeval * rqueue = NULL;
+
+      mkeep->rule = pcre_compile(pattern, PCRE_NO_AUTO_CAPTURE, 
+			     &error, &erroffset, NULL);
+      if (!(mkeep->rule))
+	{
+	  sh_string * msg =  sh_string_new(0);
+	  sh_string_add_from_char(msg, _("Bad regex: "));
+	  sh_string_add_from_char(msg, pattern);
+	  
+	  SH_MUTEX_LOCK(mutex_thread_nolog);
+	  sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+			  sh_string_str(msg),
+			  _("sh_keep_match_add"));
+	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+	  sh_string_destroy(&msg);
+	  
+	  SH_FREE(splits);
+	  SH_FREE(mkeep);
+	  SH_FREE(new);
+	  return -1;
+	}
+
+      if (0 != strcmp(queue, _("trash")))
+	{
+
+	  rqueue = sh_log_find_queue(queue);
+	  if (!rqueue)
+	    {
+	      pcre_free(mkeep->rule);
+	      SH_FREE(splits);
+	      SH_FREE(mkeep);
+	      SH_FREE(new);
+	      return -1;
+	    }
+	}
+
+      mkeep->queue = rqueue;
+      mkeep->label = sh_string_new_from_lchar(splits[0], strlen(splits[0]));
+      mkeep->next  = mkeep_list;
+      mkeep_list   = mkeep;
+    }
+  SH_FREE(new);
+  return 0;
+}
+
+void sh_keep_match_del()
+{
+  struct sh_mkeep * mkeep = mkeep_list;
+  while (mkeep)
+    {
+      mkeep_list = mkeep->next;
+      sh_string_destroy(&(mkeep->label));
+      pcre_free(mkeep->rule);
+      mkeep = mkeep_list;
+    }
+  mkeep_list = NULL;
+}
+
+static struct sh_mkeep ** dummy_mkeep;
+
+void sh_keep_match()
+{
+  if (mkeep_list)
+    {
+      sh_string       * res = sh_keep_eval();
+
+      if (res)
+	{
+	  struct sh_mkeep * mkeep = mkeep_list;
+
+	  dummy_mkeep = &mkeep;
+
+	  while (mkeep)
+	    {
+	      /* Use pcre_dfa_exec() to obtain number of matches. Needs ovector
+	       * array, otherwise number of matches is not returned.
+	       */
+	      int ovector[SH_MINIBUF];
+	      int wspace[SH_MINIBUF];
+	      int val = pcre_dfa_exec(mkeep->rule, NULL, 
+				      sh_string_str(res), 
+				      (int)sh_string_len(res), 
+				      0, /* start at offset 0 in the subject */
+				      0, 
+				      ovector, SH_MINIBUF,
+				      wspace, SH_MINIBUF);
+	      if (val >= 0)
+		{
+		  SH_MUTEX_LOCK(mutex_thread_nolog);
+		  sh_error_handle (mkeep->queue->severity, FIL__, __LINE__, 0, 
+				   MSG_LOGMON_COR, sh_string_str(mkeep->label),
+				   val);
+		  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+		}
+	      mkeep = mkeep->next;
+	    }
+	  sh_string_destroy(&res);
+	}
+    }
+  return;
+}
+
+#endif
Index: /trunk/src/sh_log_evalrule.c
===================================================================
--- /trunk/src/sh_log_evalrule.c	(revision 264)
+++ /trunk/src/sh_log_evalrule.c	(revision 265)
@@ -5,4 +5,5 @@
 #include <stdarg.h>
 #include <string.h>
+#include <ctype.h>
 #include <time.h>
 #include <limits.h>
@@ -31,4 +32,7 @@
 #include "sh_log_check.h"
 #include "sh_log_evalrule.h"
+#include "sh_log_correlate.h"
+#include "sh_log_mark.h"
+#include "sh_log_repeat.h"
 #include "zAVLTree.h"
 
@@ -38,5 +42,5 @@
 
 #ifdef DEBUG_EVALRULES
-void DEBUG(const char *fmt, ...)
+static void DEBUG(const char *fmt, ...)
 {
   va_list ap;
@@ -47,5 +51,5 @@
 }
 #else
-void DEBUG(const char *fmt, ...)
+static void DEBUG(const char *fmt, ...)
 {
   (void) fmt;
@@ -53,9 +57,4 @@
 }
 #endif
-
-enum policies {
-  EVAL_REPORT,
-  EVAL_SUM
-};
 
 struct sh_ceval    /* Counter for summarizing    */
@@ -80,263 +79,17 @@
 }
 
-struct sh_qeval  /* Queue with definitions */
-{
-  sh_string       * label;
-  enum policies     policy;
-  int               severity;
-  time_t            interval;        /* if EVAL_SUM, interval   */ 
-  struct sh_qeval * next;
-};
-
 enum {
   RFL_ISRULE  = 1 << 0,
   RFL_ISGROUP = 1 << 1,
-  RFL_KEEP    = 1 << 2
+  RFL_KEEP    = 1 << 2,
+  RFL_MARK    = 1 << 3
 };
 
-/*--------------------------------------------------------------*/
-
-struct sh_keep
-{
-  sh_string       * label;           /* label of keep rule   */
-  unsigned long     delay;           /* valid delay             */
-  time_t            last;            /* seen at                 */
-  struct sh_keep *  next; 
-};
-
-static struct sh_keep * keeplist  = NULL;
-static struct sh_keep * keeplast  = NULL;
-static unsigned long    keepcount = 0;
-
-static void sh_keep_free(void * item)
-{
-  struct sh_keep * keep = (struct sh_keep *) item;
-  if (!keep)
-    return;
-  sh_string_destroy(&(keep->label));
-  SH_FREE(keep);
-}
-
-static void sh_keep_destroy()
-{
-  struct sh_keep * keep;
-
-  while (keeplist)
-    {
-      keep = keeplist;
-      keeplist = keep->next;
-      sh_keep_free(keep);
-      --keepcount;
-    }
-  keeplist  = NULL;
-  keeplast  = NULL;
-  keepcount = 0;
-}
-
-static int sh_keep_add(sh_string * label, unsigned long delay, time_t last)
-{
-  struct sh_keep * keep = SH_ALLOC(sizeof(struct sh_keep));
-
-  keep->label = sh_string_copy(label);
-  keep->delay = delay;
-  keep->last  = last;
-  keep->next  = NULL;
-
-  if (keeplast && keeplist)
-    {
-      keeplast->next = keep;
-      keeplast       = keep;
-    }
-  else
-    {
-      keeplist = keep;
-      keeplast = keeplist;
-    }
-  ++keepcount;
-  return 0;
-}
-
-int sh_keep_comp(const void * a, const void * b)
-{
-  return ( (int)(((struct sh_keep *)a)->last) - 
-	   (int)(((struct sh_keep *)b)->last) );
-}
-
-static sh_string * sh_keep_eval()
-{
-  unsigned long count   = 0;
-  sh_string * res       = NULL;
-  time_t now            = time(NULL);
-  struct sh_keep * keep = keeplist;
-  struct sh_keep * prev = keeplist;
-  struct sh_keep * arr;
-
-  if (keepcount > 0)
-    {
-      arr = SH_ALLOC (keepcount * sizeof(struct sh_keep));
-
-      while (count < keepcount && keep)
-	{
-	  if ((now > keep->last) && ((unsigned long)(now - keep->last) <= keep->delay))
-	    {
-	      memcpy(&(arr[count]), keep, sizeof(struct sh_keep));
-	      ++count;
-	      prev = keep;
-	      keep = keep->next;
-	    }
-	  else /* Too old or in future, delete it */
-	    {
-	      if (keep != keeplist)
-		{
-		  prev->next = keep->next;
-		  sh_keep_free(keep);
-		  keep = prev->next;
-		  --keepcount;
-		}
-	      else /* list head */
-		{
-		  keeplist = keep->next;
-		  prev     = keeplist;
-		  sh_keep_free(keep);
-		  keep     = keeplist;
-		  --keepcount;
-		}
-	    }
-	}
-
-      if (count > 0)
-	{
-	  unsigned long i;
-	  qsort(arr, count, sizeof(struct sh_keep), sh_keep_comp);
-	  res = sh_string_copy(arr[0].label);
-	  for (i = 1; i < count; ++i)
-	    res = sh_string_add(res, arr[i].label);
-	}
-      SH_FREE(arr);
-    }
-  return res;
-}
-
-struct sh_mkeep
-{
-  sh_string       * label;           /* label of match rule     */
-  pcre            * rule;            /* compiled regex for rule */
-  struct sh_qeval * queue;           /* assigned queue          */
-  struct sh_mkeep * next; 
-};
-
-struct sh_mkeep * mkeep_list = NULL;
-
-static struct sh_qeval * find_queue(const char * str);
-
-static int sh_keep_match_add(const char * str, const char * queue, const char * pattern)
-{
-  unsigned int nfields = 1; /* seconds:label */
-  size_t       lengths[1];
-  char *       new    = sh_util_strdup(str);
-  char **      splits = split_array_braced(new, _("CORRELATE"), &nfields, lengths);
-
-  if (nfields == 1 && lengths[0] > 0)
-    {
-      struct sh_mkeep * mkeep = SH_ALLOC(sizeof(struct sh_mkeep));
-      const char * error;
-      int          erroffset;
-      struct sh_qeval * rqueue = NULL;
-
-      mkeep->rule = pcre_compile(pattern, PCRE_NO_AUTO_CAPTURE, 
-			     &error, &erroffset, NULL);
-      if (!(mkeep->rule))
-	{
-	  sh_string * msg =  sh_string_new(0);
-	  sh_string_add_from_char(msg, _("Bad regex: "));
-	  sh_string_add_from_char(msg, pattern);
-	  
-	  SH_MUTEX_LOCK(mutex_thread_nolog);
-	  sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
-			  sh_string_str(msg),
-			  _("sh_keep_match_add"));
-	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
-	  sh_string_destroy(&msg);
-	  
-	  SH_FREE(splits);
-	  SH_FREE(mkeep);
-	  SH_FREE(new);
-	  return -1;
-	}
-
-      if (0 != strcmp(queue, _("trash")))
-	{
-
-	  rqueue = find_queue(queue);
-	  if (!rqueue)
-	    {
-	      pcre_free(mkeep->rule);
-	      SH_FREE(splits);
-	      SH_FREE(mkeep);
-	      SH_FREE(new);
-	      return -1;
-	    }
-	}
-
-      mkeep->queue = rqueue;
-      mkeep->label = sh_string_new_from_lchar(splits[0], strlen(splits[0]));
-      mkeep->next  = mkeep_list;
-      mkeep_list   = mkeep;
-    }
-  SH_FREE(new);
-  return 0;
-}
-
-static void sh_keep_match_del()
-{
-  struct sh_mkeep * mkeep = mkeep_list;
-  while (mkeep)
-    {
-      mkeep_list = mkeep->next;
-      sh_string_destroy(&(mkeep->label));
-      pcre_free(mkeep->rule);
-      mkeep = mkeep_list;
-    }
-  mkeep_list = NULL;
-}
-
-static struct sh_mkeep ** dummy_mkeep;
-
-void sh_keep_match()
-{
-  if (mkeep_list)
-    {
-      sh_string       * res = sh_keep_eval();
-
-      if (res)
-	{
-	  struct sh_mkeep * mkeep = mkeep_list;
-
-	  dummy_mkeep = &mkeep;
-
-	  while (mkeep)
-	    {
-	      int val = pcre_exec(mkeep->rule, NULL, 
-				  sh_string_str(res), (int)sh_string_len(res), 
-				  0, 0, NULL, 0);
-	      if (val >= 0)
-		{
-		  char * tmp;
-		  SH_MUTEX_LOCK(mutex_thread_nolog);
-		  tmp = sh_util_safe_name (sh_string_str(mkeep->label));
-		  sh_error_handle (mkeep->queue->severity, FIL__, __LINE__, 0, 
-				   MSG_LOGMON_COR, tmp);
-		  SH_FREE(tmp);
-		  SH_MUTEX_UNLOCK(mutex_thread_nolog);
-		}
-	      mkeep = mkeep->next;
-	    }
-	  sh_string_destroy(&res);
-	}
-    }
-  return;
-}
-
-/*--------------------------------------------------------------*/
+
+/*--------------------------------------------------------------
+ *
+ *   Adding rules/groups/hosts
+ *
+ *--------------------------------------------------------------*/
 
 struct sh_geval  /* Group of rules (may be a single rule) */
@@ -623,5 +376,5 @@
 }
 
-static struct sh_qeval * find_queue(const char * str)
+struct sh_qeval * sh_log_find_queue(const char * str)
 {
   struct sh_qeval * retval = queuelist;
@@ -639,5 +392,23 @@
 }
 
-char * get_keep(char * str, unsigned long * seconds)
+int sh_log_lookup_severity(const char * str)
+{
+  struct sh_qeval * queue;
+
+  if (str)
+    {
+      if (0 != strcmp(str, _("trash")))
+	{
+	  queue = sh_log_find_queue(str);
+	  
+	  if (queue)
+	    return queue->severity;
+	}
+    }
+  return SH_ERR_SEVERE;
+}
+
+static char * get_label_and_time(const char * inprefix, char * str, 
+				 unsigned long * seconds)
 {
   char       * res    = NULL;
@@ -646,8 +417,9 @@
   unsigned int nfields = 2; /* seconds:label */
   size_t       lengths[2];
+  char *       prefix = sh_util_strdup(inprefix);
   char *       new    = sh_util_strdup(str);
-  char **      splits = split_array_braced(new, _("KEEP"), &nfields, lengths);
-
-  if (nfields == 2 && lengths[0] > 0 && lengths[1] > 0)
+  char **      splits = split_array_braced(new, prefix, &nfields, lengths);
+
+  if (splits && nfields == 2 && lengths[0] > 0 && lengths[1] > 0)
     {
       *seconds = strtoul(splits[0], &endptr, 10);
@@ -660,4 +432,5 @@
     SH_FREE(splits);
   SH_FREE(new);
+  SH_FREE(prefix);
   return res;
 }
@@ -677,12 +450,25 @@
   int          captures = 0;
   unsigned int nfields = 2; /* queue:regex */
-  size_t       lengths[2];
+  size_t       lengths[3];
   char *       new    = sh_util_strdup(str);
-  char **      splits = split_array(new, &nfields, ':', lengths);
-
-  int           qpos = 0;
-  volatile int  rpos = 1;
-  unsigned long dsec = 0;
-  char *        dstr = NULL;
+  char **      splits;
+
+  int           qpos  = 0;
+  volatile int  rpos  = 1;
+  unsigned long dsec  = 0;
+  char *        dstr  = NULL;
+  char *        s     = new;
+  volatile char pflag = '-';
+
+  while ( *s && isspace((int)*s) ) ++s;
+  if (0 == strncmp(s, _("KEEP"), 4)      || 
+      0 == strncmp(s, _("CORRELATE"), 9) ||
+      0 == strncmp(s, _("MARK"), 4))
+    {
+      pflag   = s[0];
+      nfields = 3;
+    }
+
+  splits = split_array(new, &nfields, ':', lengths);
 
   dummy_queue = &queue;
@@ -698,10 +484,19 @@
   if (nfields == 3)
     {
-      /* KEEP(nsec):queue:regex
-       */
-      dstr = get_keep(splits[0], &dsec);
-      if (!dstr)
-	{
-	  /* CORRELATE:queue:regex 
+      if (pflag == 'K')
+	{
+	  /* KEEP(nsec,label):queue:regex
+	   */
+	  dstr = get_label_and_time(_("KEEP"), splits[0], &dsec);
+	  if (!dstr)
+	    {
+	      SH_FREE(splits);
+	      SH_FREE(new);
+	      return -1;
+	    }
+	}
+      else if (pflag == 'C')
+	{
+	  /* CORRELATE(description):queue:regex 
 	   */
 	  int retval = sh_keep_match_add(splits[0], splits[1], splits[2]);
@@ -710,4 +505,22 @@
 	  return retval;
 	}
+      else if (pflag == 'M')
+	{
+	  /* MARK(description, interval):queue:regex 
+	   */
+	  int retval = -1;
+
+	  dstr = get_label_and_time(_("MARK"), splits[0], &dsec);
+	  if (dstr)
+	    {
+	      retval = sh_log_mark_add(dstr, dsec, splits[1]);
+	    }
+	  if (retval != 0)
+	    {
+	      SH_FREE(splits);
+	      SH_FREE(new);
+	      return retval;
+	    }
+	}
       ++qpos; ++rpos;
     }
@@ -715,5 +528,5 @@
   if (0 != strcmp(splits[qpos], _("trash")))
     {
-      queue = find_queue(splits[qpos]);
+      queue = sh_log_find_queue(splits[qpos]);
       if (!queue)
 	{
@@ -784,8 +597,15 @@
 
 
-  if (dstr)
+  if (pflag == 'K')
     {
       nr->label   = sh_string_new_from_lchar(dstr, strlen(dstr));
       nr->flags  |= RFL_KEEP;
+      nr->delay   = dsec;
+      SH_FREE(dstr);
+    }
+  else if (pflag == 'M')
+    {
+      nr->label   = sh_string_new_from_lchar(dstr, strlen(dstr));
+      nr->flags  |= RFL_MARK;
       nr->delay   = dsec;
       SH_FREE(dstr);
@@ -1005,4 +825,7 @@
 		  sl_snprintf(emsg,  SH_ERRBUF_SIZE, _("Rule %d matches, result = %d (keep)"), 
 			      count, res);
+		else if ( rule->flags & RFL_MARK )
+		  sl_snprintf(emsg,  SH_ERRBUF_SIZE, _("Rule %d matches, result = %d (mark)"), 
+			      count, res);
 		else
 		  sl_snprintf(emsg,  SH_ERRBUF_SIZE, _("Rule %d matches, result = %d"), 
@@ -1020,4 +843,11 @@
 		sh_keep_add(rule->label, rule->delay, 
 			    timestamp == 0 ? time(NULL) : timestamp);
+	      }
+
+	    else if ( rule->flags & RFL_MARK )
+	      {
+		DEBUG("debug: rule %d matches (mark)\n", count);
+		sh_log_mark_update(rule->label,
+				   timestamp == 0 ? time(NULL) : timestamp);
 	      }
 
@@ -1349,4 +1179,9 @@
 	  msg_report(DEFAULT_SEVERITY, NULL, record);
 	}
+
+      sh_repeat_message_check(record->host, 
+			      record->message, 
+			      record->timestamp);
+			      
       return 0;
     }
Index: /trunk/src/sh_log_mark.c
===================================================================
--- /trunk/src/sh_log_mark.c	(revision 265)
+++ /trunk/src/sh_log_mark.c	(revision 265)
@@ -0,0 +1,240 @@
+#include "config_xor.h"
+
+#ifdef USE_LOGFILE_MONITOR
+
+#include <string.h>
+#include <time.h>
+
+#undef  FIL__
+#define FIL__  _("sh_log_mark.c")
+
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_mem.h"
+#include "sh_string.h"
+#include "sh_error_min.h"
+#include "sh_log_check.h"
+#include "sh_log_evalrule.h"
+#include "zAVLTree.h"
+
+/* #define DEBUG_MARK */
+
+#ifdef DEBUG_MARK
+static void DEBUG(const char *fmt, ...)
+{
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap); /* flawfinder: ignore *//* we control fmt string */
+  va_end(ap);
+  return;
+}
+#else
+static void DEBUG(const char *fmt, ...)
+{
+  (void) fmt;
+  return;
+}
+#endif
+
+static zAVLTree * marklist = NULL;
+
+struct sh_mark_event
+{
+  sh_string   * label;
+  sh_string   * queue_id;
+  time_t        last_seen;
+  time_t        interval;
+  time_t        delay;
+  time_t        last_reported;
+};
+
+static void sh_marklist_free(void * item)
+{
+  struct sh_mark_event * event = (struct sh_mark_event *) item;
+  if (!event)
+    return;
+  sh_string_destroy(&(event->label));
+  sh_string_destroy(&(event->queue_id));
+  SH_FREE(event);
+  return;
+}
+
+void sh_log_mark_destroy()
+{
+  zAVLFreeTree(marklist, sh_marklist_free);
+}
+
+static zAVLKey sh_log_mark_getkey(void const *item)
+{
+  return ((struct sh_mark_event *)item)->label->str;
+}
+
+int sh_log_mark_add (const char * label, time_t interval, const char * qlabel)
+{
+  struct sh_mark_event * event;
+
+  if (!(marklist))
+    {
+      marklist = zAVLAllocTree(sh_log_mark_getkey);
+    }
+
+  event = (struct sh_mark_event *) zAVLSearch(marklist, label);
+  if (event)
+    {
+      event->interval     = interval;
+      sh_string_destroy(&(event->queue_id));
+      event->queue_id     = sh_string_new_from_lchar(qlabel, strlen(qlabel));
+      return 0;
+    }
+
+  event = SH_ALLOC(sizeof(struct sh_mark_event));
+
+  event->last_seen      = time(NULL);
+  event->interval       = interval;
+  event->delay          = 0;
+  event->last_reported  = 0;
+  event->label          = sh_string_new_from_lchar(label, strlen(label));
+  event->queue_id       = sh_string_new_from_lchar(qlabel, strlen(qlabel));
+
+  if (0 != zAVLInsert(marklist, event))
+    {
+      sh_marklist_free(event);
+      return -1;
+    }
+  return 0;
+}
+
+void sh_log_mark_update (sh_string * label, time_t timestamp)
+{
+  struct sh_mark_event * event = 
+    (struct sh_mark_event *) zAVLSearch (marklist, sh_string_str(label));
+
+  DEBUG("debug: running mark update for %s\n", sh_string_str(label));
+ 
+  if (event)
+    {
+      DEBUG("debug: updating, timestamp %lu, last_seen %lu, interval %d\n",
+	    (unsigned long)timestamp, (unsigned long) event->last_seen,
+	    (int)event->interval);
+
+      if ((timestamp > event->last_seen) && 
+	  (event->interval < (timestamp - event->last_seen)) &&
+	  (timestamp > event->last_reported) && 
+	  (event->interval < (timestamp - event->last_reported)))
+	{
+	  event->delay        = timestamp - event->last_seen;
+	  DEBUG("debug: updating delay to %d\n", (int) event->delay);
+	}
+      event->last_seen    = timestamp;
+    }
+  return;
+}
+
+/* This should allow to get all overdue labels with a for loop like:
+ *   for (label = sh_log_mark_first(); label; label = sh_log_mark_next()) {} 
+ */
+
+static zAVLCursor mark_cursor;
+
+static struct sh_mark_event * sh_log_mark_cursor(time_t * delay, time_t now, 
+						 struct sh_mark_event * event)
+{
+  while (event)
+    {
+      DEBUG("debug: echeck, delay %d, now %lu, last_seen %lu, reported %lu\n",
+	    (int) event->delay,
+	    (unsigned long)now, (unsigned long) event->last_seen,
+	    (unsigned long)event->last_reported);
+      if (event->delay > 0)
+	{
+	  DEBUG("debug: event delay > 0, value %d\n", (int) event->delay);
+	  *delay = event->delay;
+	  event->delay = 0;
+	  event->last_reported = time(NULL);
+	  return event;
+	}
+      else if ((now > event->last_seen) && 
+	       (now > event->last_reported) &&
+	       (event->interval < (now - event->last_seen)) &&
+	       (event->interval < (now - event->last_reported))
+	       )
+	{
+	  DEBUG("debug: event delay 0, now %lu, last_seen %lu, reported %lu\n",
+		(unsigned long)now, (unsigned long) event->last_seen,
+		(unsigned long)event->last_reported);
+	  *delay = now - event->last_seen;
+	  event->delay = 0;
+	  /* Subtract 1 sec to prevent accumulation of the
+	   * one second offset. */
+	  event->last_reported = time(NULL) - 1;
+	  return event;
+	}
+      event = (struct sh_mark_event *) zAVLNext(&mark_cursor);
+    }
+
+  return NULL;
+}
+
+struct sh_mark_event * sh_log_mark_first(time_t * delay, time_t now)
+{
+  struct sh_mark_event * event = 
+    (struct sh_mark_event *) zAVLFirst(&mark_cursor, marklist);
+  
+  return sh_log_mark_cursor (delay, now, event);
+}
+
+struct sh_mark_event * sh_log_mark_next(time_t * delay, time_t now)
+{
+  struct sh_mark_event * event = 
+    (struct sh_mark_event *) zAVLNext(&mark_cursor);
+  
+  return sh_log_mark_cursor (delay, now, event);
+}
+
+static int sh_mark_default_severity = SH_ERR_SEVERE;
+
+int sh_log_set_mark_severity (const char * str)
+{
+  int val = sh_error_convert_level(str);
+  if (val < 0)
+    return -1;
+  sh_mark_default_severity = val;
+  return 0;
+}
+
+static struct sh_mark_event ** dummy_event;
+
+void sh_log_mark_check()
+{
+  struct sh_mark_event * event;
+  time_t now = time(NULL);
+  time_t delay;
+
+  /* variable 'event' might be clobbered by 'longjmp' or 'vfork'
+   */
+  dummy_event = &event;
+
+  DEBUG("debug: running mark check\n"); 
+  for (event = sh_log_mark_first(&delay, now); event; 
+       event = sh_log_mark_next (&delay, now)) 
+    {
+      int severity;
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+
+      severity = sh_log_lookup_severity(sh_string_str(event->queue_id));
+      if (severity < 0)
+	severity = sh_mark_default_severity;
+
+      DEBUG("debug: mark check: queue %s, severity %d\n", 
+	    sh_string_str(event->queue_id), severity); 
+      sh_error_handle (severity, 
+		       FIL__, __LINE__, 0, MSG_LOGMON_MARK, 
+		       sh_string_str(event->label), 
+		       (unsigned long) delay);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+    }
+  return;
+}
+
+#endif
Index: /trunk/src/sh_log_parse_syslog.c
===================================================================
--- /trunk/src/sh_log_parse_syslog.c	(revision 264)
+++ /trunk/src/sh_log_parse_syslog.c	(revision 265)
@@ -72,5 +72,5 @@
 
 
-  if (sh_string_len(logline) > 0 && flag_err_debug == SL_TRUE)
+  if (flag_err_debug == SL_TRUE && sh_string_len(logline) > 0)
     {
       SH_MUTEX_LOCK(mutex_thread_nolog);
Index: /trunk/src/sh_log_repeat.c
===================================================================
--- /trunk/src/sh_log_repeat.c	(revision 265)
+++ /trunk/src/sh_log_repeat.c	(revision 265)
@@ -0,0 +1,557 @@
+#include "config_xor.h"
+
+#ifdef USE_LOGFILE_MONITOR
+
+#include <string.h>
+#include <ctype.h>
+
+#undef  FIL__
+#define FIL__  _("sh_log_repeat.c")
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_utils.h"
+#include "sh_string.h"
+#include "sh_log_check.h"
+#include "sh_log_evalrule.h"
+
+#define SH_NHIST   12
+#define SH_NFINT    5
+#define SH_NFIELDS  5*sizeof(SINT32) /* 16 */
+#define SH_NBLOCK  63
+
+typedef enum
+{
+  SH_GFLAG_EMAIL = 1 << 0,
+  SH_GFLAG_PATH  = 1 << 1,
+  SH_GFLAG_IP    = 1 << 2,
+  SH_GFLAG_FQDN  = 1 << 3,
+  SH_GFLAG_NUM   = 1 << 4,
+  SH_GFLAG_ELSE  = 1 << 5,
+
+  SH_GFLAG_XNUM  = 1 << 6,
+  SH_GFLAG_CHAR  = 1 << 7,
+  SH_GFLAG_USC   = 1 << 8
+} SH_GFlags;
+
+
+/* 64 bytes 
+ */
+struct gestalt {
+  unsigned char hist[SH_NHIST];      /* time histogram 12 minutes   */
+  union {
+    unsigned char flags[SH_NFIELDS]; /* flags indicating field type */
+    SINT32        flint[SH_NFINT];
+  } f;
+  UINT16      sum[SH_NFIELDS];     /* checksum of field           */
+  UINT16      ltime;               /* last time, in minutes       */
+  UINT16      total;               /* seen how often?             */
+};
+
+static unsigned int     nrec = 0;    /* size of array               */
+static unsigned int     urec = 0;    /* in use thereof              */
+static struct gestalt * arec = NULL; /* array                       */
+
+static int      repeat_count = 24;   /* triggers report             */
+static int     clean_counter = 0;    /* cleanup after N inserts     */
+static int        free_slots = 0;    /* free slots available        */
+
+#define SH_CLEANUP 256
+
+static struct gestalt * add_entry (unsigned char * flags, UINT16 * sum, 
+				   time_t ltime)
+{
+  struct gestalt * array = NULL;
+
+ start:
+  if (urec < nrec)
+    {
+      if (free_slots)
+	{
+	  unsigned int i;
+	  for (i = 0; i < urec; ++i)
+	    {
+	      if (arec[i].total == 0)
+		{
+		  array = &arec[i];
+		  --free_slots;
+		  break;
+		}
+	    }
+	}
+
+      if (!array)
+	{
+	  array = &arec[urec];
+	  ++urec;
+	}
+
+      memcpy(array->sum,       sum, sizeof(UINT16)      * SH_NFIELDS);
+      memcpy(array->f.flags, flags, sizeof(unsigned char) * SH_NFIELDS);
+      memset(array->hist,        0, sizeof(unsigned char) * SH_NHIST);
+
+      array->ltime               = (UINT16)(ltime % 60);
+      array->hist[SH_NHIST-1]    = 1;
+      array->total               = 1;
+
+      ++clean_counter;
+      return array;
+    }
+
+  array =    SH_ALLOC(sizeof(struct gestalt) * (nrec + SH_NBLOCK + 1));
+  memset(array,    0, sizeof(struct gestalt) * (nrec + SH_NBLOCK + 1));
+  memcpy(array, arec, sizeof(struct gestalt) * (nrec));
+
+  nrec += (SH_NBLOCK + 1);
+  goto start;
+}
+
+static UINT16 shift_history(unsigned char * hist, unsigned int shift, 
+			      UINT16 total)
+{
+  unsigned int i, j = 0;
+
+  if (shift >= SH_NHIST)
+    {
+      memset(hist,      0, sizeof(unsigned char) * SH_NHIST);
+      return 0;
+    }
+
+  for (i = shift; i < SH_NHIST; ++i)
+    {
+      if (j < shift)
+	total  -= hist[j];
+      hist[j] = hist[i];
+      ++j;
+    }
+  for (i = (SH_NHIST-shift); i < SH_NHIST; ++i)
+    {
+      hist[i] = 0;
+    }
+  return total;
+}
+ 
+static void update_entry (struct gestalt * array, time_t ltime)
+{
+  UINT16 ntime = (UINT16)(ltime % 60);
+
+  if (array->ltime == ntime)
+    {
+      if (array->hist[SH_NHIST-1] < 255) 
+	{
+	  ++(array->hist[SH_NHIST-1]);
+	  ++(array->total);
+	}
+    }
+  else if (array->ltime < ntime)
+    {
+      unsigned int shift = ntime - array->ltime;
+      array->total = shift_history(array->hist, shift, array->total);
+      array->hist[SH_NHIST-1] = 1;
+      array->ltime = ntime;
+      ++(array->total);
+    }
+}
+
+static struct gestalt * update_or_add (unsigned char * flags, UINT16 * sum, 
+				       time_t ltime)
+{
+  SINT32 flint[SH_NFINT];
+ start:
+
+  if (arec)
+    {
+      unsigned int i;
+      struct gestalt * array = arec;
+
+      memcpy(flint, flags, SH_NFIELDS);
+
+      for (i = 0; i < urec; ++i)
+	{
+	  /* Check whether field types match. Integer
+	   * comparison is much faster than memcmp() [tested].
+	   */
+	  if (flint[0] == array->f.flint[0] &&
+	      flint[1] == array->f.flint[1] &&
+	      flint[2] == array->f.flint[2] &&
+	      flint[3] == array->f.flint[3] &&
+	      flint[4] == array->f.flint[4])
+	    {
+	      unsigned int j; 
+	      int c1 = 0, c2 = 0;
+	      UINT16 * asum = array->sum;
+
+	      for (j = 0; j < SH_NFIELDS; ++j)
+		{
+		  if (flags[j] == SH_GFLAG_ELSE)
+		    {
+		      ++c1;
+		      if (asum[j] == sum[j]) 
+			++c2;
+		    }
+		}
+
+	      if (c1 == c2)
+		{
+		  /* Found a matching entry, update time histogram
+		   */
+		  update_entry (array, ltime);
+		  return array;
+		}
+	    }
+	  ++array;
+	}
+
+      /* No match found, create a new entry
+       */
+      array = add_entry (flags, sum, ltime);
+      return array;
+    }
+  
+  arec = SH_ALLOC(sizeof(struct gestalt) * SH_NBLOCK);
+  nrec = SH_NBLOCK;
+  urec = 0;
+
+  goto start;
+}
+
+/* --------------------------------------------------------------------
+ *
+ * crc16 checksum from the linux kernel.
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. 
+ */
+
+/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
+UINT16 const crc16_table[256] = {
+  0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+  0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+  0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+  0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+  0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+  0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+  0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+  0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+  0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+  0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+  0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+  0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+  0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+  0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+  0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+  0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+  0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+  0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+  0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+  0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+  0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+  0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+  0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+  0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+  0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+  0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+  0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+  0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+  0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+  0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+  0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+  0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+};
+
+static inline UINT16 crc16_byte(UINT16 crc, const unsigned char data)
+{
+  return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff];
+}
+
+/**
+ * crc16 - compute the CRC-16 for the data buffer
+ * @crc:        previous CRC value
+ * @buffer:     data pointer
+ * @len:        number of bytes in the buffer
+ *
+ * Returns the updated CRC value.
+ */
+static inline UINT16 crc16(UINT16 crc, const unsigned char * buffer, 
+			     size_t len)
+{
+  while (len--)
+    crc = crc16_byte(crc, *buffer++);
+  return crc;
+}
+
+
+/* end crc16 code
+ *
+ * -------------------------------------------------------------------- */
+
+static void  classify(char ** splits, size_t * lengths, unsigned int nfields, 
+		      unsigned char * flags, UINT16 * sums)
+{
+  unsigned int i;
+  unsigned int flag;
+
+  /* flags we don't want to see in XYZ 
+   */
+  static int m_ip    = SH_GFLAG_PATH|SH_GFLAG_EMAIL|SH_GFLAG_USC|SH_GFLAG_ELSE|SH_GFLAG_CHAR|SH_GFLAG_XNUM;
+  static int m_num   = SH_GFLAG_PATH|SH_GFLAG_EMAIL|SH_GFLAG_USC|SH_GFLAG_ELSE|SH_GFLAG_CHAR;
+  static int m_fqdn  = SH_GFLAG_PATH|SH_GFLAG_EMAIL|SH_GFLAG_USC|SH_GFLAG_ELSE;
+  static int m_email = SH_GFLAG_PATH;
+
+  nfields = (nfields > SH_NFIELDS) ? SH_NFIELDS : nfields;
+
+  for (i = 0; i < nfields; ++i) 
+    {
+      char *p = splits[i];
+      unsigned int np   = 0;
+      unsigned int fqdn = 0;
+
+      flag = 0;
+
+      while (*p)
+	{
+	  if (isxdigit((unsigned int)*p))
+	    {
+	      if (isdigit((unsigned int)*p))
+		{
+		  flag |= SH_GFLAG_NUM;
+		}
+	      else
+		{
+		  flag |= SH_GFLAG_XNUM;
+		}
+	    }
+	  else if (*p == '.')
+	    {
+	      flag |= SH_GFLAG_IP;
+	      ++np;
+	    }
+	  else if (*p == '/')
+	    {
+	      flag |= SH_GFLAG_PATH;
+	    }
+	  else if (*p == '@')
+	    {
+	      flag |= SH_GFLAG_EMAIL;
+	    }
+	  else if (*p == '-')
+	    {
+	      flag |= SH_GFLAG_FQDN;
+	    }
+	  else if (*p == '_')
+	    {
+	      flag |= SH_GFLAG_USC;
+	    }
+	  else if (isalpha((unsigned int)*p))
+	    {
+	      if (flag & SH_GFLAG_IP)
+		++fqdn;
+	      flag |= SH_GFLAG_CHAR;
+	    }
+	  else if (!isascii((unsigned int)*p))
+	    {
+	      flags[i] = SH_GFLAG_ELSE;
+	      break;
+	    }
+	  else
+	    {
+	      flag |= SH_GFLAG_ELSE;
+	    }
+	  ++p;
+	}
+
+      if (flags[i] == 0)
+	{
+	  if (0 == (flag & m_ip)         && 
+	      0 != (flag & SH_GFLAG_IP)  && 
+	      0 != (flag & SH_GFLAG_NUM) && 
+	      np > 2)
+	    {
+	      flags[i] = SH_GFLAG_IP;
+	    }
+	  else if (0 == (flag & m_num) && 
+		   (0 != (flag & SH_GFLAG_NUM) || 0 != (flag & SH_GFLAG_XNUM)))
+	    {
+	      flags[i] = SH_GFLAG_NUM;
+	    }
+	  else if (0 == (flag & m_fqdn)        && 
+		   0 != (flag & SH_GFLAG_IP)   && 
+		   0 != (flag & SH_GFLAG_CHAR) && 
+		   fqdn)
+	    {
+	      flags[i] = SH_GFLAG_FQDN;
+	    }
+	  else if ('/' == splits[i][0])
+	    {
+	      flags[i] = SH_GFLAG_PATH;
+	    }
+	  else if (0 == (flag & m_email)        && 
+		   0 != (flag & SH_GFLAG_EMAIL) && 
+		   0 != (flag & SH_GFLAG_CHAR)) 
+	    {
+	      flags[i] = SH_GFLAG_EMAIL;
+	    }
+	  else 
+	    {
+	      flags[i] = SH_GFLAG_ELSE;
+	    }
+	}
+
+      /* CRC-16 checksum
+       */ 
+      sums[i] = crc16(0, (unsigned char *) splits[i], lengths[i]);
+    }
+
+  return;
+}
+
+static void cleanup_array (time_t ltime)
+{
+  UINT16 ntime = (UINT16)(ltime % 60);
+
+  if (ntime > 12) ntime -= 12;
+
+  if (arec && urec > 0)
+    {
+      struct gestalt * array;
+      unsigned int i, last, urec_orig = urec;
+
+      last = urec-1;
+      array = &arec[0];
+  
+      for (i = 0; i < urec_orig; ++i)
+	{
+	  if (array->ltime < ntime)
+	    {
+	      memset(array,    0, sizeof(struct gestalt));
+	      if (i != last)
+		++free_slots;
+	      else
+		--urec;
+	    }
+	}
+      ++array;
+    }
+  clean_counter = 0;
+  return;
+}
+
+/* ----------------------------------------------------------------------
+ *
+ *   Public functions
+ */
+
+int sh_repeat_set_trigger (const char * str)
+{
+  unsigned long  value;
+  char * foo;
+
+  value = (size_t) strtoul(str, &foo, 0);
+
+  if (*foo == '\0' && value < 65535) {
+    repeat_count = value;
+    return 0;
+  }
+  return -1;
+}
+
+static char * sh_repeat_queue = NULL;
+
+int sh_repeat_set_queue (const char * str)
+{
+  if (!str)
+    return -1;
+  if (sh_repeat_queue)
+    SH_FREE(sh_repeat_queue);
+  sh_repeat_queue = sh_util_strdup(str);
+  return 0;
+}
+
+static int sh_repeat_cron = S_FALSE;
+
+int sh_repeat_set_cron (const char * str)
+{
+  return sh_util_flagval(str, &sh_repeat_cron);
+}
+
+int sh_repeat_message_check (const sh_string * host, 
+			     const sh_string * msg, 
+			     time_t ltime)
+{
+  struct gestalt * array;
+
+  UINT16         sums[SH_NFIELDS] = { 0 };
+  unsigned char flags[SH_NFIELDS] = { 0 };
+
+  /* split message into SH_NFIELDS+1, discard last  */
+
+  unsigned int nfields = SH_NFIELDS+1;
+  size_t       lengths[SH_NFIELDS+1];
+  char *       new;
+  char **      splits;
+
+  if (repeat_count == 0)
+    return 0;
+
+  if (sh_repeat_cron == S_FALSE)
+    {
+      char * s = sh_string_str(msg);
+
+      if (0 == strcmp(s, _("cron")) || 0 == strcmp(s, _("CRON")))
+	return 0;
+    }
+
+  new = sh_util_strdup_l(sh_string_str(msg), sh_string_len(msg));
+
+  splits = split_array_token (new, &nfields, lengths, 
+			      " :,()='[]<>\t\n");
+
+  /* classify fields                                */
+
+  classify (splits, lengths, nfields, flags, sums); 
+
+  /* compare                                        */
+
+  array = update_or_add (flags, sums, ltime);
+
+  /* report                                         */
+
+  if (array->total > repeat_count)
+    {
+      volatile int repeat = array->total;
+      char * tmpmsg;
+      char * tmphost;
+
+      /* issue report             */
+
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      tmphost = sh_util_safe_name (sh_string_str(host));
+      tmpmsg  = sh_util_safe_name_keepspace (sh_string_str(msg));
+      sh_error_handle (sh_log_lookup_severity(sh_repeat_queue), 
+		       FIL__, __LINE__, 0, MSG_LOGMON_BURST, 
+		       repeat, tmpmsg, tmphost);
+      SH_FREE(tmpmsg);
+      SH_FREE(tmphost);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+
+      /* mark slot as free        */
+
+      memset(array,    0, sizeof(struct gestalt));
+      if (array != &arec[urec-1])
+	++free_slots;
+      else
+	urec -= 1;
+    }
+
+  SH_FREE(new);
+
+  /* run cleanup routine                            */
+
+  if (clean_counter >= SH_CLEANUP)
+    {
+      cleanup_array(ltime);
+    }
+
+  return 0;
+}
+
+#endif
Index: /trunk/src/sh_readconf.c
===================================================================
--- /trunk/src/sh_readconf.c	(revision 264)
+++ /trunk/src/sh_readconf.c	(revision 265)
@@ -28,29 +28,25 @@
 
 #include "samhain.h"
+#include "sh_calls.h"
 #include "sh_error.h"
-#include "sh_database.h"
-#include "sh_unix.h"
-#include "sh_utils.h"
+#include "sh_extern.h"
 #include "sh_files.h"
-#include "sh_mail.h"
-#include "sh_nmail.h"
-#include "sh_calls.h"
-#include "sh_tiger.h"
 #include "sh_forward.h"
-#include "sh_modules.h"
 #include "sh_gpg.h"
 #include "sh_hash.h"
 #include "sh_ignore.h"
+#include "sh_database.h"
+#include "sh_mail.h"
+#include "sh_modules.h"
+#include "sh_nmail.h"
 #include "sh_prelink.h"
-#include "sh_extern.h"
-#include "sh_tools.h"
-
-#ifdef WITH_DATABASE
-#include "sh_database.h"
-#endif
-
 #ifdef HAVE_LIBPRELUDE
 #include "sh_prelude.h"
 #endif
+#include "sh_tiger.h"
+#include "sh_tools.h"
+#include "sh_unix.h"
+#include "sh_utils.h"
+
 
 extern int set_reverse_lookup (const char * c);
Index: /trunk/src/sh_socket.c
===================================================================
--- /trunk/src/sh_socket.c	(revision 264)
+++ /trunk/src/sh_socket.c	(revision 265)
@@ -385,4 +385,9 @@
     }
 
+  if (0 != sh_unix_check_piddir (sh_sockname))
+    {
+      SH_FREE(sh_sockname);
+      SL_RETURN((-1),_("sh_socket_open_int"));
+    }
 
   pf_unix_fd = socket (PF_UNIX, SOCK_STREAM, 0);
Index: /trunk/src/sh_string.c
===================================================================
--- /trunk/src/sh_string.c	(revision 264)
+++ /trunk/src/sh_string.c	(revision 265)
@@ -133,5 +133,5 @@
 char ** split_array_ws_int (char *line, 
 			    unsigned int * nfields, size_t * lengths,
-			    int isList)
+			    const char *delim, int isList)
 {
   char *a, *e, *s;
@@ -161,10 +161,11 @@
       else
 	{
-	  if ( *s && (*s == ' ' || *s == '\t' || *s == ','))
-	    {
-	      do {
-		++s;
-	      } while ( *s && (*s == ' ' || *s == '\t' || *s == ','));
-	    }
+          if ( *s && strchr(delim, (int)*s))
+            {
+              do {
+                ++s;
+              } while ( *s && strchr(delim, (int)*s));
+            }
+
 	}
 
@@ -183,7 +184,7 @@
 	  else
 	    {
-	      do {
-		a++;
-	      } while ( *a && (*a != ' ' && *a != '\t' && *a != ','));
+              do {
+                a++;
+              } while ( *a && NULL == strchr(delim, (int)*a));
 	    }
 
@@ -231,5 +232,5 @@
 			unsigned int * nfields, size_t * lengths)
 {
-  return split_array_ws_int (line, nfields, lengths, SH_SPLIT_WS);
+  return split_array_ws_int (line, nfields, lengths, NULL, SH_SPLIT_WS);
 }
 
@@ -237,5 +238,12 @@
 			  unsigned int * nfields, size_t * lengths)
 {
-  return split_array_ws_int (line, nfields, lengths, SH_SPLIT_LIST);
+  return split_array_ws_int (line, nfields, lengths, ", \t", SH_SPLIT_LIST);
+}
+
+char ** split_array_token (char *line, 
+			   unsigned int * nfields, size_t * lengths,
+			   const char * token)
+{
+  return split_array_ws_int (line, nfields, lengths, token, SH_SPLIT_LIST);
 }
 
Index: /trunk/src/sh_unix.c
===================================================================
--- /trunk/src/sh_unix.c	(revision 264)
+++ /trunk/src/sh_unix.c	(revision 265)
@@ -4235,5 +4235,5 @@
 }
 
-int sh_unix_check_piddir (char * pidfile)
+int sh_unix_check_piddir (char * pidpath)
 {
   static        struct stat   buf;
@@ -4243,5 +4243,5 @@
   SL_ENTER(_("sh_unix_check_piddir"));
 
-  pid_dir = sh_util_dirname (pidfile);
+  pid_dir = sh_util_dirname (pidpath);
 
   status = retry_lstat (FIL__, __LINE__, pid_dir, &buf);
