Major redesign. All input is handled through XML. Raw input data is first
authorarjen <arjen>
Thu, 4 Dec 2003 10:38:09 +0000 (10:38 +0000)
committerarjen <arjen>
Thu, 4 Dec 2003 10:38:09 +0000 (10:38 +0000)
transformed into an XML document for further processing.
A collection of polymorphic classes handle the transformation of various
input formats into XML.
Classifying input data is done with a finite improbability calculation.

15 files changed:
src/gcm_input/Makefile.am
src/gcm_input/Makefile.in
src/gcm_input/access_cooker.cpp
src/gcm_input/gcm_input.cpp
src/gcm_input/log_filter.cpp [new file with mode: 0644]
src/gcm_input/log_filter.h [new file with mode: 0644]
src/gcm_input/message.cpp
src/gcm_input/message.h
src/gcm_input/message_buffer.h [new file with mode: 0644]
src/gcm_input/message_filter.cpp [new file with mode: 0644]
src/gcm_input/message_filter.h [new file with mode: 0644]
src/gcm_input/rpm_filter.cpp [new file with mode: 0644]
src/gcm_input/rpm_filter.h [new file with mode: 0644]
src/gcm_input/xml_cooker.cpp [new file with mode: 0644]
src/gcm_input/xml_cooker.h [new file with mode: 0644]

index 646e560..a46f49b 100644 (file)
@@ -5,6 +5,8 @@ INCLUDES = -I../include
 LDADD = ../lib/libgnucomo.a
 
 gcm_input_SOURCES = gcm_input.cpp message.cpp string_utils.cpp syslog_cooker.cpp \
-                    irix_syslog_cooker.cpp access_cooker.cpp error_cooker.cpp
+                    irix_syslog_cooker.cpp access_cooker.cpp error_cooker.cpp \
+                    xml_cooker.cpp \
+                    message_filter.cpp log_filter.cpp rpm_filter.cpp
 
 logrunner_SOURCES = logrunner.cpp string_utils.cpp
index c2c7d28..98c3f81 100644 (file)
@@ -1,6 +1,8 @@
-# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
+# Makefile.in generated by automake 1.6.3 from Makefile.am.
+# @configure_input@
 
-# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+# Free Software Foundation, Inc.
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -10,7 +12,7 @@
 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 # PARTICULAR PURPOSE.
 
-
+@SET_MAKE@
 SHELL = @SHELL@
 
 srcdir = @srcdir@
@@ -31,13 +33,9 @@ infodir = @infodir@
 mandir = @mandir@
 includedir = @includedir@
 oldincludedir = /usr/include
-
-DESTDIR =
-
 pkgdatadir = $(datadir)/@PACKAGE@
 pkglibdir = $(libdir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
-
 top_builddir = ../..
 
 ACLOCAL = @ACLOCAL@
@@ -45,30 +43,50 @@ AUTOCONF = @AUTOCONF@
 AUTOMAKE = @AUTOMAKE@
 AUTOHEADER = @AUTOHEADER@
 
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
 INSTALL = @INSTALL@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_DATA = @INSTALL_DATA@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_HEADER = $(INSTALL_DATA)
 transform = @program_transform_name@
-
 NORMAL_INSTALL = :
 PRE_INSTALL = :
 POST_INSTALL = :
 NORMAL_UNINSTALL = :
 PRE_UNINSTALL = :
 POST_UNINSTALL = :
+
+EXEEXT = @EXEEXT@
+OBJEXT = @OBJEXT@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+AMTAR = @AMTAR@
 AWK = @AWK@
 CC = @CC@
 CPP = @CPP@
 CXX = @CXX@
+DEPDIR = @DEPDIR@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
 LN_S = @LN_S@
-MAKEINFO = @MAKEINFO@
 PACKAGE = @PACKAGE@
 RANLIB = @RANLIB@
+STRIP = @STRIP@
 VERSION = @VERSION@
 XML_CONFIG = @XML_CONFIG@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
 YACC = @YACC@
+am__include = @am__include@
+am__quote = @am__quote@
+install_sh = @install_sh@
 
 bin_PROGRAMS = gcm_input logrunner
 
@@ -76,283 +94,277 @@ INCLUDES = -I../include
 LDADD = ../lib/libgnucomo.a
 
 gcm_input_SOURCES = gcm_input.cpp message.cpp string_utils.cpp syslog_cooker.cpp \
-                    irix_syslog_cooker.cpp access_cooker.cpp error_cooker.cpp
+                    irix_syslog_cooker.cpp access_cooker.cpp error_cooker.cpp \
+                    xml_cooker.cpp \
+                    message_filter.cpp log_filter.cpp rpm_filter.cpp
 
 
 logrunner_SOURCES = logrunner.cpp string_utils.cpp
+subdir = src/gcm_input
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
-CONFIG_CLEAN_FILES = 
-PROGRAMS =  $(bin_PROGRAMS)
-
+CONFIG_CLEAN_FILES =
+bin_PROGRAMS = gcm_input$(EXEEXT) logrunner$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS)
+
+am_gcm_input_OBJECTS = gcm_input.$(OBJEXT) message.$(OBJEXT) \
+       string_utils.$(OBJEXT) syslog_cooker.$(OBJEXT) \
+       irix_syslog_cooker.$(OBJEXT) access_cooker.$(OBJEXT) \
+       error_cooker.$(OBJEXT) xml_cooker.$(OBJEXT) \
+       message_filter.$(OBJEXT) log_filter.$(OBJEXT) \
+       rpm_filter.$(OBJEXT)
+gcm_input_OBJECTS = $(am_gcm_input_OBJECTS)
+gcm_input_LDADD = $(LDADD)
+gcm_input_DEPENDENCIES = ../lib/libgnucomo.a
+gcm_input_LDFLAGS =
+am_logrunner_OBJECTS = logrunner.$(OBJEXT) string_utils.$(OBJEXT)
+logrunner_OBJECTS = $(am_logrunner_OBJECTS)
+logrunner_LDADD = $(LDADD)
+logrunner_DEPENDENCIES = ../lib/libgnucomo.a
+logrunner_LDFLAGS =
 
-DEFS = @DEFS@ -I. -I$(srcdir) 
+DEFS = @DEFS@
+DEFAULT_INCLUDES =  -I. -I$(srcdir)
 CPPFLAGS = @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
 LIBS = @LIBS@
-X_CFLAGS = @X_CFLAGS@
-X_LIBS = @X_LIBS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
-gcm_input_OBJECTS =  gcm_input.o message.o string_utils.o \
-syslog_cooker.o irix_syslog_cooker.o access_cooker.o error_cooker.o
-gcm_input_LDADD = $(LDADD)
-gcm_input_DEPENDENCIES =  ../lib/libgnucomo.a
-gcm_input_LDFLAGS = 
-logrunner_OBJECTS =  logrunner.o string_utils.o
-logrunner_LDADD = $(LDADD)
-logrunner_DEPENDENCIES =  ../lib/libgnucomo.a
-logrunner_LDFLAGS = 
-CXXFLAGS = @CXXFLAGS@
-CXXCOMPILE = $(CXX) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/access_cooker.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/error_cooker.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/gcm_input.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/irix_syslog_cooker.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/log_filter.Po ./$(DEPDIR)/logrunner.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/message.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/message_filter.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/rpm_filter.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/string_utils.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/syslog_cooker.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/xml_cooker.Po
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
 CXXLD = $(CXX)
-CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@
-DIST_COMMON =  Makefile.am Makefile.in
-
-
-DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
-
-TAR = gtar
-GZIP_ENV = --best
-DEP_FILES =  .deps/access_cooker.P .deps/error_cooker.P \
-.deps/gcm_input.P .deps/irix_syslog_cooker.P .deps/logrunner.P \
-.deps/message.P .deps/string_utils.P .deps/syslog_cooker.P
+CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
+       -o $@
+CXXFLAGS = @CXXFLAGS@
+DIST_SOURCES = $(gcm_input_SOURCES) $(logrunner_SOURCES)
+DIST_COMMON = Makefile.am Makefile.in
 SOURCES = $(gcm_input_SOURCES) $(logrunner_SOURCES)
-OBJECTS = $(gcm_input_OBJECTS) $(logrunner_OBJECTS)
-
-all: all-redirect
-.SUFFIXES:
-.SUFFIXES: .S .c .cpp .o .s
-$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) 
-       cd $(top_srcdir) && $(AUTOMAKE) --gnu src/gcm_input/Makefile
 
-Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status $(BUILT_SOURCES)
-       cd $(top_builddir) \
-         && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
-
-
-mostlyclean-binPROGRAMS:
-
-clean-binPROGRAMS:
-       -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
-
-distclean-binPROGRAMS:
-
-maintainer-clean-binPROGRAMS:
+all: all-am
 
+.SUFFIXES:
+.SUFFIXES: .cpp .o .obj
+$(srcdir)/Makefile.in:  Makefile.am  $(top_srcdir)/configure.in $(ACLOCAL_M4)
+       cd $(top_srcdir) && \
+         $(AUTOMAKE) --gnu  src/gcm_input/Makefile
+Makefile:  $(srcdir)/Makefile.in  $(top_builddir)/config.status
+       cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
 install-binPROGRAMS: $(bin_PROGRAMS)
        @$(NORMAL_INSTALL)
        $(mkinstalldirs) $(DESTDIR)$(bindir)
        @list='$(bin_PROGRAMS)'; for p in $$list; do \
-         if test -f $$p; then \
-           echo "  $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
-            $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+         p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+         if test -f $$p \
+         ; then \
+           f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+          echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) $$p $(DESTDIR)$(bindir)/$$f"; \
+          $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) $$p $(DESTDIR)$(bindir)/$$f; \
          else :; fi; \
        done
 
 uninstall-binPROGRAMS:
        @$(NORMAL_UNINSTALL)
-       list='$(bin_PROGRAMS)'; for p in $$list; do \
-         rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+       @list='$(bin_PROGRAMS)'; for p in $$list; do \
+         f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+         echo " rm -f $(DESTDIR)$(bindir)/$$f"; \
+         rm -f $(DESTDIR)$(bindir)/$$f; \
        done
 
-.s.o:
-       $(COMPILE) -c $<
-
-.S.o:
-       $(COMPILE) -c $<
+clean-binPROGRAMS:
+       -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+gcm_input$(EXEEXT): $(gcm_input_OBJECTS) $(gcm_input_DEPENDENCIES) 
+       @rm -f gcm_input$(EXEEXT)
+       $(CXXLINK) $(gcm_input_LDFLAGS) $(gcm_input_OBJECTS) $(gcm_input_LDADD) $(LIBS)
+logrunner$(EXEEXT): $(logrunner_OBJECTS) $(logrunner_DEPENDENCIES) 
+       @rm -f logrunner$(EXEEXT)
+       $(CXXLINK) $(logrunner_LDFLAGS) $(logrunner_OBJECTS) $(logrunner_LDADD) $(LIBS)
 
 mostlyclean-compile:
-       -rm -f *.o core *.core
-
-clean-compile:
+       -rm -f *.$(OBJEXT) core *.core
 
 distclean-compile:
        -rm -f *.tab.c
 
-maintainer-clean-compile:
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/access_cooker.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error_cooker.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gcm_input.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irix_syslog_cooker.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log_filter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logrunner.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message_filter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpm_filter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string_utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syslog_cooker.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xml_cooker.Po@am__quote@
 
-gcm_input: $(gcm_input_OBJECTS) $(gcm_input_DEPENDENCIES)
-       @rm -f gcm_input
-       $(CXXLINK) $(gcm_input_LDFLAGS) $(gcm_input_OBJECTS) $(gcm_input_LDADD) $(LIBS)
+distclean-depend:
+       -rm -rf ./$(DEPDIR)
 
-logrunner: $(logrunner_OBJECTS) $(logrunner_DEPENDENCIES)
-       @rm -f logrunner
-       $(CXXLINK) $(logrunner_LDFLAGS) $(logrunner_OBJECTS) $(logrunner_LDADD) $(LIBS)
 .cpp.o:
-       $(CXXCOMPILE) -c $<
+@AMDEP_TRUE@   source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@   depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@   $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+       $(CXXCOMPILE) -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$<
+
+.cpp.obj:
+@AMDEP_TRUE@   source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@   depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@   $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+       $(CXXCOMPILE) -c -o $@ `cygpath -w $<`
+CXXDEPMODE = @CXXDEPMODE@
+uninstall-info-am:
+
+ETAGS = etags
+ETAGSFLAGS =
 
 tags: TAGS
 
-ID: $(HEADERS) $(SOURCES) $(LISP)
-       list='$(SOURCES) $(HEADERS)'; \
-       unique=`for i in $$list; do echo $$i; done | \
-         awk '    { files[$$0] = 1; } \
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
               END { for (i in files) print i; }'`; \
-       here=`pwd` && cd $(srcdir) \
-         && mkid -f$$here/ID $$unique $(LISP)
+       mkid -fID $$unique
 
-TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) $(LISP)
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
        tags=; \
        here=`pwd`; \
-       list='$(SOURCES) $(HEADERS)'; \
-       unique=`for i in $$list; do echo $$i; done | \
-         awk '    { files[$$0] = 1; } \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
               END { for (i in files) print i; }'`; \
-       test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
-         || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags  $$unique $(LISP) -o $$here/TAGS)
-
-mostlyclean-tags:
+       test -z "$(ETAGS_ARGS)$$tags$$unique" \
+         || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+            $$tags $$unique
 
-clean-tags:
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && cd $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) $$here
 
 distclean-tags:
-       -rm -f TAGS ID
-
-maintainer-clean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 
-distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
-
-subdir = src/gcm_input
+top_distdir = ../..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
 
 distdir: $(DISTFILES)
-       here=`cd $(top_builddir) && pwd`; \
-       top_distdir=`cd $(top_distdir) && pwd`; \
-       distdir=`cd $(distdir) && pwd`; \
-       cd $(top_srcdir) \
-         && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu src/gcm_input/Makefile
-       @for file in $(DISTFILES); do \
-         d=$(srcdir); \
+       @list='$(DISTFILES)'; for file in $$list; do \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+         if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+           dir="/$$dir"; \
+           $(mkinstalldirs) "$(distdir)$$dir"; \
+         else \
+           dir=''; \
+         fi; \
          if test -d $$d/$$file; then \
-           cp -pr $$d/$$file $(distdir)/$$file; \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+           fi; \
+           cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
          else \
            test -f $(distdir)/$$file \
-           || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
-           || cp -p $$d/$$file $(distdir)/$$file || :; \
+           || cp -p $$d/$$file $(distdir)/$$file \
+           || exit 1; \
          fi; \
        done
-
-DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
-
--include $(DEP_FILES)
-
-mostlyclean-depend:
-
-clean-depend:
-
-distclean-depend:
-       -rm -rf .deps
-
-maintainer-clean-depend:
-
-%.o: %.c
-       @echo '$(COMPILE) -c $<'; \
-       $(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
-       @-cp .deps/$(*F).pp .deps/$(*F).P; \
-       tr ' ' '\012' < .deps/$(*F).pp \
-         | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
-           >> .deps/$(*F).P; \
-       rm .deps/$(*F).pp
-
-%.lo: %.c
-       @echo '$(LTCOMPILE) -c $<'; \
-       $(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
-       @-sed -e 's/^\([^:]*\)\.o[      ]*:/\1.lo \1.o :/' \
-         < .deps/$(*F).pp > .deps/$(*F).P; \
-       tr ' ' '\012' < .deps/$(*F).pp \
-         | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
-           >> .deps/$(*F).P; \
-       rm -f .deps/$(*F).pp
-
-%.o: %.cpp
-       @echo '$(CXXCOMPILE) -c $<'; \
-       $(CXXCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
-       @-cp .deps/$(*F).pp .deps/$(*F).P; \
-       tr ' ' '\012' < .deps/$(*F).pp \
-         | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
-           >> .deps/$(*F).P; \
-       rm .deps/$(*F).pp
-
-%.lo: %.cpp
-       @echo '$(LTCXXCOMPILE) -c $<'; \
-       $(LTCXXCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
-       @-sed -e 's/^\([^:]*\)\.o[      ]*:/\1.lo \1.o :/' \
-         < .deps/$(*F).pp > .deps/$(*F).P; \
-       tr ' ' '\012' < .deps/$(*F).pp \
-         | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
-           >> .deps/$(*F).P; \
-       rm -f .deps/$(*F).pp
-info-am:
-info: info-am
-dvi-am:
-dvi: dvi-am
 check-am: all-am
 check: check-am
-installcheck-am:
-installcheck: installcheck-am
-install-exec-am: install-binPROGRAMS
-install-exec: install-exec-am
+all-am: Makefile $(PROGRAMS)
 
-install-data-am:
-install-data: install-data-am
+installdirs:
+       $(mkinstalldirs) $(DESTDIR)$(bindir)
 
-install-am: all-am
-       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
 install: install-am
-uninstall-am: uninstall-binPROGRAMS
+install-exec: install-exec-am
+install-data: install-data-am
 uninstall: uninstall-am
-all-am: Makefile $(PROGRAMS)
-all-redirect: all-am
-install-strip:
-       $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
-installdirs:
-       $(mkinstalldirs)  $(DESTDIR)$(bindir)
 
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
 
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
 mostlyclean-generic:
 
 clean-generic:
 
 distclean-generic:
        -rm -f Makefile $(CONFIG_CLEAN_FILES)
-       -rm -f config.cache config.log stamp-h stamp-h[0-9]*
 
 maintainer-clean-generic:
-mostlyclean-am:  mostlyclean-binPROGRAMS mostlyclean-compile \
-               mostlyclean-tags mostlyclean-depend mostlyclean-generic
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
 
-mostlyclean: mostlyclean-am
+clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
 
-clean-am:  clean-binPROGRAMS clean-compile clean-tags clean-depend \
-               clean-generic mostlyclean-am
+distclean: distclean-am
 
-clean: clean-am
+distclean-am: clean-am distclean-compile distclean-depend \
+       distclean-generic distclean-tags
 
-distclean-am:  distclean-binPROGRAMS distclean-compile distclean-tags \
-               distclean-depend distclean-generic clean-am
+dvi: dvi-am
 
-distclean: distclean-am
+dvi-am:
 
-maintainer-clean-am:  maintainer-clean-binPROGRAMS \
-               maintainer-clean-compile maintainer-clean-tags \
-               maintainer-clean-depend maintainer-clean-generic \
-               distclean-am
-       @echo "This command is intended for maintainers to use;"
-       @echo "it deletes files that may require special tools to rebuild."
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
 
 maintainer-clean: maintainer-clean-am
 
-.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \
-maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \
-mostlyclean-compile distclean-compile clean-compile \
-maintainer-clean-compile tags mostlyclean-tags distclean-tags \
-clean-tags maintainer-clean-tags distdir mostlyclean-depend \
-distclean-depend clean-depend maintainer-clean-depend info-am info \
-dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
-install-exec install-data-am install-data install-am install \
-uninstall-am uninstall all-redirect all-am all installdirs \
-mostlyclean-generic distclean-generic clean-generic \
-maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
 
+.PHONY: GTAGS all all-am check check-am clean clean-binPROGRAMS \
+       clean-generic distclean distclean-compile distclean-depend \
+       distclean-generic distclean-tags distdir dvi dvi-am info \
+       info-am install install-am install-binPROGRAMS install-data \
+       install-data-am install-exec install-exec-am install-info \
+       install-info-am install-man install-strip installcheck \
+       installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic tags uninstall uninstall-am \
+       uninstall-binPROGRAMS uninstall-info-am
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index 339114b..5188204 100644 (file)
@@ -8,7 +8,7 @@
 ***********************
 **      FILE NAME      : access_cooker.cpp
 **      SYSTEM NAME    : 
-**      VERSION NUMBER : $Revision: 1.1 $
+**      VERSION NUMBER : $Revision: 1.2 $
 **
 **  DESCRIPTION      :  Cooks Apache http daemon access log lines
 **
 
 /*****************************
    $Log: access_cooker.cpp,v $
-   Revision 1.1  2003-08-11 16:56:15  arjen
+   Revision 1.2  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+   Revision 1.1  2003/08/11 16:56:15  arjen
    Different kinds of log files are parsed by a collection of objects
    of different classes, derived from the base class line_cooker
    Depending on the message content or the message_type element in
 
 *****************************/
 
-/* static const char *RCSID = "$Id: access_cooker.cpp,v 1.1 2003-08-11 16:56:15 arjen Exp $"; */
+/* static const char *RCSID = "$Id: access_cooker.cpp,v 1.2 2003-12-04 10:38:09 arjen Exp $"; */
 
 #include <ctype.h>
 
 #include "access_cooker.h"
 
-static const regex re_accesslog("(GET|POST) .+ HTTP");
+static const regex re_accesslog("(GET|POST|HEAD) .+ HTTP");
 
 bool access_cooker::check_pattern(String logline)
 {
index 4215aa4..b85fe9a 100644 (file)
@@ -7,7 +7,7 @@
 ***********************
 **      FILE NAME      : gcm_input.cpp
 **      SYSTEM NAME    : Gnucomo - Gnu Computer Monitoring
-**      VERSION NUMBER : $Revision: 1.11 $
+**      VERSION NUMBER : $Revision: 1.12 $
 **
 **  DESCRIPTION      :  Application to store client messages into the database
 **                      The client message contains a log file from one of the
 ********************************
 **      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
 **      CREATION DATE   : Aug 29, 2002
-**      LAST UPDATE     : Aug 11, 2003
+**      LAST UPDATE     : Nov 26, 2003
 **      MODIFICATIONS   : 
 **************************************************************************/
 
 /*****************************
    $Log: gcm_input.cpp,v $
-   Revision 1.11  2003-10-27 13:00:15  arjen
+   Revision 1.12  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+   Revision 1.11  2003/10/27 13:00:15  arjen
    Catch exceptions from the database library
 
    Revision 1.10  2003/09/03 06:58:31  arjen
 
 *****************************/
 
-static const char *RCSID = "$Id: gcm_input.cpp,v 1.11 2003-10-27 13:00:15 arjen Exp $";
+static const char *RCSID = "$Id: gcm_input.cpp,v 1.12 2003-12-04 10:38:09 arjen Exp $";
 
 #include <fstream>
 
 #include <getopt.h>
 
 #include "message.h"
+#include "log_filter.h"
+#include "rpm_filter.h"
+
+#include "xml_cooker.h"
 #include "syslog_cooker.h"
 #include "irix_syslog_cooker.h"
 #include "access_cooker.h"
@@ -118,7 +129,7 @@ static const char *RCSID = "$Id: gcm_input.cpp,v 1.11 2003-10-27 13:00:15 arjen
 bool verbose = false;
 bool testmode = false;
 bool incremental = false;
-std::ostream *log = &std::cerr;
+std::ostream *Log = &std::cerr;
 
 static char *Version = "gcm_input version 0.0.8 - Sep 04, 2003";
 
@@ -236,17 +247,17 @@ int main(int argc, char *argv[])
       }
       else
       {
-         log = &logfile;
+         Log = &logfile;
       }
    }
    verbose = verbose || level > 0;
 
    if (verbose)
    {
-      *log << "Hostname = " << hostname;
-      *log << " Arrival = " << arrival;
-      *log << " Service = " << service << "\n";
-      *log << "Config OK.\n";
+      *Log << "Hostname = " << hostname;
+      *Log << " Arrival = " << arrival;
+      *Log << " Service = " << service << "\n";
+      *Log << "Config OK.\n";
    }
 
    /*  Try to connect to the database */
@@ -257,15 +268,24 @@ int main(int argc, char *argv[])
    {
 
       client_message      msg(&std::cin, db);
+
+      message_filter      shortcircuit(hostname, arrival, service);
+      log_filter          lf(hostname, arrival, service);
+      rpm_filter          rf(hostname, arrival, service);
+
       syslog_cooker       slc;
       irix_syslog_cooker  islc;
       access_cooker       alc;
       error_cooker        elc;
+      xml_cooker          xlc;
+      rpm_cooker          rlc;
 
-      msg.add_cooker(&slc);
-      msg.add_cooker(&islc);
-      msg.add_cooker(&alc);
-      msg.add_cooker(&elc);
+      msg.add_cooker(&xlc,  &shortcircuit);
+      msg.add_cooker(&slc,  &lf);
+      msg.add_cooker(&islc, &lf);
+      msg.add_cooker(&alc,  &lf);
+      msg.add_cooker(&elc,  &lf);
+      msg.add_cooker(&rlc,  &rf);
 
       if (msg.classify(hostname, arrival, service) > 0.9)
       {
@@ -273,17 +293,17 @@ int main(int argc, char *argv[])
          {
             msg.enter();
          }
-         catch (exception &e)
+         catch (std::exception &e)
          {
-            *log << "Caught an exception: " << e.what() << "\n";
+            *Log << "Caught an exception: " << e.what() << "\n";
          }
       }
       return 0;
    }
    else
    {
-      *log << "gcm_input: Can not connect to database.\n";
-      *log << "Gcm_input finished at " << Now() << ".\n";
+      *Log << "gcm_input: Can not connect to database.\n";
+      *Log << "Gcm_input finished at " << Now() << ".\n";
       return 1;
    }
 }
diff --git a/src/gcm_input/log_filter.cpp b/src/gcm_input/log_filter.cpp
new file mode 100644 (file)
index 0000000..e7b78dd
--- /dev/null
@@ -0,0 +1,78 @@
+
+/**************************************************************************
+**  (c) Copyright 2003, Andromeda Technology & Automation
+** This is free software; you can redistribute it and/or modify it under the
+** terms of the GNU General Public License, see the file COPYING.
+***************************************************************************
+** MODULE INFORMATION *
+***********************
+**      FILE NAME      : log_filter.cpp
+**      SYSTEM NAME    : 
+**      VERSION NUMBER : $Revision: 1.1 $
+**
+**  DESCRIPTION      : Implementation of the log_filter class 
+**
+**  EXPORTED OBJECTS : 
+**  LOCAL    OBJECTS : 
+**  MODULES  USED    :
+***************************************************************************
+**  ADMINISTRATIVE INFORMATION *
+********************************
+**      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
+**      CREATION DATE   : Nov 26, 2003
+**      LAST UPDATE     : Nov 26, 2003
+**      MODIFICATIONS   : 
+**************************************************************************/
+
+/*****************************
+   $Log: log_filter.cpp,v $
+   Revision 1.1  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+*****************************/
+
+/* static const char *RCSID = "$Id: log_filter.cpp,v 1.1 2003-12-04 10:38:09 arjen Exp $"; */
+
+#include "log_filter.h"
+
+extern String XML_Entities(String s);
+
+/*=========================================================================
+**  NAME           : constructXML
+**  SYNOPSIS       : int constructXML(message_buffer &in, strstream &xml)
+**  PARAMETERS     : 
+**  RETURN VALUE   : None
+**
+**  DESCRIPTION    : Create the body for a Gnucomo XML document. Each line
+**                   of input from the log file is put in a <raw> XML element.
+**
+**  VARS USED      :
+**  VARS CHANGED   :
+**  FUNCTIONS USED :
+**  SEE ALSO       :
+**  LAST MODIFIED  : Nov 26, 2003
+**=========================================================================
+*/
+
+void log_filter::construct_XML(message_buffer &in, std::strstream &xml)
+{
+   String line;
+
+   scan_email_header(in);
+   construct_header(xml);
+
+   xml << "  <gcmt:data>\n";
+   xml << "    <gcmt:log>\n";
+   while (in >> line)
+   {
+      xml << "    <gcmt:raw>" << XML_Entities(line) << "</gcmt:raw>\n";
+   }
+   xml << "    </gcmt:log>\n";
+   xml << "  </gcmt:data>\n";
+   xml << "</gcmt:message>\n";
+}
+
diff --git a/src/gcm_input/log_filter.h b/src/gcm_input/log_filter.h
new file mode 100644 (file)
index 0000000..4ad7435
--- /dev/null
@@ -0,0 +1,76 @@
+
+/**************************************************************************
+**  (c) Copyright 2003, Andromeda Technology & Automation
+** This is free software; you can redistribute it and/or modify it under the
+** terms of the GNU General Public License, see the file COPYING.
+***************************************************************************
+** MODULE INFORMATION *
+***********************
+**      FILE NAME      : log_filter.h
+**      SYSTEM NAME    : 
+**      VERSION NUMBER : $Revision: 1.1 $
+**
+**  DESCRIPTION      : Message filter for various kinds of log files. 
+**
+**  EXPORTED OBJECTS : 
+**  LOCAL    OBJECTS : 
+**  MODULES  USED    :
+***************************************************************************
+**  ADMINISTRATIVE INFORMATION *
+********************************
+**      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
+**      CREATION DATE   : Nov 26, 2003
+**      LAST UPDATE     : Nov 26, 2003
+**      MODIFICATIONS   : 
+**************************************************************************/
+
+/*****************************
+   $Log: log_filter.h,v $
+   Revision 1.1  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+*****************************/
+
+/* static const char *RCSID = "$Id: log_filter.h,v 1.1 2003-12-04 10:38:09 arjen Exp $"; */
+
+#ifndef LOG_FILTER_H
+#define LOG_FILTER_H
+
+#include "message_filter.h"
+
+/*
+///////////////////////////////////////////////////////////////////////////
+//  NAME           : log_filter
+//  BASECLASS      : message_filter
+//  MEMBERS        :
+//  OPERATORS      :
+//  METHODS        : construct_XML
+//
+//  DESCRIPTION    : Transforms a log file into an XML document by putting
+//                   each line of input into a <gcmt:raw> XML element.
+//
+//  RELATIONS      :
+//  SEE ALSO       :
+//  LAST MODIFIED  : Nov 26, 2003
+///////////////////////////////////////////////////////////////////////////
+*/
+
+class log_filter : public message_filter
+{
+
+protected:
+
+public:
+
+   log_filter(String host, UTC arriv, String service) : message_filter(host, arriv, service)
+   {
+   }
+
+   virtual void construct_XML(message_buffer &in, std::strstream &xml);
+};
+
+#endif // LOG_FILTER_H
index 1c36a27..7e727ff 100644 (file)
@@ -8,7 +8,7 @@
 ***********************
 **      FILE NAME      : message.cpp
 **      SYSTEM NAME    : Gnucomo - Gnu Computer Monitoring
-**      VERSION NUMBER : $Revision: 1.15 $
+**      VERSION NUMBER : $Revision: 1.16 $
 **
 **  DESCRIPTION      :  Implementation of the message handling classes
 **
 ********************************
 **      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
 **      CREATION DATE   : Sep 16, 2002
-**      LAST UPDATE     : Jul 24, 2003
+**      LAST UPDATE     : Nov 28, 2003
 **      MODIFICATIONS   : 
 **************************************************************************/
 
 /*****************************
    $Log: message.cpp,v $
-   Revision 1.15  2003-10-27 11:28:27  arjen
+   Revision 1.16  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+   Revision 1.15  2003/10/27 11:28:27  arjen
    Do not add another parameter_notification record is the notification
    already exists for that parameter.
 
@@ -90,7 +97,7 @@
 
 *****************************/
 
-static const char *RCSID = "$Id: message.cpp,v 1.15 2003-10-27 11:28:27 arjen Exp $";
+static const char *RCSID = "$Id: message.cpp,v 1.16 2003-12-04 10:38:09 arjen Exp $";
 
 #include <algorithm>
 #include <libxml/xpath.h>
@@ -102,7 +109,7 @@ static const char *RCSID = "$Id: message.cpp,v 1.15 2003-10-27 11:28:27 arjen Ex
 extern bool verbose;   /*  Defined in the main application */
 extern bool testmode;
 extern bool incremental;
-extern std::ostream *log;
+extern std::ostream *Log;
 
 /*   Utility functions   */
 
@@ -151,6 +158,22 @@ bool operator >> (message_buffer &b, String &s)
    return input_ok;
 }
 
+/*=========================================================================
+**  NAME           : client_message
+**  SYNOPSIS       : client_message(std::istream *in, gnucomo_database db)
+**  PARAMETERS     : 
+**  RETURN VALUE   : None
+**
+**  DESCRIPTION    : Client message constructor.
+**
+**  VARS USED      :
+**  VARS CHANGED   :
+**  FUNCTIONS USED :
+**  SEE ALSO       :
+**  LAST MODIFIED  : Nov 04, 2002
+**=========================================================================
+*/
+
 client_message::client_message(std::istream *in, gnucomo_database db)
 {
    input.from(in);
@@ -164,7 +187,6 @@ client_message::client_message(std::istream *in, gnucomo_database db)
    certainty      = 0.0;
 }
 
-static const String mail_date_re("[[:alpha:]]{3}, [ 123]?[0-9] [[:alpha:]]{3} [0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2} [+-][0-9]{4}");
 static const String unix_date_re("[[:alpha:]]{3} [[:alpha:]]{3} [ 123][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{4}");
 
 static const regex re_PGP("-----BEGIN PGP MESSAGE-----");
@@ -172,48 +194,33 @@ static const regex re_dump("^ *DUMP: Date of this level");
 static const regex re_rpm("[[:alnum:]+-]+-[0-9][[:alnum:].-]");
 
 static const regex re_uxmail_from("^From [^ \t]+[ ]+" + unix_date_re);
-static const regex re_mail_From("^From:[[:blank:]]+");
-static const regex re_mail_Date("^Date:[[:blank:]]+" + mail_date_re);
-static const regex re_mail_MsId("^Message-Id:[[:blank:]]+");
-static const regex re_email_address("[[:alnum:]_.-]+@[[:alnum:]_.-]+");
-static const regex re_email_user("[[:alnum:]_.-]+@");
 static const regex re_xml_header("xml .*\?>$");
 
 /*=========================================================================
-**  NAME           : readXMLinput
-**  SYNOPSIS       : int readXMLinput(String first_line)
+**  NAME           : extractHeader
+**  SYNOPSIS       : void extractHeader()
 **  PARAMETERS     : 
-**  RETURN VALUE   : Parse the XML input and extract the header information
+**  RETURN VALUE   : True if the mandatory header elements are available.
 **
-**  DESCRIPTION    : 
+**  DESCRIPTION    : Extract the header information from the XML DOM tree.
 **
 **  VARS USED      :
 **  VARS CHANGED   :
 **  FUNCTIONS USED :
 **  SEE ALSO       :
-**  LAST MODIFIED  : Jul 24, 2003
+**  LAST MODIFIED  : Nov 26, 2003
 **=========================================================================
 */
 
-int client_message::readXMLinput(String first_line)
+bool client_message::extractHeader()
 {
-   xmlParserCtxtPtr ctxt;
-   String           line;
    xmlNodePtr       root, item;
    xmlNsPtr         namespaces[1];
 
    xmlXPathObjectPtr res;
    xmlXPathContextPtr pathcontext;
 
-
-   ctxt = xmlCreatePushParserCtxt(NULL, NULL, first_line, ~first_line, NULL);
-   while (input >> line)
-   {
-      xmlParseChunk(ctxt, line, ~line, 0);
-   }
-   xmlParseChunk(ctxt, "", 0, 1);
-   xmlDom = ctxt->myDoc;
-   xmlFreeParserCtxt(ctxt);
+   bool header_OK = true;
 
    root = xmlDocGetRootElement(xmlDom);
    namespaces[0] = root->ns;
@@ -230,70 +237,73 @@ int client_message::readXMLinput(String first_line)
 #endif
 
    res = xmlXPathEval((const xmlChar *)"gcmt:header/gcmt:messagetype/text()", pathcontext);
-   if (res->nodesetval != NULL)
+   if (res->nodesetval != NULL && res->nodesetval->nodeTab != NULL)
    {
-#ifdef DEBUG
-      xmlDebugDumpNodeList(stdout, *res->nodesetval->nodeTab, 0);
-#endif
       item = *res->nodesetval->nodeTab;
 
       //  Select a line cooker based on the message type.
 
 #ifdef DEBUG
-      std::cout << "Looking for a line cooker for " << item->content << "\n";
+      *Log << "Looking for a line cooker for " << item->content << "\n";
 #endif
-      std::list<line_cooker *>::iterator lci = kitchen.begin();
-      pan = 0;
-      while (pan == 0 && lci != kitchen.end())
+      pan.lc = 0;
+      std::list<xform>::iterator lci = kitchen.begin();
+      while (pan.lc == 0 && lci != kitchen.end())
       {
-         pan = *lci;
-         if (pan->message_type() != (const char *)(item->content))
+         if (lci->lc->message_type() == (const char *)(item->content))
          {
-            pan = 0;
+            pan.lc = lci->lc;
          }
          lci++;
       }
-      if (pan == 0)
+      if (pan.lc == 0)
       {
-         *log << "Can not find a line cooker for message type " << item->content << "\n";
+         *Log << "Can not find a line cooker for message type " << item->content << "\n";
+         header_OK = false;
       }
    }
    else
    {
-      *log << "Message type not found in XML header.\n";
+      *Log << "Message type not found in XML header.\n";
+      header_OK = false;
    }
 
    res = xmlXPathEval((const xmlChar *)"gcmt:header/gcmt:hostname/text()", pathcontext);
-   if (res->nodesetval != NULL)
+   if (res->nodesetval != NULL && res->nodesetval->nodeTab != NULL)
    {
-#ifdef DEBUG
-      xmlDebugDumpNodeList(stdout, *res->nodesetval->nodeTab, 0);
-#endif
       item = *res->nodesetval->nodeTab;
       hostname = (const char *)item->content;
    }
    else
    {
-      *log << "Hostname not found in XML header.\n";
+      *Log <<  "Can not determine the hostname where the message came from.\n";
+      header_OK = false;
    }
 
    res = xmlXPathEval((const xmlChar *)"gcmt:header/gcmt:service/text()", pathcontext);
-   if (res->nodesetval != NULL)
+   if (res->nodesetval != NULL && res->nodesetval->nodeTab != NULL)
    {
       item = *res->nodesetval->nodeTab;
       service = (const char *)item->content;
    }
    res = xmlXPathEval((const xmlChar *)"gcmt:header/gcmt:time/text()", pathcontext);
-   if (res->nodesetval != NULL)
+   if (res->nodesetval != NULL && res->nodesetval->nodeTab != NULL)
    {
       item = *res->nodesetval->nodeTab;
       arrival = String((char *)item->content);
+      if (!arrival.proper())
+      {
+         *Log << "Arrival time is not properly stated.\n";
+         header_OK = false;
+      }
 
    }
    //xmlDebugDumpNodeList(stdout, *res->nodesetval->nodeTab, 0);
 
+   return header_OK;
 }
 
+
 /*=========================================================================
 **  NAME           : classify
 **  SYNOPSIS       : double classify(String host, date arriv_d, hour arriv_t, String serv)
@@ -306,7 +316,7 @@ int client_message::readXMLinput(String first_line)
 **  VARS CHANGED   :
 **  FUNCTIONS USED :
 **  SEE ALSO       :
-**  LAST MODIFIED  : Aug 11, 2003
+**  LAST MODIFIED  : Nov 27, 2003
 **=========================================================================
 */
 
@@ -318,6 +328,11 @@ double client_message::classify(String host, UTC arriv, String serv)
    arrival     = arriv;
    service     = serv;
 
+   const double   epsilon = 0.1;   //  Threshold for uncertainty
+   const double   P       = 0.5;   //  Probability of a wrong match
+
+   double uncertainty;
+
    /*  First, check if the message has a mail header. */
 
    if (input >> line && line == re_uxmail_from)
@@ -326,34 +341,10 @@ double client_message::classify(String host, UTC arriv, String serv)
 
       mail_header = true;
 
-      /*  Scan ahead for the hostname and date of arrival.  */
+      /*  Skip the mail header until the first empty line.  */
 
       while (input >> line && line != "")
       {
-         if (line == re_mail_From)
-         {
-            from_address = line(re_email_address);
-            from_address(re_email_user) = "";            //  Remove the user part;
-            if (from_address != "" && ~hostname < ~from_address)
-            {
-               *log << "Detected hostname " << from_address << "\n";
-               hostname = from_address;
-            }
-         }
-         if (line == re_mail_MsId)
-         {
-            from_address = line(re_email_address);
-            from_address(re_email_user) = "";            //  Remove the user part;
-            if (from_address != "" && ~hostname < ~from_address)
-            {
-               *log << "Detected hostname " << from_address << "\n";
-               hostname = from_address;
-            }
-         }
-         if (line == re_mail_Date)
-         {
-            arrival = UTC(line(regex(mail_date_re)));
-         }
       }
    }
    else
@@ -363,97 +354,59 @@ double client_message::classify(String host, UTC arriv, String serv)
 
    }
 
-   pan = 0;
-
    /*
     *  Now that we have the mail header out of the way, try to figure
     *  out what the content of the message is.
     */
 
 
-   while (input >> line && certainty < 0.9)
+   uncertainty = 1.0;
+
+   while (input >> line && uncertainty > 0.1)
    {
       if (verbose)
       {
-         *log << "  testing: " << line << "\n";
+         *Log << "  testing: " << line << "\n";
       }
 
-      if (line == re_xml_header)
+      if (line == re_PGP)
       {
-         certainty = 1.0;
-         classification = XML;
-         if (verbose)
-         {
-            *log << "XML input detected.\n";
-         }
-         readXMLinput(line);
-      }
-      else if (line == re_PGP)
-      {
-         certainty = 1.0;
+         uncertainty = 0.0;
          gpg_encrypted = true;
-         *log << "The message is PGP/GnuPG encrypted.\n";
-      }
-      else if (line == re_dump)
-      {
-          certainty = 1.0;
-          if (verbose)
-          {
-             *log << "DUMP output detected.\n";
-          }
-      }
-      else if (line == re_rpm)
-      {
-          certainty = 1.0;
-          classification = RPMLIST;
-          service = "";
-          if (verbose)
-          {
-             *log << "RPM package list detected.\n";
-          }
+         *Log << "The message is PGP/GnuPG encrypted.\n";
       }
       else
       {
          //  Scan the list of line cookers if there is anything familiar.
 
-         std::list<line_cooker *>::iterator lci = kitchen.begin();
-         pan = 0;
-         while (pan == 0 && lci != kitchen.end())
+         std::list<xform>::iterator lci = kitchen.begin();
+         uncertainty = 1.0;
+         while (lci != kitchen.end())
          {
-            pan = *lci;
-            if (!pan->check_pattern(line))
+            if (lci->lc->check_pattern(line))
             {
-               pan = 0;
+               //  We have a match; decrease the uncertainty
+
+               lci->uncertainty *= P;
+               if (uncertainty > lci->uncertainty)
+               {
+                  uncertainty = lci->uncertainty;
+                  pan = *lci;
+               }
+               if (verbose)
+               {
+                  *Log << lci->lc->message_type() << " detected with "
+                       << lci->uncertainty << " uncertainty.\n";
+               }
             }
             lci++;
          }
-         if (pan != 0)
-         {
-            certainty = 1.0;
-            classification = COOKER_OBJECT;
-            if (verbose)
-            {
-               *log << "Detected message type " << pan->message_type() << "\n";
-            }
-         }
+         classification = COOKER_OBJECT;
       }
    }
    input.rewind();
 
-   if (hostname == "")
-   {
-      *log <<  "Can not determine the hostname where the message came from.\n";
-      certainty = 0.0;
-   }
-   else if (!arrival.proper())
-   {
-      *log << "Arrival time is not known.\n";
-      certainty = 0.0;
-   }
-   else
-   {
-      certainty = 1.0;
-   }
+   certainty = 1.0 - uncertainty;
 
    return certainty;
 }
@@ -471,12 +424,20 @@ double client_message::classify(String host, UTC arriv, String serv)
 **  VARS CHANGED   :
 **  FUNCTIONS USED :
 **  SEE ALSO       :
-**  LAST MODIFIED  : Jul 24, 2003
+**  LAST MODIFIED  : Nov 28, 2003
 **=========================================================================
 */
 
+struct param_property
+{
+   String name;
+   String value;
+};
+
 void client_message::enterXML()
 {
+   //TODO : return the number of elements that are handled.
+
    xmlXPathObjectPtr res;
    xmlXPathContextPtr pathcontext;
    xmlNsPtr           namespaces[1];
@@ -484,16 +445,17 @@ void client_message::enterXML()
    /*  Try to find the host in the database */
 
    String objectid;
+   String remark;        //  For notifications
 
    objectid = database.find_host(hostname);
    if (objectid == "")
    {
-      *log << "Please define the host " << hostname << " in the database.\n";
+      *Log << "Please define the host " << hostname << " in the database.\n";
       return;
    }
    if (verbose)
    {
-      *log << "Object id for " << hostname << " is " << objectid << "\n";
+      *Log << "Object id for " << hostname << " is " << objectid << "\n";
    }
 
    pathcontext = xmlXPathNewContext(xmlDom);
@@ -528,29 +490,29 @@ void client_message::enterXML()
                String      raw("");;
                String      log_service;
 
-               if (strcmp((char *)node->name, "raw") == 0)
+               if (strcmp((char *)node->name, "raw") == 0 && node->children != NULL)
                {
                   item = node->children;
-                  if (pan == 0)
+                  if (pan.lc == 0)
                   {
-                     *log << "Can not cook this type of <raw> log element.\n";
+                     *Log << "Can not cook this type of <raw> log element.\n";
                   }
                   else
                   {
                      raw = String((const char *)item->content);
-                     if (pan->cook_this(raw, arrival))
+                     if (pan.lc->cook_this(raw, arrival))
                      {
-                        log_hostname = pan->hostname();
+                        log_hostname = pan.lc->hostname();
                         if (log_hostname == "")
                         {
                            log_hostname = hostname;
                         }
-                        log_service = pan->service();
-                        log_date    = pan->timestamp();
+                        log_service = pan.lc->service();
+                        log_date    = pan.lc->timestamp();
                      }
                      else
                      {
-                        *log << "Log line " << raw << " does not match.\n";
+                        *Log << "gcm_input WARNING: Not a valid line: " << raw << "\n";
                         raw = "";
                      }
                   }
@@ -561,7 +523,7 @@ void client_message::enterXML()
 
                   if (verbose)
                   {
-                     *log << "Analyzing cooked element.\n";
+                     *Log << "Analyzing cooked element.\n";
                   }
                   pathcontext->node = node;
 
@@ -572,7 +534,7 @@ void client_message::enterXML()
                      log_hostname = (const char *)item->content;
                      if (log_hostname != hostname(0, ~log_hostname))
                      {
-                        *log << "Hostname " << log_hostname << " does not match.\n";
+                        *Log << "Hostname " << log_hostname << " does not match.\n";
                         log_hostname = "";
                      }
                   }
@@ -600,7 +562,7 @@ void client_message::enterXML()
                   }
                   else
                   {
-                     *log << "<timestamp> missing from cooked log element.\n";
+                     *Log << "<timestamp> missing from cooked log element.\n";
                   }
 
                   res = xmlXPathEval((const xmlChar *)"raw/text()", pathcontext);
@@ -611,7 +573,7 @@ void client_message::enterXML()
                   }
                   else
                   {
-                     *log << "<raw> missing from cooked log element.\n";
+                     *Log << "<raw> missing from cooked log element.\n";
                   }
 
                }
@@ -633,396 +595,215 @@ void client_message::enterXML()
 
                   if (testmode)
                   {
-                     *log << insertion << "\n";
+                     *Log << insertion << "\n";
                   }
                   else
                   {
                      database.Query(insertion);
                   }
 
-                  if (verbose)
-                  {
-                     *log << "\n\n";
-                  }
                }
  
             }
             node = node->next;
          }
       }
-      else
+      else if (strcmp((char *)node->name, "parameters") == 0)
       {
-         *log << "Data element " << node->name << " is not supported.\n";
-      }
-   }
-   else
-   {
-      *log << "Data node not found.\n";
-   }
-}
+         //  Each child contains a parameter entry, with at least one property
 
-/*=========================================================================
-**  NAME           : enter
-**  SYNOPSIS       : int enter()
-**  PARAMETERS     : 
-**  RETURN VALUE   : The number of lines successfully parsed from the input
-**
-**  DESCRIPTION    : 
-**
-**  VARS USED      :
-**  VARS CHANGED   :
-**  FUNCTIONS USED :
-**  SEE ALSO       :
-**  LAST MODIFIED  : Jul 24, 2003
-**=========================================================================
-*/
-
-int client_message::enter()
-{
-   if (classification == XML)
-   {
-      enterXML();
-      return 1;
-   }
-
-   long   nr_lines = 0;
-   String line;
-   String qry;
-
-   String change_notification("");
-   String create_notification("");
-   bool initial_entry = false;
-
-   std::list<String>  packages;
-
-
-   /*  Double-check the classification of the message */
-
-   if (classification == UNKNOWN || certainty < 0.9 || gpg_encrypted)
-   {
-      return 0;
-   }
+         String   qry;
+         String   insertion;
+         String   change_notification("");
+         String   create_notification("");
+         String   remove_notification("");
+         bool     initial_entry = false;
+         String   param_class((const char *)xmlGetProp(node, (const xmlChar *)"class"));
 
-   if (mail_header)
-   {
-      //  Skip the mail header.
+         pathcontext->node = node;
 
-      while (input >> line && line != "");
-   }
+         //  If we don;t have any parameters of this class, this will be
+         //  an initial entry.
 
-   /*  Try to find the host in the database */
-
-   String objectid;
-
-   objectid = database.find_host(hostname);
-   if (objectid == "")
-   {
-      *log << "Please define the host " << hostname << " in the database.\n";
-      return 0;
-   }
-   if (verbose)
-   {
-      *log << "Object id for " << hostname << " is " << objectid << "\n";
-   }
-
-   if (classification == RPMLIST)
-   {
-
-      int n_packages;
-
-      /*   Read all packages, so we will know which ones are  */
-      /*   missing at the end.                                */
-
-      qry = "select name from parameter where objectid='";
-      qry += objectid + "' and class='package'";
-      n_packages = database.Query(qry);
-      initial_entry = n_packages == 0;
-
-#ifdef DEBUG
-      *log << n_packages << " packages in database.\n";
-#endif
-      for (int t = 0; t < n_packages; t++)
-      {
-         packages.push_back(database.Field(t, "name"));
-      }
-#ifdef DEBUG
-      *log << "Package list built: " << packages.size() << ".\n";
-#endif
-   }
-
-   /*  Scan the input line by line, entring records into the database */
-
-   String rest;   //  Rest of the line to be parsed
-   regex  re_any(".*");
-
-   while (input >> line)
-   {
-      if (verbose)
-      {
-         *log << line << "\n";
-      }
+         qry = "select name from parameter where objectid='";
+         qry += objectid + "' and class='" + param_class + "'";
+         initial_entry = database.Query(qry) == 0;
 
+         node = node->children;
+         while (node != NULL)
+         {
+            if (node->type == XML_ELEMENT_NODE &&
+                strcmp((char *)node->name, "parameter") == 0)
+            {
+               String param_name((const char *)xmlGetProp(node, (const xmlChar *)"name"));
 
-      /*  Check each line if it contains valid information */
+               std::list<param_property>  properties;
+               param_property             prop;
+               xmlNodePtr                 item;
 
-      const regex *check;
+               String                     paramid;
 
-      switch (classification)
-      {
-      case RPMLIST:
-            check = &re_rpm;
-            break;
-      case COOKER_OBJECT:
-            check = &re_any;
-            break;
-      }
 
-      if (line == *check)
-      {
-         date   log_date;
-         hour   log_time;
-         int    i;
+               //  Collect the parameter's properties.
 
-         String insertion("insert into log (objectid, servicecode,"
-                           " object_timestamp, timestamp, rawdata, processed) values (");
-         String datestring;
-
-         switch (classification)
-         {
-         case COOKER_OBJECT:
-#ifdef DEBUG
-            std::cerr << "\ncooker check: " << pan->check_pattern(line) << "\n";
-#endif
-            if (pan->cook_this(line, arrival))
-            {
-               if (pan->hostname() == hostname(0,~pan->hostname()))
+               item = node->children;
+               while (item != NULL)
                {
-
-#ifdef DEBUG
-                  std::cerr << " Information from cooker:\n";
-                  std::cerr << "     timestamp = " << pan->timestamp() << "\n";
-                  std::cerr << "     hostname  = " << pan->hostname() << "\n";
-                  std::cerr << "     service   = " << pan->service() << "\n";
-#endif
-                  /*   Insert a new record into the log table   */
-
-                  insertion += "'" + objectid + "',";
-                  insertion += "'" + pan->service() + "',";
-                  insertion += "'" + pan->timestamp().format("%Y-%m-%d %T") + "',";
-                  insertion += "'" + arrival.format("%Y-%m-%d %T") + "',";
-                  insertion += "'" + SQL_Escape(line) + "',FALSE";
-                  insertion += ")";
-
-                  if (testmode)
+                  if (item->type == XML_ELEMENT_NODE &&
+                      strcmp((char *)item->name, "property") == 0)
                   {
-                     *log << insertion << "\n";
-                  }
-                  else
-                  {
-                     database.Query(insertion);
+                     prop.name = (const char *)xmlGetProp(item, (const xmlChar *)"name");
+                     if (item->children != NULL)
+                     {
+                        prop.value = (const char *)item->children->content;
+                        properties.push_back(prop);
+                     }
+                     else
+                     {
+                        *Log << "WARNING: Property " << prop.name << " has no value.\n";
+                     }
                   }
 
-                  if (verbose)
-                  {
-                     *log << "\n\n";
-                  }
+                  //  TODO: Hanlde description element
 
-                  nr_lines++;
+                  item = item->next;
                }
-               else
-               {
-                  *log << "   Hostname " << pan->hostname() << " does not match.\n";
-               }
-            }
-            else
-            {
-               *log << "gcm_input WARNING: Not a valid line: " << line << "\n";
-            }
-            break;
-
 
-         case RPMLIST:
-            //  Scan a list of packages and versions from "rpm -a".
-            //  A similar listing can be created on IRIX 6.5 by using the
-            //  command "showprods -3 -n|awk '{printf "%s-%s\n",$2,$3}'|grep -v '^[-=]' \
-            //            |grep -v Version-Description".
-            //
-            //  We have to separate the package name and the version.
-            //  The separation is marked by a '-', followed by a digit.
-            //  However, there may be other sequences of '-'digit in the package name,
-            //  do we have to scan ahead until there is at most one such sequence
-            //  left in the version string. The '-'digit seqeunce inside the
-            //  version usually separates the version and the release number.
+               //  Check the parameter in the database.
 
-            int  version_start, next_version_start;
+               std::list<param_property>::iterator pi = properties.begin();
 
-            i = line.index('-');
-            version_start = i;
-            next_version_start = i;
+               qry = "select paramid from parameter where objectid='";
+               qry += objectid + "' and class='";
+               qry += param_class + "' and name='";
+               qry += param_name + "'";
 
-            while (i < ~line - 1)
-            {
-               while (i < ~line - 1 && !(line[i] == '-' && isdigit(line[i + 1])))
-               {
-                  i++;
-               }
-               if (i < ~line - 1)
+               if (database.Query(qry) == 1)
                {
-                  version_start = next_version_start;
-                  next_version_start = i;
-               }
-               i++;
-            }
-            
-            if (!isdigit(line[version_start + 1]))
-            {
-               version_start = next_version_start;
-            }
-            String package(line(0,version_start));
-            String version(line(version_start + 1, ~line));
-            String paramid;
-            String remark;
-            String insert_h;
+                  //  The parameter exists in the database; check all properties.
 
-            if (verbose)
-            {
-               *log << "Package is " << package;
-               *log << ", version is " << version << "\n";
-            }
+                  bool  param_changed = false;
 
-            //  Construct a qry to check the package's existance
+                  paramid = database.Field(0, "paramid");
+                  while (pi != properties.end())
+                  {
+                     qry = "select value from property where paramid='";
+                     qry += paramid + "' and name='";
+                     qry += pi->name + "'";
+                     if (database.Query(qry) == 0)
+                     {
+                        *Log << "Property " << pi->name << " of "
+                             << param_name << " does not exist.\n";
+                     }
+                     else if (database.Field(0, "value") != pi->value)
+                     {
+                        *Log << "Property " << pi->name << " of "
+                             << param_name << " is different.\n";
 
-            qry = "select paramid from parameter where objectid='";
-            qry += objectid + "' and class='package' and name='";
-            qry += package + "'";
+                        insertion = "update property set value='";
+                        insertion += pi->value + "' where paramid='";
+                        insertion += paramid + "' and name='";
+                        insertion += pi->name + "'";
 
-            if (database.Query(qry) == 1)
-            {
-               std::list<String>::iterator  lp;
+                        database.Query(insertion);
 
-               lp = find(packages.begin(), packages.end(), package);
-               if (lp != packages.end())
-               {
-                  packages.erase(lp);
-               }
-               else
-               {
-                  *log <<  "Could NOT find " << package << " in list.\n";
-               }
+                        insertion = "insert into history (paramid, modified,";
+                        insertion += " change_nature, changed_property, new_value)";
+                        insertion += " values ('";
+                        insertion += paramid + "', '" + arrival.format("%Y-%m-%d %T")
+                                             + "', 'MODIFIED', '";
+                        insertion += pi->name + "', '";
+                        insertion += pi->value + "')";
 
-               paramid = database.Field(0, "paramid");
-               qry = "select value from property where paramid='";
-               qry += paramid + "' and name='version'";
-               if (database.Query(qry) == 0)
-               {
-                  *log << "Database corruption: Package " << package;
-                  *log << " does not have a 'version' property.\n";
-               }
-               else if (database.Field(0, "value") != version)
-               {
-                  if (verbose)
-                  {
-                     *log << "  Parameter " << package << " has different version\n";
-                  }
-                  insertion = "update property set value='";
-                  insertion += version + "' where paramid='";
-                  insertion += paramid + "' and name='version'";
+                        database.Query(insertion);
 
-                  insert_h = "insert into history (paramid, modified, change_nature, changed_property, new_value)";
-                  insert_h += " values ('";
-                  insert_h += paramid + "', '" + arrival.format("%Y-%m-%d %T") + "', 'MODIFIED', 'version', '";
-                  insert_h += version + "')";
+                        param_changed = true;
 
-                  database.Query(insertion);
-                  database.Query(insert_h);
 
-                  if (change_notification == "")
-                  {
-                     remark = "Gnucomo detected a different version for package parameter(s) ";
-                     change_notification = database.new_notification(objectid, "property modified", remark);
-                     change_notification = database.new_notification(objectid, "property modified", remark);
+                     }
+                     pi++;
                   }
 
-                  if (change_notification != "")
+                  if (param_changed)
                   {
-                     qry = "select * from parameter_notification where notificationid='";
-                     qry += change_notification + "' and paramid='";
-                     qry += paramid + "'";
+                     if (change_notification == "")
+                     {
+                        remark = "Gnucomo detected a different version for package parameter(s) ";
+                        change_notification = database.new_notification(objectid,
+                                                           "property modified", remark);
+                     }
 
-                     if (database.Query(qry) == 0)
+                     if (change_notification != "")
                      {
-                        insertion = "insert into parameter_notification (notificationid, paramid) values ('";
-                        insertion += change_notification + "', '";
-                        insertion += paramid + "')";
+                        qry = "select * from parameter_notification where notificationid='";
+                        qry += change_notification + "' and paramid='";
+                        qry += paramid + "'";
 
-                        database.Query(insertion);
+                        if (database.Query(qry) == 0)
+                        {
+                           insertion = "insert into parameter_notification";
+                           insertion += " (notificationid, paramid) values ('";
+                           insertion += change_notification + "', '";
+                           insertion += paramid + "')";
+
+                           database.Query(insertion);
+                        }
+                     }
+                     else
+                     {
+                        *Log << "gcm_input ERROR: Cannot create 'property modified' notification.\n";
                      }
-                  }
-                  else
-                  {
-                     *log << "gcm_input ERROR: Cannot create 'property modified' notification.\n";
                   }
                }
                else
                {
-                  if (verbose)
-                  {
-                     *log << "   Parameter " << package << " has not changed.\n";
-                  }
-               }
-            }
-            else
-            {
+                  //   The parameter does not exist; create anew.
 
-               if (verbose)
-               {
-                  *log << "  Parameter " << package << " does not exist.\n";
-               }
-               //  Create a new package parameter, including version property and history record
+                  // TODO: Insert description
 
-               insertion = "insert into parameter (objectid, name, class, description) values ('";
-               insertion += objectid + "', '" + package + "', 'package', 'RPM package " + package + "')";
-               if (testmode)
-               {
-                  paramid = "0";
-                  *log << insertion << "\n";
-               }
-               else
-               {
+                  insertion = "insert into parameter (objectid, name, class, description) values ('";
+                  insertion += objectid + "', '" + param_name + "', '" + param_class + "', '')";
                   database.Query(insertion);
+
                   qry = "select paramid from parameter where objectid='";
-                  qry += objectid + "' and class='package' and name='";
-                  qry += package + "'";
+                  qry += objectid + "' and class='";
+                  qry += param_class + "' and name='";
+                  qry += param_name + "'";
                   database.Query(qry);
                   paramid = database.Field(0, "paramid");
-               }
 
-               insertion = "insert into property (paramid, name, value, type) values ('";
-               insertion += paramid + "', 'version', '";
-               insertion += version + "', 'STATIC')";
-               insert_h = "insert into history (paramid, modified, change_nature, changed_property, new_value)";
-               insert_h += " values ('";
-               insert_h += paramid + "', '" + arrival.format("%Y-%m-%d %T") + "', 'CREATED', 'version', '";
-               insert_h += version + "')";
+                  while (pi != properties.end())
+                  {
+                     insertion = "insert into property (paramid, name, value, type) values ('";
+                     insertion += paramid + "', '";
+                     insertion += pi->name + "', '";
+                     insertion += pi->value + "', 'STATIC')";
+                     database.Query(insertion);
+
+                     insertion = "insert into history (paramid, modified,";
+                     insertion += " change_nature, changed_property, new_value)";
+                     insertion += " values ('";
+                     insertion += paramid + "', '" + arrival.format("%Y-%m-%d %T")
+                                          + "', 'CREATED', '";
+                     insertion += pi->name + "', '";
+                     insertion += pi->value + "')";
+                     database.Query(insertion);
+
+                     pi++;
+                  }
 
-               if (testmode)
-               {
-                  *log << insertion << "\n" << insert_h << "\n";
-               }
-               else
-               {
-                  database.Query(insertion);
-                  database.Query(insert_h);
                   if (!initial_entry)
                   {
                      if (create_notification == "")
                      {
                         remark = "Gnucomo detected new parameter(s) of class package";
-                        create_notification = database.new_notification(objectid, "parameter created", remark);
+                        create_notification = database.new_notification(objectid,
+                                                         "parameter created", remark);
                      }
                      if (create_notification != "")
                      {
-                        insertion = "insert into parameter_notification (notificationid, paramid) values ('";
+                        insertion = "insert into parameter_notification";
+                        insertion += " (notificationid, paramid) values ('";
                         insertion += create_notification + "', '";
                         insertion += paramid + "')";
 
@@ -1030,98 +811,138 @@ int client_message::enter()
                      }
                      else
                      {
-                        *log << "gcm_input ERROR: Cannot create 'parameter created' notification.\n";
+                        *Log << "gcm_input ERROR: Cannot create 'parameter created' notification.\n";
                      }
                   }
                }
             }
 
-            if (verbose)
-            {
-               *log << "\n";
-            }
-
-            nr_lines++;
-            break;
-
+            node = node->next;
          }
-      }
-      else
-      {
-         *log << "gcm_input WARNING: Not a valid line: " << line << "\n";
-      }
-   }
 
-   if (classification == RPMLIST && !incremental)
-   {
-      std::list<String>::iterator  lp;
-      String     remove_notification("");
+         if (!incremental)
+         {
+            //  Check if any parameters in this class have disappeared.
 
-      /*
-       *     If there are any packages left in the list, they seem to have
-       *     disappeared from the system.
-       */
+            qry = "select name, paramid from parameter where objectid='";
+            qry += objectid + "' and class='" + param_class + "'";
 
-      for (lp = packages.begin(); lp != packages.end(); lp++)
-      {
-         String paramid;
-         String remark;
-         String insert;
+            int          nr_parameters = database.Query(qry);
+            pqxx::Result parameter_set = database.Result();
 
-         //  Construct a qry to check the package's existance
+            for (int i = 0; i < nr_parameters; i++)
+            {
+               String XPath;
+               String param_name, paramid;
 
-         qry = "select paramid from parameter where objectid='";
-         qry += objectid + "' and class='package' and name='";
-         qry += *lp + "'";
+               param_name = database.Field(parameter_set, i, "name");
+               XPath = "gcmt:parameter[@name='" + param_name + "']";
 
-         if (database.Query(qry) == 1)
-         {
-            paramid = database.Field(0, "paramid");
-            qry ="select change_nature from history where paramid='";
-            qry += paramid + "' order by modified desc";
-            if (database.Query(qry) <= 0)
-            {
-               *log << "Database ERROR: no history record for parameter " << *lp << ".\n";
-            }
-            else if (database.Field(0, "change_nature") != "REMOVED")
-            {
-               if (verbose)
+               res = xmlXPathEval((const xmlChar *)(const char *)XPath, pathcontext);
+               if (res->nodesetval->nodeTab == NULL)
                {
-                 *log << "Removing parameter " << *lp << ".\n";
-               }
+                  // The parameter is in the database but not in the report
 
-               insert = "insert into history (paramid, modified, change_nature)";
-               insert += " values ('";
-               insert += paramid + "', '" + arrival.format("%Y-%m-%d %T") + "', 'REMOVED')";
+                  paramid = database.Field(parameter_set, i, "paramid");
+                  qry ="select change_nature from history where paramid='";
+                  qry += paramid + "' order by modified desc";
+                  if (database.Query(qry) <= 0)
+                  {
+                     *Log << "Database ERROR: no history record for parameter "
+                          << param_name << ".\n";
+                  }
+                  else if (database.Field(0, "change_nature") != "REMOVED")
+                  {
+                     if (verbose)
+                     {
+                       *Log << "Removing parameter " << param_name << ".\n";
+                     }
 
-               database.Query(insert);
+                     insertion = "insert into history (paramid, modified, change_nature)";
+                     insertion += " values ('";
+                     insertion += paramid + "', '" + arrival.format("%Y-%m-%d %T")
+                                          + "', 'REMOVED')";
 
-               if (remove_notification == "")
-               {
-                  remark = "Gnucomo detected that package(s) have disappeared ";
-                  remove_notification = database.new_notification(objectid, "parameter removed", remark);
-               }
+                     database.Query(insertion);
 
-               if (remove_notification != "")
-               {
-                  insert = "insert into parameter_notification (notificationid, paramid) values ('";
-                  insert += remove_notification + "', '";
-                  insert += paramid + "')";
+                     if (remove_notification == "")
+                     {
+                        remark = "Gnucomo detected that package(s) have disappeared ";
+                        remove_notification = database.new_notification(objectid,
+                                                         "parameter removed", remark);
+                     }
 
-                  database.Query(insert);
-               }
-               else
-               {
-                  *log << "gcm_input ERROR: Cannot create 'parameter removed' notification.\n";
+                     if (remove_notification != "")
+                     {
+                        insertion = "insert into parameter_notification";
+                        insertion += " (notificationid, paramid) values ('";
+                        insertion += remove_notification + "', '";
+                        insertion += paramid + "')";
+
+                        database.Query(insertion);
+                     }
+                     else
+                     {
+                        *Log << "gcm_input ERROR: Cannot create 'parameter removed' notification.\n";
+                     }
+                  }
                }
             }
          }
       }
+      else
+      {
+         *Log << "Data element " << node->name << " is not supported.\n";
+      }
    }
+   else
+   {
+      *Log << "Data node not found.\n";
+   }
+}
 
-   if (verbose)
+/*=========================================================================
+**  NAME           : enter
+**  SYNOPSIS       : int enter()
+**  PARAMETERS     : 
+**  RETURN VALUE   : The number of lines successfully parsed from the input
+**
+**  DESCRIPTION    : 
+**
+**  VARS USED      :
+**  VARS CHANGED   :
+**  FUNCTIONS USED :
+**  SEE ALSO       :
+**  LAST MODIFIED  : Nov 26, 2003
+**=========================================================================
+*/
+
+int client_message::enter()
+{
+   pan.mf->set_message_type(pan.lc->message_type());
+
+   pan.mf->construct_XML(input, xmlBuffer);
+
+#ifdef DEBUG
+   *Log << "Constructed XML document:\n\n";
+   *Log << xmlBuffer.str();
+   *Log << "\n";
+#endif
+
+   xmlDom = xmlParseMemory(xmlBuffer.str(), xmlBuffer.pcount());
+
+   if (xmlDom)
+   {
+      if (extractHeader())
+      {
+         enterXML();
+      }
+   }
+   else
    {
-      *log << nr_lines << " lines parsed from the log file.\n";
+      *Log << "XML parser FAILED.\n";
    }
-   return nr_lines;
+
+   return 0;
+
 }
index eb32449..723aabd 100644 (file)
@@ -8,7 +8,7 @@
 ***********************
 **      FILE NAME      : message.h
 **      SYSTEM NAME    : 
-**      VERSION NUMBER : $Revision: 1.7 $
+**      VERSION NUMBER : $Revision: 1.8 $
 **
 **  DESCRIPTION      :  Classes to for handling client messages
 **
 
 /*****************************
    $Log: message.h,v $
-   Revision 1.7  2003-08-11 16:56:16  arjen
+   Revision 1.8  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+   Revision 1.7  2003/08/11 16:56:16  arjen
    Different kinds of log files are parsed by a collection of objects
    of different classes, derived from the base class line_cooker
    Depending on the message content or the message_type element in
@@ -61,7 +68,7 @@
 
 *****************************/
 
-/* static const char *RCSID = "$Id: message.h,v 1.7 2003-08-11 16:56:16 arjen Exp $"; */
+/* static const char *RCSID = "$Id: message.h,v 1.8 2003-12-04 10:38:09 arjen Exp $"; */
 
 #include <iostream>
 #include <list>
 
 #include <libxml/parser.h>
 
+#include "message_filter.h"
 #include "line_cooker.h"
 #include "database.h"
 
-/*
-///////////////////////////////////////////////////////////////////////////
-//  NAME           : message_buffer
-//  BASECLASS      : 
-//  MEMBERS        :
-//  OPERATORS      :
-//  METHODS        : rewind()
-//
-//  DESCRIPTION    : 
-//
-//  RELATIONS      :
-//  SEE ALSO       :
-//  LAST MODIFIED  : Nov 04, 2002
-///////////////////////////////////////////////////////////////////////////
-*/
+//  Associates line cookers and message filters
 
-class message_buffer
+struct xform
 {
-   std::istream       *input;
-   std::list<String>  buffer;
-
-   std::list<String>::iterator   next_line;
-
-public:
+   message_filter    *mf;
+   line_cooker       *lc;
+   double            uncertainty;
 
-   message_buffer()
+   xform()
    {
-      input = 0;
-      next_line = buffer.begin();
+      mf = NULL;
+      lc = NULL;
+      uncertainty = 1.0;
    }
 
-   message_buffer(std::istream *in)
+   xform(message_filter *m, line_cooker *l)
    {
-      input = in;
-      next_line = buffer.begin();
-   }
-
-   void from(std::istream *in)
-   {
-      input = in;
-   }
-
-   friend bool operator >> (message_buffer &, String &);
-
-   void rewind()
-   {
-      next_line = buffer.begin();
-   }
-
-   void operator ++()
-   {
-      if (next_line != buffer.end())
-      {
-         next_line++;
-      }
-   }
-
-   void operator --()
-   {
-      if (next_line != buffer.begin())
-      {
-         next_line--;
-      }
+      mf = m;
+      lc = l;
+      uncertainty = 1.0;
    }
 };
 
@@ -162,13 +127,14 @@ class client_message
    UTC        arrival;       //  When we got the message.
    String     service;       //  Service that created the message
 
-   std::list<line_cooker *>  kitchen;   //  The collection of line cookers
-   line_cooker           *   pan;       //  The one we cook this log with
+   std::list<xform>  kitchen;   //  The collection of message filters and line cookers
+   xform             pan;       //  The one we cook this log with
 
    bool       mail_header;   //  Does the message contain a mail header ?
    bool       gpg_encrypted; //  Is the message encrypted ?
 
-   xmlDocPtr  xmlDom;
+   std::strstream  xmlBuffer;
+   xmlDocPtr       xmlDom;
 
    double     certainty;     //  How certain are we about the message
    enum
@@ -180,16 +146,18 @@ class client_message
    message_buffer    input;
    gnucomo_database  database;
 
-   int     readXMLinput(String first_line);
    void    enterXML();
+   bool    extractHeader();
 
 public:
 
    client_message(std::istream *in, gnucomo_database db);
 
-   void   add_cooker(line_cooker *lc)
+   void   add_cooker(line_cooker *lc, message_filter *mf)
    {
-      kitchen.push_back(lc);
+      xform x(mf, lc);
+
+      kitchen.push_back(x);
    }
 
    double classify(String host, UTC arrival = Now(), String serv = "");
diff --git a/src/gcm_input/message_buffer.h b/src/gcm_input/message_buffer.h
new file mode 100644 (file)
index 0000000..2a495cd
--- /dev/null
@@ -0,0 +1,111 @@
+
+/**************************************************************************
+**  (c) Copyright 2002, Andromeda Technology & Automation
+** This is free software; you can redistribute it and/or modify it under the
+** terms of the GNU General Public License, see the file COPYING.
+***************************************************************************
+** MODULE INFORMATION *
+***********************
+**      FILE NAME      : message_buffer.h
+**      SYSTEM NAME    : 
+**      VERSION NUMBER : $Revision: 1.1 $
+**
+**  DESCRIPTION      :  Classes to for buffering the input stream
+**
+**  EXPORTED OBJECTS : 
+**  LOCAL    OBJECTS : 
+**  MODULES  USED    :
+***************************************************************************
+**  ADMINISTRATIVE INFORMATION *
+********************************
+**      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
+**      CREATION DATE   : Sep 16, 2002
+**      LAST UPDATE     : Aug 06, 2003
+**      MODIFICATIONS   : 
+**************************************************************************/
+
+/*****************************
+   $Log: message_buffer.h,v $
+   Revision 1.1  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+*****************************/
+
+/* static const char *RCSID = "$Id: message_buffer.h,v 1.1 2003-12-04 10:38:09 arjen Exp $"; */
+
+#include <iostream>
+#include <list>
+#include <AXE/String.h>
+
+
+/*
+///////////////////////////////////////////////////////////////////////////
+//  NAME           : message_buffer
+//  BASECLASS      : 
+//  MEMBERS        :
+//  OPERATORS      :
+//  METHODS        : from()
+//                   rewind()
+//
+//  DESCRIPTION    : 
+//
+//  RELATIONS      :
+//  SEE ALSO       :
+//  LAST MODIFIED  : Nov 04, 2002
+///////////////////////////////////////////////////////////////////////////
+*/
+
+class message_buffer
+{
+   std::istream       *input;
+   std::list<String>  buffer;
+
+   std::list<String>::iterator   next_line;
+
+public:
+
+   message_buffer()
+   {
+      input = 0;
+      next_line = buffer.begin();
+   }
+
+   message_buffer(std::istream *in)
+   {
+      input = in;
+      next_line = buffer.begin();
+   }
+
+   void from(std::istream *in)
+   {
+      input = in;
+   }
+
+   friend bool operator >> (message_buffer &, String &);
+
+   void rewind()
+   {
+      next_line = buffer.begin();
+   }
+
+   void operator ++()
+   {
+      if (next_line != buffer.end())
+      {
+         next_line++;
+      }
+   }
+
+   void operator --()
+   {
+      if (next_line != buffer.begin())
+      {
+         next_line--;
+      }
+   }
+};
+
diff --git a/src/gcm_input/message_filter.cpp b/src/gcm_input/message_filter.cpp
new file mode 100644 (file)
index 0000000..02a5978
--- /dev/null
@@ -0,0 +1,174 @@
+
+/**************************************************************************
+**  (c) Copyright 2003, Andromeda Technology & Automation
+** This is free software; you can redistribute it and/or modify it under the
+** terms of the GNU General Public License, see the file COPYING.
+***************************************************************************
+** MODULE INFORMATION *
+***********************
+**      FILE NAME      : message_filter.cpp
+**      SYSTEM NAME    : 
+**      VERSION NUMBER : $Revision: 1.1 $
+**
+**  DESCRIPTION      :  
+**
+**  EXPORTED OBJECTS : 
+**  LOCAL    OBJECTS : 
+**  MODULES  USED    :
+***************************************************************************
+**  ADMINISTRATIVE INFORMATION *
+********************************
+**      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
+**      CREATION DATE   : Nov 26, 2003
+**      LAST UPDATE     : Nov 26, 2003
+**      MODIFICATIONS   : 
+**************************************************************************/
+
+/*****************************
+   $Log: message_filter.cpp,v $
+   Revision 1.1  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+*****************************/
+
+/* static const char *RCSID = "$Id: message_filter.cpp,v 1.1 2003-12-04 10:38:09 arjen Exp $"; */
+
+#include "message_filter.h"
+
+extern std::ostream *Log;
+
+/*=========================================================================
+**  NAME           : constructXML
+**  SYNOPSIS       : int constructXML(message_buffer &in, std::strstream &xml)
+**  PARAMETERS     : 
+**  RETURN VALUE   : 
+**
+**  DESCRIPTION    : Copy the input stream into the internal XML buffer
+**                   The input is already in XML format, so no real transformation
+**                   is needed.
+**
+**  VARS USED      :
+**  VARS CHANGED   :
+**  FUNCTIONS USED :
+**  SEE ALSO       :
+**  LAST MODIFIED  : Nov 26, 2003
+**=========================================================================
+*/
+
+void message_filter::construct_XML(message_buffer &in, std::strstream &xml)
+{
+   String line;
+
+   while (in >> line)
+   {
+      xml << line << "\n";
+   }
+}
+
+static const String mail_date_re("[[:alpha:]]{3}, [ 123]?[0-9] [[:alpha:]]{3} [0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2} [+-][0-9]{4}");
+static const String unix_date_re("[[:alpha:]]{3} [[:alpha:]]{3} [ 123][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{4}");
+static const regex re_uxmail_from("^From [^ \t]+[ ]+" + unix_date_re);
+static const regex re_mail_From("^From:[[:blank:]]+");
+static const regex re_mail_Date("^Date:[[:blank:]]+" + mail_date_re);
+static const regex re_mail_MsId("^Message-Id:[[:blank:]]+");
+static const regex re_email_address("[[:alnum:]_.-]+@[[:alnum:]_.-]+");
+static const regex re_email_user("[[:alnum:]_.-]+@");
+
+/*=========================================================================
+**  NAME           : scan_email_header
+**  SYNOPSIS       : void scan_email_header(message_buffer &in)
+**  PARAMETERS     : 
+**  RETURN VALUE   : 
+**
+**  DESCRIPTION    : 
+**
+**  VARS USED      :
+**  VARS CHANGED   :
+**  FUNCTIONS USED :
+**  SEE ALSO       :
+**  LAST MODIFIED  : Nov 26, 2003
+**=========================================================================
+*/
+
+void message_filter::scan_email_header(message_buffer &in)
+{
+   String line;
+
+   /*  First, check if the message has a mail header. */
+
+   if (in >> line && line == re_uxmail_from)
+   {
+      String    from_address;
+
+      /*  Scan ahead for the hostname and date of arrival.  */
+
+      while (in >> line && line != "")
+      {
+
+         if (line == re_mail_From)
+         {
+            from_address = line(re_email_address);
+            from_address(re_email_user) = "";            //  Remove the user part;
+            if (from_address != "" && ~hn < ~from_address)
+            {
+               *Log << "Detected hostname " << from_address << "\n";
+               hn = from_address;
+            }
+         }
+         if (line == re_mail_MsId)
+         {
+            from_address = line(re_email_address);
+            from_address(re_email_user) = "";            //  Remove the user part;
+            if (from_address != "" && ~hn < ~from_address)
+            {
+               *Log << "Detected hostname " << from_address << "\n";
+               hn = from_address;
+            }
+         }
+         if (line == re_mail_Date)
+         {
+            ts = UTC(line(regex(mail_date_re)));
+         }
+      }
+   }
+   else
+   {
+      //  Push the first line back, we need to read it again.
+      --in;
+
+   }
+
+}
+
+/*=========================================================================
+**  NAME           : construct_header
+**  SYNOPSIS       : void construct_header(std::strstream &xml)
+**  PARAMETERS     : 
+**  RETURN VALUE   : 
+**
+**  DESCRIPTION    : Create the header for a Gnucomo XML document.
+**
+**  VARS USED      :
+**  VARS CHANGED   :
+**  FUNCTIONS USED :
+**  SEE ALSO       :
+**  LAST MODIFIED  : Nov 26, 2003
+**=========================================================================
+*/
+
+void message_filter::construct_header(std::strstream &xml)
+{
+   //xml << "<?xml version='1.0' encoding='utf-8'?>\n";
+   xml << "<?xml version='1.0' encoding='ISO-8859-1'?>\n";
+   xml << "<gcmt:message xmlns:gcmt='http://gnucomo.org/transport/'>\n";
+   xml << "  <gcmt:header>\n";
+   xml << "    <gcmt:messagetype>" << mt << "</gcmt:messagetype>\n";
+   xml << "    <gcmt:hostname>" << hn << "</gcmt:hostname>\n";
+   xml << "    <gcmt:service>" << srv << "</gcmt:service>\n";
+   xml << "    <gcmt:time>" << ts << "</gcmt:time>\n";
+   xml << "  </gcmt:header>\n";
+}
diff --git a/src/gcm_input/message_filter.h b/src/gcm_input/message_filter.h
new file mode 100644 (file)
index 0000000..65c4090
--- /dev/null
@@ -0,0 +1,98 @@
+
+/**************************************************************************
+**  (c) Copyright 2003, Andromeda Technology & Automation
+** This is free software; you can redistribute it and/or modify it under the
+** terms of the GNU General Public License, see the file COPYING.
+***************************************************************************
+** MODULE INFORMATION *
+***********************
+**      FILE NAME      : message_filter.h
+**      SYSTEM NAME    : 
+**      VERSION NUMBER : $Revision: 1.1 $
+**
+**  DESCRIPTION      :  
+**
+**  EXPORTED OBJECTS : 
+**  LOCAL    OBJECTS : 
+**  MODULES  USED    :
+***************************************************************************
+**  ADMINISTRATIVE INFORMATION *
+********************************
+**      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
+**      CREATION DATE   : Nov 26, 2003
+**      LAST UPDATE     : Nov 26, 2003
+**      MODIFICATIONS   : 
+**************************************************************************/
+
+/*****************************
+   $Log: message_filter.h,v $
+   Revision 1.1  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+*****************************/
+
+/* static const char *RCSID = "$Id: message_filter.h,v 1.1 2003-12-04 10:38:09 arjen Exp $"; */
+
+#ifndef MESSAGE_FILTER_H
+#define MESSAGE_FILTER_H
+
+#include <strstream>
+
+#include <AXE/String.h>
+#include <AXE/date.h>
+
+#include "message_buffer.h"
+
+/*
+///////////////////////////////////////////////////////////////////////////
+//  NAME           : message_filter
+//  BASECLASS      : 
+//  MEMBERS        :
+//  OPERATORS      :
+//  METHODS        : contruct_XML
+//                   set_message_type
+//
+//  DESCRIPTION    : Transform any kind of input into an XML document
+//                   for Gnucomo
+//
+//  RELATIONS      :
+//  SEE ALSO       :
+//  LAST MODIFIED  : Nov 26, 2003
+///////////////////////////////////////////////////////////////////////////
+*/
+
+class message_filter
+{
+
+protected:
+
+   UTC    ts;     //    the timestamp.
+   String hn;     //    the hostname
+   String srv;    //    the service
+   String mt;     //    the message type
+
+   void scan_email_header(message_buffer &in);
+   void construct_header(std::strstream &xml);
+
+public:
+
+   message_filter(String host, UTC arriv, String service)
+   {
+      hn  = host;
+      ts  = arriv;
+      srv = service;
+   }
+
+   virtual void construct_XML(message_buffer &in, std::strstream &xml);
+
+   void set_message_type(String msgt)
+   {
+      mt = msgt;
+   }
+};
+
+#endif // MESSAGE_FILTER_H
diff --git a/src/gcm_input/rpm_filter.cpp b/src/gcm_input/rpm_filter.cpp
new file mode 100644 (file)
index 0000000..dd8655d
--- /dev/null
@@ -0,0 +1,134 @@
+
+/**************************************************************************
+**  (c) Copyright 2003, Andromeda Technology & Automation
+** This is free software; you can redistribute it and/or modify it under the
+** terms of the GNU General Public License, see the file COPYING.
+***************************************************************************
+** MODULE INFORMATION *
+***********************
+**      FILE NAME      : rpm_filter.cpp
+**      SYSTEM NAME    : 
+**      VERSION NUMBER : $Revision: 1.1 $
+**
+**  DESCRIPTION      :  Transform a list of packages into a Gnucomo XML document
+**
+**  EXPORTED OBJECTS : 
+**  LOCAL    OBJECTS : 
+**  MODULES  USED    :
+***************************************************************************
+**  ADMINISTRATIVE INFORMATION *
+********************************
+**      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
+**      CREATION DATE   : Nov 27, 2003
+**      LAST UPDATE     : Nov 27, 2003
+**      MODIFICATIONS   : 
+**************************************************************************/
+
+/*****************************
+   $Log: rpm_filter.cpp,v $
+   Revision 1.1  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+*****************************/
+
+/* static const char *RCSID = "$Id: rpm_filter.cpp,v 1.1 2003-12-04 10:38:09 arjen Exp $"; */
+
+#include <ctype.h>
+
+#include "rpm_filter.h"
+
+/*=========================================================================
+**  NAME           : constructXML
+**  SYNOPSIS       : int constructXML(message_buffer &in, strstream &xml)
+**  PARAMETERS     : 
+**  RETURN VALUE   : Create an XML document from a list of RPMs
+**
+**  DESCRIPTION    : 
+**                   Scan a list of packages and versions from "rpm -a".
+**                   A similar listing can be created on IRIX 6.5 by using the
+**                   command "showprods -3 -n|awk '{printf "%s-%s\n",$2,$3}'|grep -v '^[-=]' \
+**                             |grep -v Version-Description".
+**                 
+**                   We have to separate the package name and the version.
+**                   The separation is marked by a '-', followed by a digit.
+**                   However, there may be other sequences of '-'digit in the package name,
+**                   do we have to scan ahead until there is at most one such sequence
+**                   left in the version string. The '-'digit seqeunce inside the
+**                   version usually separates the version and the release number.
+**
+**  VARS USED      :
+**  VARS CHANGED   :
+**  FUNCTIONS USED :
+**  SEE ALSO       :
+**  LAST MODIFIED  : Nov 27, 2003
+**=========================================================================
+*/
+
+void rpm_filter::construct_XML(message_buffer &in, std::strstream &xml)
+{
+   String line;
+
+   scan_email_header(in);
+   construct_header(xml);
+
+   xml << "  <gcmt:data>\n";
+   xml << "    <gcmt:parameters class='package'>\n";
+
+   while (in >> line)
+   { 
+      int  version_start, next_version_start;
+      int  i;
+
+      //   Separate the package name from the version number
+      i = line.index('-');
+      version_start = i;
+      next_version_start = i;
+
+      while (i < ~line - 1)
+      {
+         while (i < ~line - 1 && !(line[i] == '-' && isdigit(line[i + 1])))
+         {
+            i++;
+         }
+         if (i < ~line - 1)
+         {
+            version_start = next_version_start;
+            next_version_start = i;
+         }
+         i++;
+      }
+      
+      if (!isdigit(line[version_start + 1]))
+      {
+         version_start = next_version_start;
+      }
+      String package(line(0,version_start));
+      String version(line(version_start + 1, ~line));
+
+      //   Create the XML element.
+
+      xml << "    <gcmt:parameter name='" << package << "'>\n";
+      xml << "      <gcmt:property name='version'>" << version << "</gcmt:property>\n";
+      xml << "    </gcmt:parameter>\n";
+   }
+   xml << "    </gcmt:parameters>\n";
+   xml << "  </gcmt:data>\n";
+   xml << "</gcmt:message>\n";
+}
+
+static const regex re_rpm("[[:alnum:]+-]+-[0-9][[:alnum:].-]");
+
+bool rpm_cooker::check_pattern(String logline)
+{
+   return logline == re_rpm;
+}
+
+bool rpm_cooker::cook_this(String logline, UTC arrival)
+{
+   return true;
+}
diff --git a/src/gcm_input/rpm_filter.h b/src/gcm_input/rpm_filter.h
new file mode 100644 (file)
index 0000000..3450522
--- /dev/null
@@ -0,0 +1,109 @@
+
+/**************************************************************************
+**  (c) Copyright 2003, Andromeda Technology & Automation
+** This is free software; you can redistribute it and/or modify it under the
+** terms of the GNU General Public License, see the file COPYING.
+***************************************************************************
+** MODULE INFORMATION *
+***********************
+**      FILE NAME      : rpm_filter.h
+**      SYSTEM NAME    : 
+**      VERSION NUMBER : $Revision: 1.1 $
+**
+**  DESCRIPTION      :  
+**
+**  EXPORTED OBJECTS : 
+**  LOCAL    OBJECTS : 
+**  MODULES  USED    :
+***************************************************************************
+**  ADMINISTRATIVE INFORMATION *
+********************************
+**      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
+**      CREATION DATE   : Nov 27, 2003
+**      LAST UPDATE     : Nov 27, 2003
+**      MODIFICATIONS   : 
+**************************************************************************/
+
+/*****************************
+   $Log: rpm_filter.h,v $
+   Revision 1.1  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+*****************************/
+
+/* static const char *RCSID = "$Id: rpm_filter.h,v 1.1 2003-12-04 10:38:09 arjen Exp $"; */
+
+#include "message_filter.h"
+#include "line_cooker.h"
+
+/*
+///////////////////////////////////////////////////////////////////////////
+//  NAME           : rpm_filter
+//  BASECLASS      : 
+//  MEMBERS        :
+//  OPERATORS      :
+//  METHODS        : 
+//
+//  DESCRIPTION    : 
+//
+//  RELATIONS      :
+//  SEE ALSO       :
+//  LAST MODIFIED  : Nov 27, 2003
+///////////////////////////////////////////////////////////////////////////
+*/
+
+class rpm_filter : public message_filter
+{
+
+protected:
+
+public:
+
+   rpm_filter(String host, UTC arriv, String service) : message_filter(host, arriv, service)
+   {
+   }
+
+   virtual void construct_XML(message_buffer &in, std::strstream &xml);
+};
+
+/*
+///////////////////////////////////////////////////////////////////////////
+//  NAME           : rpm_cooker
+//  BASECLASS      : 
+//  MEMBERS        :
+//  OPERATORS      :
+//  METHODS        : 
+//
+//  DESCRIPTION    : 
+//
+//  RELATIONS      :
+//  SEE ALSO       :
+//  LAST MODIFIED  : Nov 27, 2003
+///////////////////////////////////////////////////////////////////////////
+*/
+
+class rpm_cooker : public line_cooker
+{
+
+protected:
+
+
+public:
+
+   rpm_cooker()
+   {
+   }
+
+   bool check_pattern(String logline);
+   bool cook_this(String logline, UTC arrival);
+
+   String message_type()
+   {
+      return "package list";
+   }
+
+};
diff --git a/src/gcm_input/xml_cooker.cpp b/src/gcm_input/xml_cooker.cpp
new file mode 100644 (file)
index 0000000..f532f95
--- /dev/null
@@ -0,0 +1,69 @@
+
+/**************************************************************************
+**  (c) Copyright 2002, Andromeda Technology & Automation
+** This is free software; you can redistribute it and/or modify it under the
+** terms of the GNU General Public License, see the file COPYING.
+***************************************************************************
+** MODULE INFORMATION *
+***********************
+**      FILE NAME      : xml_cooker.cpp
+**      SYSTEM NAME    : 
+**      VERSION NUMBER : $Revision: 1.1 $
+**
+**  DESCRIPTION      :  Detect whether the input stream is in XML format
+**
+**  EXPORTED OBJECTS : 
+**  LOCAL    OBJECTS : 
+**  MODULES  USED    :
+***************************************************************************
+**  ADMINISTRATIVE INFORMATION *
+********************************
+**      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
+**      CREATION DATE   : Aug 06, 2003
+**      LAST UPDATE     : Aug 06, 2003
+**      MODIFICATIONS   : 
+**************************************************************************/
+
+/*****************************
+   $Log: xml_cooker.cpp,v $
+   Revision 1.1  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+*****************************/
+
+/* static const char *RCSID = "$Id: xml_cooker.cpp,v 1.1 2003-12-04 10:38:09 arjen Exp $"; */
+
+#include <ctype.h>
+
+#include "xml_cooker.h"
+
+static const regex re_xml_header("xml .*\?>$");
+
+bool xml_cooker::check_pattern(String logline)
+{
+   if (XML_detected)
+   {
+      return true;
+   }
+   else if (logline == re_xml_header)
+   {
+      XML_detected = true;
+      return true;
+   }
+   else
+   {
+      return false;
+   }
+}
+
+bool xml_cooker::cook_this(String logline, UTC arrival)
+{
+
+   return true;
+}
+
+
diff --git a/src/gcm_input/xml_cooker.h b/src/gcm_input/xml_cooker.h
new file mode 100644 (file)
index 0000000..7b36936
--- /dev/null
@@ -0,0 +1,79 @@
+
+/**************************************************************************
+**  (c) Copyright 2003, Andromeda Technology & Automation
+** This is free software; you can redistribute it and/or modify it under the
+** terms of the GNU General Public License, see the file COPYING.
+***************************************************************************
+** MODULE INFORMATION *
+***********************
+**      FILE NAME      : xml_cooker.h
+**      SYSTEM NAME    : 
+**      VERSION NUMBER : $Revision: 1.1 $
+**
+**  DESCRIPTION      :  The XML cooker does not cook anything. It is here to
+**                      match the XML header.
+**
+**  EXPORTED OBJECTS : 
+**  LOCAL    OBJECTS : 
+**  MODULES  USED    :
+***************************************************************************
+**  ADMINISTRATIVE INFORMATION *
+********************************
+**      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
+**      CREATION DATE   : Nov 26, 2003
+**      LAST UPDATE     : Nov 26, 2003
+**      MODIFICATIONS   : 
+**************************************************************************/
+
+/*****************************
+   $Log: xml_cooker.h,v $
+   Revision 1.1  2003-12-04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+*****************************/
+
+/* static const char *RCSID = "$Id: xml_cooker.h,v 1.1 2003-12-04 10:38:09 arjen Exp $"; */
+
+#include "line_cooker.h"
+
+/*
+///////////////////////////////////////////////////////////////////////////
+//  NAME           : xml_cooker
+//  BASECLASS      : line_cooker
+//  MEMBERS        :
+//  OPERATORS      :
+//  METHODS        : 
+//
+//  DESCRIPTION    : 
+//
+//  RELATIONS      :
+//  SEE ALSO       :
+//  LAST MODIFIED  : Nov 26, 2003
+///////////////////////////////////////////////////////////////////////////
+*/
+
+class xml_cooker : public line_cooker
+{
+
+   bool XML_detected;
+
+public:
+
+   xml_cooker()
+   {
+      XML_detected = false;
+   }
+
+   bool check_pattern(String logline);
+   bool cook_this(String logline, UTC arrival);
+
+   String message_type()
+   {
+      return "XML";
+   }
+
+};