]> SAFE projects GIT repository - mailleur.ref/commitdiff
Sync project from jmp/safe sources VI-0.19-dvl master tag-0.19
authorVincent Nolet-Pigeon <vincent@safe.ca>
Wed, 17 Dec 2025 19:30:10 +0000 (14:30 -0500)
committerVincent Nolet-Pigeon <vincent@safe.ca>
Wed, 17 Dec 2025 19:30:10 +0000 (14:30 -0500)
228 files changed:
Makefile [new file with mode: 0644]
Makefile.dbg [new file with mode: 0644]
Makefile.dist [new file with mode: 0644]
Notes [new file with mode: 0644]
app/Makefile [new file with mode: 0644]
app/chkspf.c [new file with mode: 0644]
app/chkspf.d [new file with mode: 0644]
app/feeder.c [new file with mode: 0644]
app/feeder.d [new file with mode: 0644]
app/receiver.c [new file with mode: 0644]
app/receiver.d [new file with mode: 0644]
app/scanner.c [new file with mode: 0644]
app/scanner.d [new file with mode: 0644]
app/sender.c [new file with mode: 0644]
app/sender.d [new file with mode: 0644]
app/sorter.c [new file with mode: 0644]
app/sorter.d [new file with mode: 0644]
app/toremake [new file with mode: 0644]
bin [new symlink]
certs/localhost-cert.pem [new file with mode: 0644]
certs/localhost-chain-cert.pem [new file with mode: 0644]
certs/localhost-key.pem [new file with mode: 0644]
certs/mailleur_server-cert_x509.pem [new file with mode: 0644]
certs/mailleur_server-chain-cert_x509.pem [new file with mode: 0644]
certs/mailleur_server-key.pem [new file with mode: 0644]
certs/root-safe_CA.pem [new file with mode: 0644]
certs/safeMDL.pem [new file with mode: 0644]
conf/.mailleur.conf.swp [new file with mode: 0644]
conf/blacklister.conf [new file with mode: 0644]
conf/mailleur.conf [new file with mode: 0644]
conf/mailleur.conf.dvl [new file with mode: 0644]
conf/relayed.conf [new file with mode: 0644]
conf/relayed.conf.dvl [new file with mode: 0644]
cron/mailleur.cron [new file with mode: 0644]
data-feed/dbgfeed.tst [new file with mode: 0644]
data-feed/extfeed00.tst [new file with mode: 0644]
data-feed/feed000.tst [new file with mode: 0644]
data-feed/feed001.tst [new file with mode: 0644]
data-feed/feed002.tst [new file with mode: 0644]
data-feed/feed003.tst [new file with mode: 0644]
data-feed/feed004.tst [new file with mode: 0644]
data-feed/feed005.tst [new file with mode: 0644]
data-feed/feed006.tst [new file with mode: 0644]
data-feed/feed007.tst [new file with mode: 0644]
data-feed/feed008.tst [new file with mode: 0644]
data-feed/feed009.tst [new file with mode: 0644]
data-feed/feed010.tst [new file with mode: 0644]
data-feed/feed011.tst [new file with mode: 0644]
data-feed/feed10.tst [new file with mode: 0644]
data-feed/feed11.tst [new file with mode: 0644]
data-feed/feed12.tst [new file with mode: 0644]
data-feed/feed13.tst [new file with mode: 0644]
data-feed/feed20.tst [new file with mode: 0644]
data-feed/feed21.tst [new file with mode: 0644]
data-feed/feed30.tst [new file with mode: 0644]
data-feed/feedx10.tst [new file with mode: 0644]
data-feed/feedxx.tst [new file with mode: 0644]
data-feed/feedyy.tst [new file with mode: 0644]
data-feed/zone.example.com [new file with mode: 0644]
data-spf/spf.tst [new file with mode: 0644]
dbg/mailleur.conf [new file with mode: 0644]
dovecot/conf.d/70-mailleur-mysql.conf [new file with mode: 0644]
dovecot/conf.d/70-mailleur-pgsql.conf [new file with mode: 0644]
dovecot/conf.d/80-mailleur-usesql.conf [new file with mode: 0644]
lib/Makefile [new file with mode: 0644]
lib/devlog.c [new file with mode: 0644]
lib/devlog.d [new file with mode: 0644]
lib/devlog.h [new file with mode: 0644]
lib/devsoc.c [new file with mode: 0644]
lib/devsoc.d [new file with mode: 0644]
lib/devsoc.h [new file with mode: 0644]
lib/devsql.c [new file with mode: 0644]
lib/devsql.d [new file with mode: 0644]
lib/devsql.h [new file with mode: 0644]
lib/geseml.c [new file with mode: 0644]
lib/geseml.d [new file with mode: 0644]
lib/geseml.h [new file with mode: 0644]
lib/gesspf.c [new file with mode: 0644]
lib/gesspf.d [new file with mode: 0644]
lib/gesspf.h [new file with mode: 0644]
lib/gessql.c [new file with mode: 0644]
lib/gessql.d [new file with mode: 0644]
lib/gessql.h [new file with mode: 0644]
lib/gestcp.c [new file with mode: 0644]
lib/gestcp.d [new file with mode: 0644]
lib/gestcp.h [new file with mode: 0644]
lib/lvleml.c [new file with mode: 0644]
lib/lvleml.d [new file with mode: 0644]
lib/lvleml.h [new file with mode: 0644]
lib/modrec.c [new file with mode: 0644]
lib/modrec.d [new file with mode: 0644]
lib/modrec.h [new file with mode: 0644]
lib/numver.h [new file with mode: 0644]
lib/subafn.c [new file with mode: 0644]
lib/subafn.d [new file with mode: 0644]
lib/subafn.h [new file with mode: 0644]
lib/subcnv.c [new file with mode: 0644]
lib/subcnv.d [new file with mode: 0644]
lib/subcnv.h [new file with mode: 0644]
lib/subrou.c [new file with mode: 0644]
lib/subrou.d [new file with mode: 0644]
lib/subrou.h [new file with mode: 0644]
lib/toremake [new file with mode: 0644]
lib/unidig.c [new file with mode: 0644]
lib/unidig.d [new file with mode: 0644]
lib/unidig.h [new file with mode: 0644]
lib/unidns.c [new file with mode: 0644]
lib/unidns.d [new file with mode: 0644]
lib/unidns.h [new file with mode: 0644]
lib/unieml.c [new file with mode: 0644]
lib/unieml.d [new file with mode: 0644]
lib/unieml.h [new file with mode: 0644]
lib/unimar.c [new file with mode: 0644]
lib/unimar.d [new file with mode: 0644]
lib/unimar.h [new file with mode: 0644]
lib/unipar.c [new file with mode: 0644]
lib/unipar.d [new file with mode: 0644]
lib/unipar.h [new file with mode: 0644]
lib/unipos.c [new file with mode: 0644]
lib/unipos.d [new file with mode: 0644]
lib/unipos.h [new file with mode: 0644]
lib/uniprc.c [new file with mode: 0644]
lib/uniprc.d [new file with mode: 0644]
lib/uniprc.h [new file with mode: 0644]
lib/unisig.c [new file with mode: 0644]
lib/unisig.d [new file with mode: 0644]
lib/unisig.h [new file with mode: 0644]
lib/unisql.c [new file with mode: 0644]
lib/unisql.d [new file with mode: 0644]
lib/unisql.h [new file with mode: 0644]
lib/unitls.c [new file with mode: 0644]
lib/unitls.d [new file with mode: 0644]
lib/unitls.h [new file with mode: 0644]
linux/osukiss/mailleur [new file with mode: 0644]
linux/osukiss/receiver [new file with mode: 0644]
linux/osukiss/scanner [new file with mode: 0644]
linux/osukiss/sender [new file with mode: 0644]
linux/osukiss/sorter [new file with mode: 0644]
mailleur.spec.in [new file with mode: 0644]
shell/getsysos.sh [new file with mode: 0644]
shell/test-spf.sh [new file with mode: 0755]
sql/.gitignore [new file with mode: 0644]
sql/Makefile [new file with mode: 0644]
sql/datatest.def [new file with mode: 0644]
sql/datatest.sql [new file with mode: 0644]
sql/mailleur.sql [new file with mode: 0644]
sql/preset.sql [new file with mode: 0644]
support/addconfig.sh [new file with mode: 0755]
support/do_database.sh [new file with mode: 0755]
support/do_dns_tlsa.sh [new file with mode: 0755]
support/do_httpd.sh [new file with mode: 0755]
support/dummy-cert.sh [new file with mode: 0755]
support/mailleur-reset-db.sh [new file with mode: 0755]
support/mailleur-settest-db.sh [new file with mode: 0755]
support/starting.sh [new file with mode: 0755]
sysconfig/mailleur [new file with mode: 0644]
test_area/etc/mailleur/blacklister.conf [new file with mode: 0644]
test_area/etc/mailleur/mailleur.conf [new file with mode: 0644]
test_area/etc/mailleur/mailleur.conf.dvl [new file with mode: 0644]
test_area/etc/mailleur/relayed.conf [new file with mode: 0644]
test_area/etc/mailleur/relayed.conf.dvl [new file with mode: 0644]
test_area/usr/share/mailleur/sql/.gitignore [new file with mode: 0644]
test_area/usr/share/mailleur/sql/Makefile [new file with mode: 0644]
test_area/usr/share/mailleur/sql/datatest.def [new file with mode: 0644]
test_area/usr/share/mailleur/sql/datatest.sql [new file with mode: 0644]
test_area/usr/share/mailleur/sql/mailleur.mysql [new file with mode: 0644]
test_area/usr/share/mailleur/sql/mailleur.postg [new file with mode: 0644]
test_area/usr/share/mailleur/sql/mailleur.sql [new file with mode: 0644]
test_area/usr/share/mailleur/sql/preset.sql [new file with mode: 0644]
test_area/var/run/mailleur/receiver.lock [new file with mode: 0644]
test_area/var/run/mailleur/scanner.lock [new file with mode: 0644]
test_area/var/run/mailleur/sender.lock [new file with mode: 0644]
test_area/var/run/mailleur/sorter.lock [new file with mode: 0644]
test_area/var/spool/mailleur/mails/example.com/postmaster/dovecot/new/23292-20251210053923-0979-0000 [new file with mode: 0644]
test_area/var/spool/mailleur/mails/example.com/utf8-áö_üñ/dovecot/new/23387-20251210053926-0983-0000 [new file with mode: 0644]
test_area/var/spool/mailleur/mails/example.com/webmaster/dovecot/new/23292-20251210053923-0979-0000 [new file with mode: 0644]
test_area/var/spool/mailleur/mails/example.com/webmaster/dovecot/new/23383-20251210053924-0903-0000 [new file with mode: 0644]
test_area/var/spool/mailleur/mails/example.com/webmaster/dovecot/new/23396-20251210053928-0663-0000 [new file with mode: 0644]
test_area/var/spool/mailleur/mails/mailleur.example.com/user1/dovecot/new/23398-20251210053932-0389-0000 [new file with mode: 0644]
test_area/var/spool/mailleur/receiver-logs/event-20251210.jrl [new file with mode: 0644]
test_area/var/spool/mailleur/scanner-logs/event-20251210.jrl [new file with mode: 0644]
test_area/var/spool/mailleur/scanner-logs/event-20251211.jrl [new file with mode: 0644]
test_area/var/spool/mailleur/sender-logs/event-20251210.jrl [new file with mode: 0644]
tools/DIGEST-MD5 [new file with mode: 0644]
tools/DIGEST-MD5-1 [new file with mode: 0644]
tools/DIGEST-MD5-2 [new file with mode: 0644]
tools/Makefile [new file with mode: 0644]
tools/chkhex.c [new file with mode: 0644]
tools/digmd5.c [new file with mode: 0644]
tools/genpsdusr.sh [new file with mode: 0755]
www/.lvlmai.php.swp [new file with mode: 0644]
www/coloration.js [new file with mode: 0644]
www/coloration.js.org [new file with mode: 0644]
www/complex.java [new file with mode: 0644]
www/devsql.php [new file with mode: 0644]
www/directives.js [new file with mode: 0644]
www/exetbl.php [new file with mode: 0644]
www/gesdis.php [new file with mode: 0644]
www/gessql.php [new file with mode: 0644]
www/index.php [new symlink]
www/lvllog.php [new file with mode: 0644]
www/lvlmai.php [new file with mode: 0644]
www/lvlrmt.php [new file with mode: 0644]
www/lvlusr.php [new file with mode: 0644]
www/passval.html [new file with mode: 0644]
www/reg-icons/Wwalczyszyn-Android-Style-Mail.96.png [new file with mode: 0644]
www/reg-icons/folder.gif [new file with mode: 0644]
www/reg-icons/login.gif [new file with mode: 0644]
www/release.in [new file with mode: 0644]
www/release.php [new file with mode: 0644]
www/res/chat.php [new file with mode: 0644]
www/res/chat1.php [new file with mode: 0644]
www/res/chat2.php [new file with mode: 0644]
www/res/chat3.php [new file with mode: 0644]
www/res/chat4.php [new file with mode: 0644]
www/res/gestbl.php [new file with mode: 0644]
www/res/unilng.php [new file with mode: 0644]
www/res/unimar.php [new file with mode: 0644]
www/res/unipos.php [new file with mode: 0644]
www/selectajax.js [new file with mode: 0644]
www/selectajax.js.ang [new file with mode: 0644]
www/selectajax.js.bad [new file with mode: 0644]
www/selectajax.js.fra [new file with mode: 0644]
www/subrou.php [new file with mode: 0644]
www/theme.css [new file with mode: 0644]
www/theme2.css [new file with mode: 0644]
www/translations.js [new file with mode: 0644]
www/unienv.php [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..24823ba
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,299 @@
+#====================================================================
+#Makefile to build the package
+#default make
+default        :  clean prod
+#--------------------------------------------------------------------
+#test area Makefile
+include        Makefile.dbg
+#distribution area Makefile
+include        Makefile.dist
+#--------------------------------------------------------------------
+#Executable generation area
+LOAD    = $(shell echo -j `echo $(shell /usr/bin/getconf _NPROCESSORS_ONLN)`)
+#--------------------------------------------------------------------
+prod                                                   \
+debug                                                  \
+       :
+          @ for i in $(SUBDIR) ;                       \
+              do                                       \
+              $(MAKE) $(LOAD) DB=$(DB) -C $$i $@ ;     \
+              done
+       
+       
+allclean                                               \
+clean  :  cleanrpm
+          @ for i in $(SUBDIR) ;                       \
+              do                                       \
+              $(MAKE) -s -C $$i $@ ;                   \
+              done
+          @ - rm -fr $(APPNAME)-*
+
+posql  :  clean debug
+          @ ln -nsf bin-posql bin
+          @ $(MAKE) -s newtest
+
+mysql  :  clean debug
+          @ ln -nsf bin-mysql bin
+          @ $(MAKE) -s newtest
+
+#====================================================================
+FEEDPAR        =                                               \
+               $(TESTIP)                               \
+               $(TESTPORT)                             \
+               ./$(DATATST)/feed0*.tst
+
+#to test with an external server
+extfeed        :       debug
+               @ bin-utils/feeder                      \
+                       -f                              \
+                       -d3                             \
+                       -c ./conf/feeder.conf.dvl       \
+                       $(EXTIP)                        \
+                       $(EXTPORT)                      \
+                       ./$(DATATST)/$(ONEFEED)
+
+
+#direct test
+tstfeed        :  debug
+          @ bin-utils/feeder                           \
+                         -c ./conf/$(APPNAME).conf.dvl \
+                        $(FEEDPAR)
+               
+               
+
+#using gdb
+dbgfeed        :  debug
+          @ gdb                                        \
+               --args                                  \
+                       bin-utils/feeder                \
+                         -f                            \
+                         -d 2                          \
+                         -c ./conf/$(APPNAME).conf.dvl \
+                         $(TESTIP)                     \
+                         $(TESTPORT)                   \
+                         ./$(DATATST)/$(ONEFEED).tst
+
+valfeed        :  debug                        #valgring of emlrcvr
+          @ echo "feed valgrind test"
+          @ valgrind                                   \
+               --leak-check=full                       \
+               --show-leak-kinds=$(VALKIND)            \
+                       bin-utils/feeder                \
+                         -f                            \
+                         -d2                           \
+                         -c ./conf/feeder.conf.dvl     \
+                         $(TESTIP)                     \
+                         $(TESTPORT)                   \
+                         ./$(DATATST)/$(ONEFEED)
+
+            
+#              --track-fds=yes                         \
+#testing TLS connection
+tlsrcvr        :       
+               @ clear
+                openssl s_client                                       \
+                       -trace                                          \
+                       -crlf                                           \
+                       -status                                         \
+                       -CAfile /etc/pki/tls/make-ca/ca-bundle.crt      \
+                       -cert certs/localhost-cert.pem                  \
+                       -key certs/localhost-key.pem                    \
+                       -starttls smtp                                  \
+                       -connect mailpostg.example.com:25
+#                      -connect smtp1.example.com:25
+#                      -connect courriel.colba.net:25
+#                      -connect mx1.free.fr:25
+#                      -connect $(TESTSRV):$(TESTPORT) 
+
+tlsmx1 :
+               @ clear
+                openssl s_client                                       \
+                       -trace                                          \
+                       -crlf                                           \
+                       -status                                         \
+                       -CAfile /etc/pki/tls/make-ca/ca-bundle.crt      \
+                       -cert certs/localhost-cert.pem                  \
+                       -key certs/localhost-key.pem                    \
+                       -starttls smtp                                  \
+                       -connect mx1.free.fr:25
+
+go465  :
+               @ clear
+               @ openssl s_client                                      \
+                       -crlf                                           \
+                       --showcerts                                     \
+                       -key certs/localhost-key.pem                    \
+                       -cert_chain certs/localhost-chain-cert.pem      \
+                       -CAfile /etc/pki/tls/make-ca/ca-bundle.crt      \
+                       -tls1_2                                         \
+                       -connect $(TESTIP):1025
+
+#                      -status                 
+#                      -crlf
+#                      -msg                    
+#                      -debug                  
+#                      -showcerts                      
+#                      -tls1_2
+#                      -connect mailpostg.example.com:25
+#                      -connect $(TESTSRV):$(TESTPORT) 
+#                      -connect smtp.google.com:25
+#                      -connect courriel.colba.net:25
+#
+#testing TLS with google
+tlsref:        
+               @ clear
+               @ openssl s_client                                      \
+                       -quiet                                          \
+                       -crlf                                           \
+                       -CAfile /etc/pki/tls/make-ca/ca-bundle.crt      \
+                       -cert certs/localhost-cert.pem                  \
+                       -key certs/localhost-key.pem                    \
+                       -starttls smtp                                  \
+                       -connect tar1.osukiss.org:25
+
+#                      -connect mx2.free.fr:25
+#                      -connect tar1.orukiss.org:25
+#                      -connect mailprod1.safe.ca:587
+#                      -connect tar1.osukiss.org:25
+#                      -connect smtp.google.com:25
+#                      -connect courriel.colba.net:25
+#                      -connect courriel.colba.net:587
+#                      -connect mailmysql.example.com:25
+#                      -connect $(TESTSRV):$(TESTPORT) 
+
+#--------------------------------------------------------------------
+#starting email receiver
+
+eml465 :  prepare
+          @ echo
+          @ echo "--------------"
+          @ echo "starting $@"
+          @ $(TESTDIR)/$(SBINDIR)/emlrcvr              \
+               -c ./conf/$(APPNAME).conf.dvl           \
+               -d 1                                    \
+               -r $(TESTDIR)                           \
+               smtps:$(TESTIP):1065:1
+
+
+prepare        :  clean debug newtest
+
+#--------------------------------------------------------------------
+#test procedure
+#--------------------------------------------------------------------
+#testing SPF fonction
+tstspf :  clean debug
+          @ shell/test-spf.sh data-spf/spf.tst
+
+#todo a specfic test
+#DATA  =  chkaddr.spf.example.com 127.0.1.255
+
+#--------------------------------------------------------------------
+#preparing a test area
+newtest        :  deltest
+          @ mkdir -p $(TESTDIR)/var/run
+          @ mkdir -p $(TESTDIR)/$(SBINDIR)
+          @ mkdir -p $(TESTDIR)/etc/$(APPNAME)
+          @ mkdir -p $(TESTDIR)/usr/share/$(APPNAME)
+          @ mkdir -p $(TESTDIR)/var/spool/$(APPNAME)/queue
+          @ mkdir -p $(TESTDIR)/var/spool/$(APPNAME)/mails
+          @ mkdir -p $(TESTDIR)/usr
+          @ cp -a                                      \
+               conf/*                                  \
+               $(TESTDIR)/etc/$(APPNAME)
+          @ cp -a                                      \
+               sql                                     \
+               $(TESTDIR)/usr/share/$(APPNAME)/
+          @ $(MAKE) -s -C sql prepdb
+
+deltest        :  
+          @ rm -fr $(TESTDIR)
+
+#--------------------------------------------------------------------
+#Installation procedure
+#--------------------------------------------------------------------
+install        :
+          @ # Creating all needed system directory
+          @ install -d $(DESTDIR)/$(BINDIR)/
+          @ install -d $(DESTDIR)/$(DATADIR)/$(APPN)/
+          @ install -d $(DESTDIR)/$(ETCDIR)/$(APPN)/
+          @ install -d $(DESTDIR)/$(ETCDIR)/pki/$(APPN)/
+          @ install -d $(DESTDIR)/$(ETCDIR)/sysconfig/
+          @ install -d $(DESTDIR)/$(ETCDIR)/httpd/conf.d/
+          @ install -d $(DESTDIR)/$(ETCDIR)/cron.d/
+          @ install -d $(DESTDIR)/$(LIBDIR)/$(APPN)/
+          @ install -d $(DESTDIR)/$(LIBDIR)/$(APPN)/tools
+          @ install -d $(DESTDIR)/$(EXECDIR)/$(APPN)/
+          @ install -d $(DESTDIR)/$(SPOOLDIR)/$(APPN)/mails
+          @ install -d $(DESTDIR)/$(SPOOLDIR)/$(APPN)/queue
+          @ install -d $(DESTDIR)/var/www/$(APPN)/
+          @ cp -a                                      \
+               bin-*/                                  \
+               $(DESTDIR)/$(EXECDIR)/$(APPN)
+          #set some utilities
+          @ ln -s                                      \
+               $(EXECDIR)/$(APPN)/bin-utils/chkspf     \
+               $(DESTDIR)/$(BINDIR)/
+          #
+          @ cp -a                                      \
+               conf/$(APPN).conf                       \
+               conf/relayed.conf                       \
+               conf/blacklister.conf                   \
+               $(DESTDIR)/$(ETCDIR)/$(APPN)
+          @ cp -a                                      \
+               conf/dovecot                            \
+               $(DESTDIR)/$(ETCDIR)/
+          @ cp -a                                      \
+               certs/root-safe_CA.pem                  \
+               $(DESTDIR)/$(ETCDIR)/pki/$(APPN)/
+          @ cp -a                                      \
+               linux                                   \
+               $(DESTDIR)/$(DATADIR)/$(APPN)/
+          @ cp -a                                      \
+               shell                                   \
+               support                                 \
+               $(DESTDIR)/$(LIBDIR)/$(APPN)/
+          @ cp -a                                      \
+               sysconfig                               \
+               $(DESTDIR)/$(ETCDIR)/
+          @ cp -a                                      \
+               cron/$(APPN).cron                       \
+               $(DESTDIR)/$(ETCDIR)/cron.d/
+          @ cp -a                                      \
+               sql                                     \
+               $(DESTDIR)/$(DATADIR)/$(APPN)/
+          @ cp -a                                      \
+               tools/genpsdusr.sh                      \
+               $(DESTDIR)/$(LIBDIR)/$(APPN)/tools
+          @ cp -a                                      \
+               www/*                                   \
+               $(DESTDIR)/var/www/$(APPN)/
+
+
+#--------------------------------------------------------------------
+SUBDIR =                                               \
+          lib                                          \
+          app                                          \
+          tools                                        \
+          sql                                          \
+
+#--------------------------------------------------------------------
+#definitions globale
+APPNAME        =  mailleur
+#--------------------------------------------------------------------
+#to set the compiled default library
+ifeq ($(strip $(DB)),)
+DB=0
+endif
+#--------------------------------------------------------------------
+#Managing testarea
+LIBDIR = /usr/lib
+DATADIR        = /usr/share
+BINDIR = /usr/bin
+SBINDIR = /usr/sbin
+SPOOLDIR= /var/spool
+EXECDIR        = /usr/libexec
+ETCDIR = /etc
+CURDIR  = $(shell pwd)
+#--------------------------------------------------------------------
+.PHONY:   clean cleanrpm
+#===================================================================
diff --git a/Makefile.dbg b/Makefile.dbg
new file mode 100644 (file)
index 0000000..d2b5932
--- /dev/null
@@ -0,0 +1,243 @@
+#===================================================================
+#debug area definition
+SBINDIR        =       usr/sbin
+DATATST        =       data-feed
+LOCKDIR        =       $(TESTDIR)/var/run/$(APPNAME)
+
+#managing debugging test
+TESTDIR        =       $(CURDIR)/test_area
+TESTSRV        =       mailleur.example.com
+TESTIP =       127.127.10.25
+TESTPORT=      1025
+#TESTPROT=     smtps
+EXTIP  =       safemail3.safe.ca
+EXTPORT =      25
+TESTITER=      2
+
+DBGPAR =                                               \
+          -r $(TESTDIR)                                \
+          -c ./conf/$(APPNAME).conf.dvl                \
+          -d 2                                         \
+
+EMLPAR =                                               \
+          $(DBGPAR)                                    \
+          "$(TESTPROT)|$(TESTIP)|$(TESTPORT)|"
+
+#====================================================================
+#area to start components receiver,sorter,sender once at a time
+#--------------------------------------------------------------------
+stdrcvr        :  clean debug
+          @                                            \
+            ./bin/receiver                             \
+               $(EMLPAR)$(TESTITER)
+
+gorcvr :  clean debug
+          @                                            \
+            ./bin/receiver                             \
+               -f                                      \
+               $(EMLPAR)$(TESTITER)
+
+digest :  clean debug
+             gdb                                       \
+               --args                                  \
+                                                       \
+            ./bin/receiver                             \
+               -f                                      \
+               -r $(TESTDIR)                           \
+               -c ./conf/$(APPNAME).conf.dvl           \
+               -d 9                                    \
+               "|devel5.safe.ca|1025|1"
+
+valdigest:  clean debug
+             valgrind                                  \
+               --leak-check=full                       \
+                --show-leak-kinds=$(VALKIND)           \
+                                                       \
+            ./bin/receiver                             \
+               -f                                      \
+               -r $(TESTDIR)                           \
+               -c ./conf/$(APPNAME).conf.dvl           \
+               -d 9                                    \
+               "|devel5.safe.ca|1025|1"
+
+#              "|127.127.10.25|1025|1"
+
+onercvr        :  clean debug
+          @                                            \
+            ./bin/receiver                             \
+               -f                                      \
+               $(EMLPAR)$(TESTITER)
+
+onescar:  clean debug
+         @ echo "Starting scanner"
+         @                                             \
+           bin/scanner                                 \
+               -f                                      \
+               $(DBGPAR)
+
+onefeed        :  debug
+          @                                            \
+                bin-utils/feeder                       \
+                       -f                              \
+                       -c ./conf/$(APPNAME).conf.dvl   \
+                       $(TESTIP)                       \
+                       $(TESTPORT)                     \
+                       ./$(DATATST)/$(ONEFEED).tst
+
+onesendr:  debug
+          @                                            \
+            ./bin/sender                               \
+                 -f                                    \
+                 -d 2                                  \
+                 -c ./conf/mailleur.conf.dvl           \
+                -r $(TESTDIR)                          \
+          
+onesortr:  debug
+          @                                            \
+            bin/sorter                                 \
+                  -f                                   \
+                  -d 9                                 \
+                  -c ./conf/mailleur.conf.dvl          \
+                  -r $(TESTDIR) 
+
+#--------------------------------------------------------------------
+#procedure to start all needed components to have
+#a full complete test
+
+restart        :  killall dosortr dorcvr doscarmt dosendr
+          @ echo "Restart completed"
+
+actions        :  killall debug newtest restart
+
+dosortr :  
+          @  echo "Starting Sorter"
+          @                                            \
+            bin/sorter                                 \
+                  -d 2                                 \
+                  -c ./conf/mailleur.conf.dvl          \
+                  -r $(TESTDIR) 
+
+
+dorcvr :  
+          @ echo "Starting receiver"
+          @                                            \
+            bin/receiver                               \
+               $(EMLPAR)$(TESTITER)
+
+dosendr:  debug
+          @ echo "Starting sender"
+          @                                            \
+            ./bin/sender                               \
+                 -d 2                                  \
+                 -c ./conf/mailleur.conf.dvl           \
+                -r $(TESTDIR)                          \
+
+doscarmt:
+          @ echo "Starting scanner"
+          @                                            \
+            bin/scanner                                \
+               $(DBGPAR)
+
+#to kill all remaining mailleur process
+killall        :
+          - kill -TERM receiver
+          - kill -TERM sender
+          - kill -TERM sorter
+          - kill -TERM scanner
+
+#--------------------------------------------------------------------
+#procedure to call debugger for each main component
+
+dbgrcvr        :  clean debug
+          @                                            \
+             gdb                                       \
+               --args                                  \
+              bin/receiver                             \
+               -f                                      \
+               $(EMLPAR)$(TESTITER)
+
+dbgsortr:  clean debug
+           @                                           \
+             gdb                                       \
+               --args                                  \
+               bin/sorter                              \
+                  -f                                   \
+                  -d 2                                 \
+                  -c ./conf/mailleur.conf.dvl          \
+                  -r $(TESTDIR) 
+
+dbgscar:  clean debug
+         @                                             \
+           gdb                                         \
+               --args                                  \
+               bin/scanner                             \
+                  -f                                   \
+                  $(DBGPAR)
+
+valscar:  clean debug
+         @                                             \
+             valgrind                                  \
+               --leak-check=full                       \
+                --show-leak-kinds=$(VALKIND)           \
+                                                       \
+               bin/scanner                             \
+                  -f                                   \
+                  $(DBGPAR)
+
+TODO   =  `basename -a                                 \
+          $(TESTDIR)/var/spool/$(APPNAME)/queue/*.todo|\
+           tr '\\n' ' ' `
+
+dbgsendr:  clean debug
+          @                                            \
+             gdb                                       \
+               --args                                  \
+                 bin/sender                            \
+                   -f                                  \
+                   -d2                                 \
+                   -c ./conf/mailleur.conf.dvl         \
+                   -r $(TESTDIR)                       \
+
+#--------------------------------------------------------------------
+#procedure to check programe against valgrind
+#VALKIND= "definite,possible,indirect,reachable"
+VALKIND        = "definite,possible,indirect"
+
+valrcvr        :  debug
+          @                                            \
+            valgrind                                   \
+               --leak-check=full                       \
+               --show-leak-kinds=$(VALKIND)            \
+              bin/receiver                             \
+               -f                                      \
+               $(EMLPAR)$(TESTITER)
+
+valsortr:  debug
+          @                                            \
+            valgrind                                   \
+               --leak-check=full                       \
+               bin/sorter                              \
+                  -f                                   \
+                  -d 2                                 \
+                  -c ./conf/mailleur.conf.dvl          \
+                  -r $(TESTDIR) 
+
+valsendr:  debug
+          @                                            \
+            valgrind                                   \
+               --leak-check=full                       \
+               --show-leak-kinds=$(VALKIND)            \
+            ./bin/sender                               \
+                 -f                                    \
+                 -d 2                                  \
+                 -c ./conf/mailleur.conf.dvl           \
+                -r $(TESTDIR)                          \
+                  `basename -a $(TESTDIR)/var/spool/$(APPNAME)/queue/*.todo  | \
+                       tr '\\n' ' ' `
+
+#--------------------------------------------------------------------
+#to Specify FEED
+ONEFEED=$(FEED)
+ifeq ($(strip $(FEED)),)
+ONEFEED=feed002
+endif
diff --git a/Makefile.dist b/Makefile.dist
new file mode 100644 (file)
index 0000000..767b79a
--- /dev/null
@@ -0,0 +1,106 @@
+#====================================================================
+#Makefile with all necessary to build distribution RPM
+APPN   =       mailleur
+#====================================================================
+binary :  rpm
+          @ echo "'$(APLV)' RPM binary file, building"
+          @ rpmbuild                                           \
+               --quiet                                         \
+               --noclean                                       \
+               --rebuild                                       \
+               --define "_topdir $(RPMDIR)"                    \
+               --target `uname -m`                             \
+               $(SRPM)/$(APPN)-$(VERSION).$(RELEASE)-*.src.rpm
+          @ echo "'$(APLV)' RPM binary file, ready"
+
+rpm    : cleanrpm tarfile spec
+         @ echo "'$(APLV)' RPM source file, building"
+         @ mkdir -p $(SRPM)
+         @ rpmbuild -bs                                        \
+               --quiet                                         \
+               --define "_topdir $(RPMDIR)"                    \
+               --define "_source_filedigest_algorithm md5"     \
+               --define "_binary_filedigest_algorithm md5"     \
+               spec
+         @ echo "'$(APLV)' RPM source file, ready"
+
+tarfile        : clean
+         @ mkdir -p $(SRC)
+         @ echo "'$(APLV)' tar file, building"
+         @ mkdir -p                                            \
+               $(APLV)/app                                     \
+               $(APLV)/certs                                   \
+               $(APLV)/conf                                    \
+               $(APLV)/bin                                     \
+               $(APLV)/lib                                     \
+               $(APLV)/linux                                   \
+               $(APLV)/shell                                   \
+               $(APLV)/sysconfig                               \
+               $(APLV)/support                                 \
+               $(APLV)/sql                                     \
+               $(APLV)/tools                                   \
+               $(APLV)/www                                     \
+               $(APLV)
+         @ date > $(APLV)/$(APLV).build_date
+         @ cp -a Makefile*     $(APLV)/
+         @ cp -a app/*.c       $(APLV)/app/
+         @ cp -a                                               \
+               certs/root-safe_CA.pem                          \
+               $(APLV)/certs/
+         @ cp -a                                               \
+               conf/*.conf                                     \
+               dovecot                                         \
+               $(APLV)/conf/
+         @ cp -a lib/{*.c,*.h} $(APLV)/lib/
+         @ cp -a linux/*       $(APLV)/linux/
+         @ cp -a                                               \
+               shell/getsysos.sh                               \
+               $(APLV)/shell/
+         @ cp -a                                               \
+               cron/                                           \
+               tools/                                          \
+               $(APLV)/
+         @ cp -a                                               \
+               www/*.php                                       \
+               www/reg-icons                                   \
+               $(APLV)/www
+         @ cp -a support/*.sh                                  \
+               $(APLV)/support
+         @ cp -a sysconfig/*                                   \
+               $(APLV)/sysconfig
+         @ cp -a                                               \
+               sql/$(APPN).sql                                 \
+               sql/preset.sql                                  \
+               sql/datatest.*                                  \
+               sql/Makefile                                    \
+               $(APLV)/sql
+         @ cp -a app/Makefile  $(APLV)/app/Makefile
+         @ cp -a lib/Makefile  $(APLV)/lib/Makefile
+         @ tar zcf $(SRC)/$(APLV).tar.gz $(APLV)
+         @ rm -fr $(APLV)
+         @ echo "'$(APLV)' tar file, ready"
+         
+spec   : $(APPN).spec.in
+         @ sed                                                 \
+               -e 's/@@DIST@@/dvl/g'                           \
+               -e 's/@@APPN@@/$(APPN)/g'                       \
+               -e 's/@@VERSION@@/$(VERSION).$(RELEASE)/g'      \
+               -e 's/@@RELEASE@@//g'                           \
+               < $< > $@
+
+
+               
+cleanrpm:
+         @ rm -fr $(RPMDIR)/* spec
+
+#====================================================================
+#rpm definitions
+RPMDIR  =       $(CURDIR)/rpmbuild
+SRC    =       $(RPMDIR)/SOURCES
+SRPM   =       $(RPMDIR)/SRPMS
+#====================================================================
+VERSION        =       $(shell echo `grep VERSION lib/numver.h | cut -d '"' -f2`)
+RELEASE        =       $(shell echo `grep RELEASE lib/numver.h | cut -d '"' -f2`)
+APPV   =       $(VERSION).$(RELEASE)
+APLV   =       $(APPN)-$(APPV)
+#====================================================================
diff --git a/Notes b/Notes
new file mode 100644 (file)
index 0000000..33bbff0
--- /dev/null
+++ b/Notes
@@ -0,0 +1,19 @@
+Versions:
+
+#-----------------------------------------------------------
+MA-0.6-dvl, certificat exchange is working properly
+#-----------------------------------------------------------
+#Format for trans file
+status date    delta   session from    rcpt
+Char   long    int     char *  char *  char *
+status can be:
+       C       completed       ;email dispatched
+       D       default         ;email can not be dispatched
+       L       local           ;Email stay on local serveur
+       R       Remote          ;email is to be sent on remote site
+#-----------------------------------------------------------
+#b64 Exchange
+cmVhbG09Im1haWxsZXVyLWVtYWlsIixub25jZT0iTVd0dlVIWlZhVzV3ZDJSWGJFZEtTWEJGTVZSbFRHbGljazVpYTIxQiIscW9wPWF1dGgsYWxnb3JpdGhtPW1kNS1zZXNzLGNoYXJzZXQ9dXRmLTg=
+
+dXNlcm5hbWU9ImRpZ2kiLHJlYWxtPSJtYWlsbGV1ci1lbWFpbCIsbm9uY2U9Ik1XdHZVSFpWYVc1d2QyUlhiRWRLU1hCRk1WUmxUR2xpY2s1aWEyMUIiLGNub25jZT0iNU1TQ2Z5YUN0WWxES2c2YkVhK0kiLG5jPTAwMDAwMDAxLHFvcD0iYXV0aCIsZGlnZXN0LXVyaT0ic210cC8xOTIuMjE5LjI1NC4xODEiLHJlc3BvbnNlPWQxYTFlOGFmMDUzZmJhNmQzY2NhMzg3NmQ2YWY1N2ExLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz
+#-----------------------------------------------------------
diff --git a/app/Makefile b/app/Makefile
new file mode 100644 (file)
index 0000000..d3d4f25
--- /dev/null
@@ -0,0 +1,167 @@
+#--------------------------------------------------------------------
+#Executable generation area
+#--------------------------------------------------------------------
+debug  : 
+           @ $(MAKE) OPTIME="-g" DEBUG="-DDEBUG" exe 
+           @ echo "application compiled in '$@' mode now ready"
+
+prod   :
+          @ $(MAKE) OPTIME="-g -O2" exe
+          @ echo "application compiled in '$@' mode now ready"
+
+exe    :
+          $(MAKE) $(EXE)
+
+
+clean  :
+          - rm -fr *.o $(EXE)
+          - rm -fr ../bin-*
+
+allclean:  clean
+          - rm -fr *.d
+
+#--------------------------------------------------------------------
+#Equivalences
+#--------------------------------------------------------------------
+EXE    =                                               \
+       chkspf                                          \
+       feeder                                          \
+       receiver                                        \
+       scanner                                         \
+       sender                                          \
+       sorter                                          \
+
+SRC    =                                               \
+       chkspf.c                                        \
+       feeder.c                                        \
+       receiver.c                                      \
+       scanner.c                                       \
+       sender.c                                        \
+       sorter.c                                        \
+
+OBJS   =                                               \
+       chkspf.o                                        \
+       feeder.o                                        \
+       receiver.o                                      \
+       scanner.o                                       \
+       sorter.o                                        \
+       sender.o                                        \
+
+#--------------------------------------------------------------------
+#definitions
+#--------------------------------------------------------------------
+CC     =  gcc 
+LD     =  gcc -g
+CFLAGS =  -I ../lib -Wall $(OPTIME)
+LIBMAIL        =  ../lib/libmail.a
+
+LIBS   =       $(LIBMAIL)                              \
+               -luuid                                  \
+               -lcrypto                                \
+               -lcrypt                                 \
+               -lssl                                   \
+
+#--------------------------------------------------------------------
+#Dependances
+#--------------------------------------------------------------------
+chkspf                                                 \
+feeder                                                 \
+       :  toremake $(OBJS)
+          @ $(LD) $(LDFLAGS) -o ../bin-utils/$@ $@.o $(LIBS)
+
+receiver                                               \
+scanner                                                        \
+sender                                                 \
+sorter                                                 \
+       :  toremake $(OBJS)
+          @ $(LD) $(LDFLAGS)                           \
+                -o ../bin-posql/$@ $@.o                \
+                $(LIBS)                                \
+                ../lib/libpos.a                        \
+                -lpq
+          @ $(LD) $(LDFLAGS)                           \
+                -o ../bin-mysql/$@ $@.o                \
+                $(LIBS)                                \
+                ../lib/libmar.a                        \
+                -lmysqlclient
+
+
+chkspf.o:  chkspf.c                                    \
+          ../lib/unidns.h                              \
+          ../lib/subafn.h                              \
+          ../lib/subrou.h
+
+receiver.o:receiver.c                                  \
+          ../lib/modrec.h                              \
+          ../lib/gessql.h                              \
+          ../lib/devsql.h                              \
+          ../lib/unitls.h                              \
+          ../lib/unisig.h                              \
+          ../lib/uniprc.h                              \
+          ../lib/unipar.h                              \
+          ../lib/unidns.h                              \
+          ../lib/subrou.h
+
+feeder.o:  feeder.c                                    \
+          ../lib/gestcp.h                              \
+          ../lib/devsoc.h                              \
+          ../lib/unieml.h                              \
+          ../lib/unipar.h                              \
+          ../lib/subrou.h
+
+sorter.o:  sorter.c                                    \
+          ../lib/geseml.h                              \
+          ../lib/unisig.h                              \
+          ../lib/uniprc.h                              \
+          ../lib/unipar.h                              \
+          ../lib/unieml.h                              \
+          ../lib/unidns.h                              \
+          ../lib/subrou.h
+
+scanner.o: scanner.c                                   \
+          ../lib/devlog.h                              \
+          ../lib/devsql.h                              \
+          ../lib/gessql.h                              \
+          ../lib/unidns.h                              \
+          ../lib/unipar.h                              \
+          ../lib/uniprc.h                              \
+          ../lib/unisig.h                              \
+          ../lib/subafn.h                              \
+          ../lib/subrou.h
+
+sender.o:  sender.c                                    \
+          ../lib/lvleml.h                              \
+          ../lib/geseml.h                              \
+          ../lib/unieml.h                              \
+          ../lib/unipar.h                              \
+          ../lib/uniprc.h                              \
+          ../lib/unisig.h                              \
+          ../lib/subrou.h
+
+toremake:  Makefile
+          touch toremake
+          @ - mkdir -p ../bin-utils
+          @ - mkdir -p ../bin-posql
+          @ - mkdir -p ../bin-mysql
+          @ # - rm -f $(EXE) *.o
+
+#--------------------------------------------------------------------
+#to manage dependencies
+%.d:     %.c
+         @ set -e; rm -f $@;                                   \
+           $(CC) -I ../lib -M $(CPPFLAGS) $< > $@.$$$$;        \
+           sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+           rm -f $@.$$$$
+
+sources        =                                                       \
+           chkspf.c                                            \
+           feeder.c                                            \
+           receiver.c                                          \
+           scanner.c                                           \
+           sender.c                                            \
+           sorter.c                                            \
+
+include $(sources:.c=.d)
+#--------------------------------------------------------------------
+.PHONY:   toremake clean allclean
+#--------------------------------------------------------------------
diff --git a/app/chkspf.c b/app/chkspf.c
new file mode 100644 (file)
index 0000000..7dac2ac
--- /dev/null
@@ -0,0 +1,107 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Validation program to check SPF extraction      */
+/*      library.                                        */
+/*                                                     */
+/********************************************************/
+#include       <errno.h>
+#include       <stdio.h>
+#include       <stdlib.h>
+#include       <string.h>
+#include       <unistd.h>
+
+#include       "subrou.h"
+#include       "subafn.h"
+#include       "unidns.h"
+#include       "unipar.h"
+#include       "uniprc.h"
+#include       "gesspf.h"
+
+#define CHKSPF "chkspf"        //application name
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to check SPF domain/peerip status     */
+/*      return the SPF status as an ascii.              */
+/*                                                     */
+/********************************************************/
+static void checkstatus(char *domain,char *peerip)
+
+{
+int try;
+SPFENU spf;
+AFNTYP **afns;
+
+try=0;
+afns=afn_getipnums(peerip);
+if (afns!=(AFNTYP **)0) {
+  for (int i=0;afns[i]!=(AFNTYP *)0;i++) {
+    spf=spf_getstatus(&try,domain,afns[i]);
+    (void) fprintf(stdout,"%s\n",spf_spfASCII(spf));
+    }
+  afns=(AFNTYP **)rou_freelist((void **)afns,(genfree_t)afn_freeipnum);
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Main routine                                    */
+/*             Return an SPF status according arguments*/
+/*              1 - Domain name                         */
+/*              2 - IP                                  */
+/*                                                     */
+/********************************************************/
+int main(int argc,char *argv[])
+
+{
+int status;
+int phase;
+ARGTYP *params;
+_Bool proceed;
+
+status=0;
+params=(ARGTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //checking parameters
+      if ((params=par_getparams(argc,argv,"d:hr:v"))==(ARGTYP *)0) 
+        phase=999;      //no need to go further
+      break;
+    case 1      :       //check if we have file to scan
+      if (params->argc!=2) {
+        (void) fprintf(stderr,"Unable to proceed with test\n");
+        (void) fprintf(stderr,"\tthe test pattern number should be 2\n");
+        status=-1;
+        phase=999;
+        }
+      break;
+    case 2      :
+      (void) rou_modesubrou(true);
+      (void) rou_setappname(CHKSPF);
+      (void) prc_preptitle(argc,argv,environ);
+      foreground=true;
+      break;
+    case 3      :       //doing main task
+      (void) checkstatus(params->argv[0],params->argv[1]);
+      break;
+    case 4      :       //task completed
+      (void) prc_cleantitle();
+      params=par_freeparams(params);
+      (void) rou_modesubrou(false);
+      break;
+    default     :       //end of task
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+(void) exit(status);
+}
diff --git a/app/chkspf.d b/app/chkspf.d
new file mode 100644 (file)
index 0000000..6f2e3af
--- /dev/null
@@ -0,0 +1,60 @@
+chkspf.o chkspf.d : chkspf.c /usr/include/stdc-predef.h /usr/include/errno.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/stdio.h \
+ /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/__fpos_t.h \
+ /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/stdlib.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h \
+ ../lib/subrou.h /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h ../lib/subafn.h \
+ /usr/include/netdb.h /usr/include/netinet/in.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h /usr/include/bits/socket.h \
+ /usr/include/bits/socket_type.h /usr/include/bits/sockaddr.h \
+ /usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h ../lib/unidns.h \
+ ../lib/subafn.h ../lib/unipar.h ../lib/uniprc.h ../lib/subrou.h \
+ ../lib/gesspf.h ../lib/gesspf.h
diff --git a/app/feeder.c b/app/feeder.c
new file mode 100644 (file)
index 0000000..e5c7fd0
--- /dev/null
@@ -0,0 +1,862 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*      SMTP protocol feeder.                           */
+/*     Used to transmit data to remote SMTP server.    */
+/*                                                      */
+/*      Format is:                                      */
+/*              feeder ip port [file1 file2...]         */
+/*                                                     */
+/********************************************************/
+#include       <errno.h>
+#include       <signal.h>
+#include       <stdlib.h>
+#include       <stdio.h>
+#include       <string.h>
+#include       <syslog.h>
+#include       <unistd.h>
+
+#include       "subrou.h"
+#include       "unipar.h"
+#include       "unieml.h"
+#include       "devsoc.h"
+#include       "gestcp.h"
+
+#define FNAME   "feeder"
+
+#define        WRESP   30              //wait 30 sec for SMTP server
+                                //to answer
+
+static  char titre[100];        //test title
+static  char testname[300];     //test description
+
+typedef struct  {
+        char *destip;           //IP to connect to
+        char *destport;         //Port to connect to
+        char *srcip;            //IP to be used as source
+        _Bool commented;        //feed in comment mode
+        FILE *datatst;          //reference to the data-tst file
+        SOCPTR *socptr;         //connection socket
+        }FEEDTYP;
+
+typedef enum    {               //List of command
+        cmd_comment,            //Test sequence in int/out comment mode
+        cmd_connect,            //Start connection with smtp server
+        cmd_data,               //start the SMTP data sequence
+        cmd_gotls,              //going in tls mode
+        cmd_orgn,             //Change peer IP origin
+        cmd_restart,            //restart connection with remote server
+        cmd_wait,               //delay exchange with remote
+        cmd_unknown
+        }CMDTYP;
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to display feeder usage               */
+/*                                                     */
+/********************************************************/
+static void usage(const char *name)
+
+{
+(void) fprintf(stderr,"usage:\n  ");
+(void) fprintf(stderr,"%s\t"
+                 "[-d debug] "
+                 "[-h] "
+                  "remote_name port [filename1 filename2...]\n",name);
+(void) fprintf(stderr,"\twhere:\n");
+(void) fprintf(stderr,"\t\t-d level\t: debug level [1-10]\n");
+(void) fprintf(stderr,"\t\t-h\t\t: print this help message\n");
+(void) fprintf(stderr,"\t\t:remote_name, fully qualified domain name\n");
+(void) fprintf(stderr,"\t\t:port, remote acces port to access\n");
+(void) fprintf(stderr,"\t\t:filenames, a set a filename to be feed to remote\n");
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Reporting a problem with parsing test           */
+/*                                                     */
+/********************************************************/
+static void report(int numline,char *line,char *trouble)
+
+
+{
+(void) rou_alert(0,"line %d %s",numline,trouble);
+(void) rou_alert(0,"\t'%s'",line);
+}
+/*
+^L
+*/
+/************************************************/
+/*                                             */
+/*     Send transmission out                   */
+/*                                             */
+/************************************************/
+static int sendout(SOCPTR *socptr,char *line)
+
+{
+int taille;
+
+taille=tcp_write(socptr,line);
+taille+=tcp_write(socptr,CRLF);
+return taille;
+}
+/*
+^L
+*/
+/************************************************/
+/*                                             */
+/*     procedure to set feed scan in comment   */
+/*      mode.                                   */
+/*                                             */
+/************************************************/
+static _Bool setcomment(FEEDTYP *fd,char *action)
+
+{
+_Bool done;
+_Bool flag;
+
+done=false;
+flag=false;
+if (strcmp("START",action)==0)
+  flag=true;
+if (flag!=fd->commented) {
+  fd->commented=flag;
+  done=true;
+  }
+return done;
+}
+/*
+^L
+*/
+/************************************************/
+/*                                             */
+/*     procedure to send data to remote SMTP   */
+/*      server.                                 */
+/*                                             */
+/************************************************/
+static _Bool dosenddata(FEEDTYP *fd,int *numline,char *expected)
+
+{
+#define OPEP    "feeder.c:dosenddata,"
+
+int status;
+char *line;
+char *action;
+int numdata;
+char data[300];
+int phase;
+_Bool proceed;
+
+status=false;
+line=(char *)0;
+action="DATA";
+numdata=0;
+(void) strcpy(data,"");
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Transmit "DATA" to remote
+      if (sendout(fd->socptr,action)!=(strlen(action)+2))
+        phase=999;      //Unable to send STARTTLS sequence 
+      break;
+    case 1      :       //Get DATA command status
+      if (tcp_getline(fd->socptr,WRESP,&line)<0)
+        phase=999;      //Didn't get signon
+      break;
+    case 2      :       //did we received the Proper "proceed" acknoledge
+      if (expected==(char *)0)
+        expected="Nothing?";
+      if (strcmp(line,expected)!=0) {
+        (void) rou_alert(0,"expecting\t'%s'",expected);
+        (void) rou_alert(0,"found\t\t'%s'",line);
+        phase=999;      //did NOT get proper signon
+        }
+      break;
+    case 3      :       //
+      while (fgets(data,sizeof(data),fd->datatst)!=(char *)0) {
+        _Bool completed;
+
+        completed=false;
+        numdata++;
+        (*numline)++;
+        (void) eml_removecrlf(data);
+        if (data[1]!=':') {
+          (void) rou_alert(0,"%s Unexpected data type <%s> (Bug?)",
+                                  OPEP,data);
+          continue;
+          }
+        switch (data[0]) {
+          case 'D'      :       //pure data
+            if (data[2]=='.') {
+              (void) memmove(data+3,data+2,strlen(data+2));
+              }
+            (void) sendout(fd->socptr,data+2);
+            break;
+          case 'C'      :       //data marker
+            switch (data[2]) {
+              case '.'  :       //end of data marker
+                if (numdata>1)  //could be empty data
+                  (void) sendout(fd->socptr,".");
+                completed=true;
+                break;
+              case 'T'  :       //timer data
+                time_t isnow;
+                char ed[100];
+
+                isnow=time((time_t *)0);
+                (void) snprintf(ed,sizeof(ed),"Date: %s",rou_ascsysstamp(isnow));
+                (void) sendout(fd->socptr,ed);
+                break;
+              default   :       //unexpected data marker
+                 (void) rou_alert(0,"%s Unexpected data marker <%s> (Bug?)",
+                                     OPEP,data);
+                break;
+              }
+            break;
+          default       :
+            (void) rou_alert(0,"%s Unexpected data to be send <%s> (Bug?)",
+                                  OPEP,data);
+            break;
+          }
+        if (completed==true) {
+          status=true;
+          break;        //All data sent.
+          }
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+#undef  OPEP
+}
+/*
+^L
+*/
+/************************************************/
+/*                                             */
+/*     procedure to set the link in TLS mode   */
+/*                                             */
+/************************************************/
+static _Bool gomodetls(SOCPTR *socptr)
+
+{
+#define OPEP    "feeder.c:gomodetls,"
+
+_Bool status;
+char *got;
+char *action;
+int phase;
+_Bool proceed;
+
+status=false;
+got=(char *)0;
+action="starttls";
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d' status='%d'",OPEP,phase,status);
+  switch (phase) {
+    case 0      :       //Sending START TLS command
+      if (sendout(socptr,action)!=(strlen(action)+2))
+        phase=999;      //Unable to send STARTTLS sequence 
+      break;
+    case 1      :       //Get STARTTLS command status
+      if (tcp_getline(socptr,WRESP,&got)<=0)
+        phase=999;      //Didn't get signon
+      break;
+    case 2      :       //did we received the signon
+      if (got!=(char *)0) {     //Always
+        int code;
+
+        code=0;
+        (void) sscanf(got,"%d",&code); 
+        if (code!=SIGNON) 
+          phase=999;
+        got=rou_freestr(got);
+        }
+      break;
+    case 3      :       //initiating TLS-Crypted in client mode
+      if (soc_starttls(socptr,false)==false)  
+        phase=999;
+      break;
+    case 4      :       //everything is fine SOC in crypted mode
+      (void) usleep(10000);     //sleep 10 millisec
+      status=true;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to connect to SMTP server if NOT YET  */
+/*      connected                                       */
+/*                                                     */
+/********************************************************/
+static _Bool doconnect(FEEDTYP *fd)
+
+{
+#define OPEP    "feeder.c:doconnect"
+
+_Bool isopen;
+int phase;
+_Bool proceed;
+
+isopen=true;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //is the connection really open
+      if (fd->socptr==(SOCPTR *)0) {
+        fd->socptr=soc_openfeedsock(pro_smtp,fd->srcip,fd->destip,fd->destport);
+        if (fd->socptr==(SOCPTR *)0) {
+          (void) rou_alert(0,"%s Unable to open link to [%s:%s]",
+                              OPEP,fd->destip,fd->destport);
+          isopen=false;
+          phase=999;
+          }
+        }
+      break;
+    case 1      :       //is the connect really open
+      (void) usleep(100000);    //let wait form remote disconnect
+      if (soc_waitforchar(fd->socptr,10*1000)>=0) {
+        phase=999;      //Yes no need to go further
+        }
+      break;
+    case 2      :       //the remote just disconnect
+      fd->socptr=soc_closefeedsock(fd->socptr); //let clean the link
+      fd->socptr=soc_openfeedsock(pro_smtp,fd->srcip,fd->destip,fd->destport);
+      if (soc_waitforchar(fd->socptr,10*1000)<0) {
+        (void) rou_alert(0,"%s Unable to re-open link to [%s:%s]",
+                            OPEP,fd->destip,fd->destport);
+        isopen=false;
+        phase=999;
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return isopen;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/************************************************/
+/*                                             */
+/*     procedure to to restart link with a     */
+/*      new source IP.                          */
+/*                                             */
+/************************************************/
+static _Bool dorestart(FEEDTYP *fd,char *newip)
+
+{
+_Bool status;
+
+status=true;
+if ((newip!=(char *)0)&&(strlen(newip)>0)) {
+  fd->srcip=rou_freestr(fd->srcip);
+  fd->srcip=strdup(newip);
+  }
+fd->socptr=soc_closefeedsock(fd->socptr);
+(void) usleep(100000); //wait 10 millisec
+fd->socptr=soc_openfeedsock(pro_smtp,fd->srcip,fd->destip,fd->destport);
+if (fd->socptr==(SOCPTR *)0) 
+  status=false;
+return status;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to execute a command to test SMTP     */
+/*      protocol.                                       */
+/*                                                     */
+/********************************************************/
+static int getcmd(const char *command)
+
+
+{
+static struct   {
+      int code;
+      const char *cmd;
+      }cmdavail[]={
+            {cmd_comment,"COMMENT"},
+            {cmd_connect,"CONNECT"},
+            {cmd_data,"DATA"},
+            {cmd_gotls,"GOTLS"},
+            {cmd_orgn,"ORGN:"},
+            {cmd_restart,"RESTART"},
+            {cmd_wait,"WAIT"},
+            {cmd_unknown,(const char *)0}
+            };
+
+int code;
+
+code=cmd_unknown;
+for (int i=0;cmdavail[i].cmd!=(const char *)0;i++) {
+  if (strcasecmp(cmdavail[i].cmd,command)==0) {
+    code=cmdavail[i].code;
+    break;      //Code found
+    }
+  }
+return code;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to execute a command to test SMTP     */
+/*      protocol.                                       */
+/*                                                     */
+/********************************************************/
+static _Bool docommand(FEEDTYP *fd,int *numline,char *line)
+
+{
+#define OPEP    "docommand"
+
+_Bool status;
+char *param;
+
+status=true;
+if ((param=strchr(line,' '))!=(char *)0) {
+  *param='\000';         //cut line
+  param++;              //pointer on parameter
+  }
+switch (getcmd(line)) {
+  case cmd_comment      :       //detect comment flag
+    if (setcomment(fd,param)==false)
+      (void) report(*numline,line,"Unable to set comment mode");
+    break;
+  case cmd_connect      :       //start link connection
+    if (doconnect(fd)==false) {
+      (void) report(*numline,line,"Unable connect!");
+      status=false;
+      }
+    break;
+  case cmd_data         :       //sending email content to remote
+    if ((status=dosenddata(fd,numline,param))==false)
+      (void) report(*numline,line,"Unable to fully send data");
+    break;
+  case cmd_gotls        :       //GOTLS
+    if ((status=gomodetls(fd->socptr))==false)
+      (void) report(*numline,line,"Unable to set TLS mode");
+    break;
+  case cmd_orgn       :       //send command to override client IP
+    if (param!=(char *)0) {
+      char cmd[100];
+
+      (void) snprintf(cmd,sizeof(cmd)-1,"%s %s",line,param);
+      (void) sendout(fd->socptr,cmd);
+      }
+    else {
+      (void) report(*numline,line,"Missing new PEER IP value");
+      status=false;
+      }
+    break;
+  case cmd_restart      :       //restart link
+    if ((status=dorestart(fd,(char *)0))==false)
+      (void) report(*numline,line,"Unable to restart feed socket!");
+    break;
+  case cmd_wait         :       //SLEEP mode
+    if (param!=(char *)0)
+      (void) sleep(atoi(param));
+    else {
+      (void) report(*numline,line,"Missing 'seconds' parameters");
+      status=false;
+      }
+    break;
+  case cmd_unknown      :
+  default       :
+    (void) report(*numline,line,"Unknown command");
+    status=false;
+    break;
+  }
+return status;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to compare incoming data with expected*/
+/*                                                     */
+/********************************************************/
+static _Bool doincoming(SOCPTR *socptr,int numline,char *line)
+
+{
+#define OPEP    "feeder.c:doincoming"
+#define STRETC  "..."
+
+_Bool status;
+int got;
+char *received;
+int tocheck;
+int phase;
+_Bool proceed;
+
+status=false;
+got=0;
+received=(char *)0;
+tocheck=strlen(line);
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG, phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //do we have a "..." sequence at the end
+      if (tocheck>0) {
+        char *ptr;
+
+        if ((ptr=strstr(line,STRETC))!=(char *)0) {
+          if (strlen(ptr)==strlen(STRETC)) {
+            tocheck-=strlen(STRETC);
+            }
+          }
+        }
+      break;
+    case 1      :       //waiting for a line with CRLF
+      received=(char *)0;
+      got=tcp_getline(socptr,WAITRMT,&received);
+      (void) rou_alert(3,"%s, received=<%s>",OPEP,received);
+      switch (got) {
+        case  0 :       //Reading timeout
+          (void) rou_alert(0,"Unable to receive line in due time");
+          phase=999;      //No need to go further
+          break;
+        case -1 :       //signal received
+          (void) rou_alert(0,"A Signal was received");
+          phase=999;      //No need to go further
+          break;
+        case -2 :       //remote is disconnected
+          received=rou_freestr(received);
+          received=strdup("Disconnected");
+          break;
+        default :       //got a good line
+          break;
+        }
+      break;
+    case 2      :       //get available character
+      if (strcmp(line,received)==0)
+        phase++;        //ligne is equal, good
+      else {            //small check?
+        if ((tocheck<strlen(line))&&(strncmp(line,received,tocheck)==0)) 
+          phase++;
+        }
+      break;
+    case 3      :       //can we test shorter
+      (void) rou_alert(0,"\n%s\tline %04d, fatal error! (on %s)",
+                          titre,numline,testname);
+      (void) rou_alert(0,"expecting\t'%s'",line);
+      (void) rou_alert(0,"found\t\t'%s'",received);
+       phase=999;
+      break;
+    case 4      :       //everything fine
+      //(void) rou_alert(0,"JMPDBG line <%s> OK!",line);
+      status=true;
+      break;
+    default     :       //SAFE Guard
+      received=rou_freestr(received);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Scanning one line from test file                */
+/*                                                     */
+/********************************************************/
+static _Bool scanline(FEEDTYP *fd,int *numline,char *line)
+
+{
+#define OPEP "feeder.c:scanline"
+
+_Bool status;
+char action;
+int phase;
+_Bool proceed;
+
+status=true;
+action='\000';
+phase=0;
+proceed=true;
+(void) rou_alert(3,"%s line=<%s>",OPEP,line);
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //removing first space
+      while ((line[0]==' ')||(line[0]=='\t'))
+        (void) memmove(line,line+1,(strlen(line+1)+1));
+      if (line[0]=='#') {       //line is a comment.
+        proceed=false;;         //discarding line
+        (*numline)++;
+        }
+      break;
+    case 1      :       //checking if test file format is OK
+      if (line[1]!=':') {
+        (void) report(*numline,line,"Malformed test data");
+        status=false;
+        proceed=false; //discarding line
+        }
+      action=line[0];
+      line+=2;
+      break;
+    case 2      :       //check open version action
+      switch (action) {
+        case 'R'        :       //wait Receiving line
+        case 'S'        :       //Sending line
+          break;
+        default         :       //Nothing to do
+          break;
+        }
+      break;
+    case 3      :       //discarding comment
+      switch (action) {
+        case 'C'        :       //command to execute
+          status=docommand(fd,numline,line);
+          break;
+        case 'R'        :       //Receiving data
+          status=doincoming(fd->socptr,*numline,line);
+          break;
+        case 'S'        :       //sending data
+          (void) sendout(fd->socptr,line);
+          break;
+        case 'T'        :       //Get the test titre
+          (void) memset(testname,'\000',sizeof(testname));
+          status=snprintf(testname,sizeof(testname)-1,"%s",line);
+          break;
+        default         :       //Unexpected
+          line-=2;
+          (void) report(*numline,line,"Unexpected directive");
+          status=false;
+          break;
+        }
+      (*numline)++;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Scanning data file.                             */
+/*                                                     */
+/********************************************************/
+static _Bool scanonefile(FEEDTYP *fd,const char *filename)
+
+{
+_Bool status;
+int numline;
+char line[3000];
+int phase;
+_Bool proceed;
+
+status=false;
+(void) strcpy(testname,"");
+numline=1;
+(void) strcpy(line,"");
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG scanfile phase='%d'",phase);
+  switch (phase) {
+    case 0      :       //checking parameters
+      (void) rou_alert(0,"Scanning file <%s>",filename);
+      if (filename!=(char *)0) {
+        const char *ptr;
+
+        if ((ptr=strrchr(filename,'/'))!=(const char *)0)
+          ptr++;
+        else
+          ptr=filename;
+        (void) snprintf(titre,sizeof(titre),"%s:",ptr);
+        }
+      if ((fd->datatst=fopen(filename,"r"))==(FILE *)0) {
+        (void) rou_alert(0,"Unable to open file <%s> (error=<%s>",
+                            filename,strerror(errno));
+        proceed=false;
+        }
+      break;
+    case 1      :       //Opening the socket (obsolete)
+      break;
+    case 2      :       //reading line;
+      while (fgets(line,sizeof(line),fd->datatst)!=(char *)0) {
+        char *ptr;
+
+        (void) rou_alert(5,"%s num='%02d' <%s>","feeder",numline,line);
+        while ((ptr=strrchr(line,'\n'))!=(char *)0)
+          *ptr='\000';
+        if ((fd->commented==true)&&(strncmp(line,"C:",2)!=0)) {
+          numline++;
+          continue;
+          }
+        if (scanline(fd,&numline,line)==false) {
+          phase=999;    //Trouble trouble exiting
+          break;
+          }
+        }
+      break;
+    case 3      :       //scanning went well
+      status=true;
+      break;
+    default     :       //SAFE Guard
+      fd->socptr=soc_closefeedsock(fd->socptr);
+      (void) fclose(fd->datatst);
+      fd->datatst=(FILE *)0;
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+if (status==true)
+  (void) rou_alert(2,"Scanning file <%s> was completed OK",filename);
+return status;
+}
+/********************************************************/
+/*                                                     */
+/*     procedure to scan all files                     */
+/*                                                     */
+/********************************************************/
+static int scanallfiles(int argc,char *argv[])
+
+{
+int numfile;
+FEEDTYP *feed;
+
+int next;
+int phase;
+
+numfile=0;
+feed=(FEEDTYP *)calloc(1,sizeof(FEEDTYP));
+feed->srcip=strdup(srcip);
+feed->destip=strdup(argv[0]);
+feed->destport=strdup(argv[1]);
+next=2;
+phase=0;
+while (next<argc) {
+  switch (phase) {
+    case  0     :       //display scanned file
+      break;
+    case  1     :       //scanning file
+      if (scanonefile(feed,argv[next])==false) 
+        next=argc;      //do not check next file
+      break;
+    case  2     :       //incrementing numfile scanned
+      numfile++;
+      break;
+    default     :       //still file to be scanned
+      next++;
+      phase=0;
+      break;
+    }
+  phase++;
+  }
+feed->destport=rou_freestr(feed->destport);
+feed->destip=rou_freestr(feed->destip);
+feed->srcip=rou_freestr(feed->srcip);
+(void) free(feed);
+return numfile;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Main routine                                    */
+/*             Start a channel to a remote ip.port     */
+/*              read file and transmit contecnts to     */
+/*              remote SMTP server.                     */
+/*                                                     */
+/********************************************************/
+int main(int argc,char *argv[])
+
+{
+int status;
+ARGTYP *params;
+int numfile;
+int phase;
+_Bool proceed;
+
+status=0;
+params=(ARGTYP *)0;
+foreground=true;
+numfile=0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) fprintf(stdout,"JMPDBG main phase='%d'\n",phase);
+  switch (phase) {
+    case 0      :       //checking parameters
+      if ((params=par_getparams(argc,argv,"c:d:fhi:r:v"))==(ARGTYP *)0) {
+        proceed=false;      //no need to go further
+        }
+      break;
+    case 1      :       //initialising process
+      if (params->argc<2) {
+        (void) fprintf(stdout,"Error, missing! remote_name? port?\n");
+        (void) usage("feeder");
+        phase=999;      //can not go further
+        }
+      break;
+    case 2      :       //opening remote channel
+      (void)  rou_loadconfig(config,true);
+      (void) openlog("feeder",LOG_NDELAY|LOG_PID,LOG_DAEMON);
+      numfile=scanallfiles(params->argc,params->argv);
+      if (numfile==(params->argc-2))
+        (void) rou_alert(0,"%d file successfully submitted to <%s.%s>",
+                               numfile,params->argv[0],params->argv[1]);
+      (void) closelog();
+      break;
+    default     :       //end of task
+      params=par_freeparams(params);
+      (void)  rou_loadconfig(config,false);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+(void) exit(status);
+}
diff --git a/app/feeder.d b/app/feeder.d
new file mode 100644 (file)
index 0000000..291315d
--- /dev/null
@@ -0,0 +1,119 @@
+feeder.o feeder.d : feeder.c /usr/include/stdc-predef.h /usr/include/errno.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/signal.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/signum-generic.h \
+ /usr/include/bits/signum-arch.h /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timespec.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/types/stack_t.h /usr/include/sys/ucontext.h \
+ /usr/include/bits/sigstack.h /usr/include/bits/sigstksz.h \
+ /usr/include/bits/ss_flags.h /usr/include/bits/types/struct_sigstack.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/stdlib.h \
+ /usr/include/bits/libc-header-start.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/struct_timeval.h \
+ /usr/include/alloca.h /usr/include/bits/stdlib-float.h \
+ /usr/include/stdio.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/syslog.h /usr/include/sys/syslog.h \
+ /usr/include/bits/syslog-path.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h \
+ ../lib/subrou.h /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h ../lib/unipar.h \
+ ../lib/unieml.h ../lib/subafn.h /usr/include/netdb.h \
+ /usr/include/netinet/in.h /usr/include/bits/stdint-uintn.h \
+ /usr/include/sys/socket.h /usr/include/bits/types/struct_iovec.h \
+ /usr/include/bits/socket.h /usr/include/bits/socket_type.h \
+ /usr/include/bits/sockaddr.h /usr/include/asm/socket.h \
+ /usr/include/asm-generic/socket.h /usr/include/asm/sockios.h \
+ /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h ../lib/devsoc.h \
+ ../lib/unitls.h /usr/include/openssl/ssl.h /usr/include/openssl/macros.h \
+ /usr/include/openssl/opensslconf.h /usr/include/openssl/configuration.h \
+ /usr/include/openssl/opensslv.h /usr/include/openssl/e_os2.h \
+ /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-least.h /usr/include/openssl/e_ostime.h \
+ /usr/include/sys/time.h /usr/include/openssl/comp.h \
+ /usr/include/openssl/crypto.h /usr/include/openssl/safestack.h \
+ /usr/include/openssl/stack.h /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h /usr/include/openssl/core.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/comperr.h /usr/include/openssl/bio.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/x509.h \
+ /usr/include/openssl/buffer.h /usr/include/openssl/buffererr.h \
+ /usr/include/openssl/evp.h /usr/include/openssl/core_dispatch.h \
+ /usr/include/openssl/indicator.h /usr/include/openssl/params.h \
+ /usr/include/openssl/bn.h /usr/include/openssl/bnerr.h \
+ /usr/include/openssl/evperr.h /usr/include/openssl/objects.h \
+ /usr/include/openssl/obj_mac.h /usr/include/openssl/asn1.h \
+ /usr/include/openssl/asn1err.h /usr/include/openssl/objectserr.h \
+ /usr/include/openssl/ec.h /usr/include/openssl/ecerr.h \
+ /usr/include/openssl/rsa.h /usr/include/openssl/rsaerr.h \
+ /usr/include/openssl/dsa.h /usr/include/openssl/dh.h \
+ /usr/include/openssl/dherr.h /usr/include/openssl/dsaerr.h \
+ /usr/include/openssl/sha.h /usr/include/openssl/x509err.h \
+ /usr/include/openssl/x509_vfy.h /usr/include/openssl/lhash.h \
+ /usr/include/openssl/pkcs7.h /usr/include/openssl/pkcs7err.h \
+ /usr/include/openssl/http.h /usr/include/openssl/conf.h \
+ /usr/include/openssl/conferr.h /usr/include/openssl/conftypes.h \
+ /usr/include/openssl/pem.h /usr/include/openssl/pemerr.h \
+ /usr/include/openssl/hmac.h /usr/include/openssl/async.h \
+ /usr/include/openssl/asyncerr.h /usr/include/openssl/ct.h \
+ /usr/include/openssl/cterr.h /usr/include/openssl/sslerr.h \
+ /usr/include/openssl/sslerr_legacy.h /usr/include/openssl/prov_ssl.h \
+ /usr/include/openssl/ssl2.h /usr/include/openssl/ssl3.h \
+ /usr/include/openssl/tls1.h /usr/include/openssl/dtls1.h \
+ /usr/include/openssl/srtp.h /usr/include/openssl/quic.h ../lib/gestcp.h \
+ ../lib/subrou.h ../lib/unidns.h ../lib/devlog.h ../lib/devsoc.h
diff --git a/app/receiver.c b/app/receiver.c
new file mode 100644 (file)
index 0000000..f126022
--- /dev/null
@@ -0,0 +1,110 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*      Email Receiver                                  */
+/*     SMTP Daemon dedicated to receive Email.         */
+/*                                                     */
+/********************************************************/
+#include       <signal.h>
+#include       <stdlib.h>
+#include       <stdio.h>
+#include       <unistd.h>
+
+#include       "subrou.h"
+#include       "unipar.h"
+#include       "uniprc.h"
+#include       "unisig.h"
+#include       "unitls.h"
+#include       "devsql.h"
+#include       "gessql.h"
+#include       "modrec.h"
+
+#define RECNAME "receiver"
+
+//port listening format is "IP:PORT NUMBER:num iteration"
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Main routine                                    */
+/*             Start multiple SMTP daemon and make     */
+/*              sure there always ready to answer to    */
+/*              SMTP request.                           */
+/*                                                     */
+/********************************************************/
+int main(int argc,char *argv[])
+
+{
+int status;
+ARGTYP *params;
+SQLPTR *sqlptr;
+int phase;
+_Bool proceed;
+
+status=0;
+params=(ARGTYP *)0;
+sqlptr=(SQLPTR *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //checking parameters
+      if ((params=par_getparams(argc,argv,"c:d:D:fhr:v"))==(ARGTYP *)0) {
+        phase=999;      //no need to go further
+        }
+      break;
+    case 1      :       //initialising process
+      //changing the working ID if started as root
+      if (foreground==true)
+        (void) fprintf(stdout,"Running application in foreground\n");
+      (void) prc_preptitle(argc,argv,environ);
+      (void) rou_setappname(RECNAME);
+      (void) rou_modesubrou(true);
+      (void) prc_chgid(argv[0]);
+      (void) rou_loadconfig(config,true);
+      (void) prc_modeuniprc(true);
+      (void) sig_modeunisig(true);
+      (void) tls_modeunitls(true);
+      (void) sig_trapsignal(true,sig_alrm);
+      break;
+    case 2      :       //sett lock
+      if (foreground==false) {
+        if (prc_divedivedive()!=0)
+          phase=999;    //direct exit
+        }
+      break;
+    case 3      :       //opening data base
+      if ((sqlptr=sql_opensql())==(SQLPTR *)0) {
+        (void) fprintf(stdout,"Unable to reach database server (aborting!)\n");
+        phase=999;      //Aborting
+        }
+      break;
+    case 4      :       //make sur link are all zero in DB
+      (void) sql_droplinks(sqlptr);
+      sqlptr=sql_closesql(sqlptr);
+      break;
+    case 5      :       //doing main task
+      if (prc_locking(appname,true,5)==false)
+        phase=999;      //Trouble trouble
+      break;
+    case 6      :       //doing main task
+      (void) rec_handlesmtp(params->argc,params->argv);
+      (void) prc_locking(appname,false,1);
+      break;
+    default     :       //end of task
+      params=par_freeparams(params);
+      (void) rou_loadconfig(config,false);
+      (void) sig_trapsignal(false,sig_alrm);
+      (void) tls_modeunitls(false);
+      (void) sig_modeunisig(false);
+      (void) prc_modeuniprc(false);
+      (void) rou_modesubrou(false);
+      (void) prc_cleantitle();
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+(void) exit(status);
+}
diff --git a/app/receiver.d b/app/receiver.d
new file mode 100644 (file)
index 0000000..57ba83d
--- /dev/null
@@ -0,0 +1,116 @@
+receiver.o receiver.d : receiver.c /usr/include/stdc-predef.h /usr/include/signal.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/signum-generic.h \
+ /usr/include/bits/signum-arch.h /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timespec.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/types/stack_t.h /usr/include/sys/ucontext.h \
+ /usr/include/bits/sigstack.h /usr/include/bits/sigstksz.h \
+ /usr/include/bits/ss_flags.h /usr/include/bits/types/struct_sigstack.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/stdlib.h \
+ /usr/include/bits/libc-header-start.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/struct_timeval.h \
+ /usr/include/alloca.h /usr/include/bits/stdlib-float.h \
+ /usr/include/stdio.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h \
+ ../lib/subrou.h /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ ../lib/unipar.h ../lib/uniprc.h ../lib/subrou.h ../lib/unisig.h \
+ ../lib/unitls.h /usr/include/openssl/ssl.h /usr/include/openssl/macros.h \
+ /usr/include/openssl/opensslconf.h /usr/include/openssl/configuration.h \
+ /usr/include/openssl/opensslv.h /usr/include/openssl/e_os2.h \
+ /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/bits/stdint-least.h \
+ /usr/include/openssl/e_ostime.h /usr/include/sys/time.h \
+ /usr/include/openssl/comp.h /usr/include/openssl/crypto.h \
+ /usr/include/openssl/safestack.h /usr/include/openssl/stack.h \
+ /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h /usr/include/openssl/core.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/comperr.h /usr/include/openssl/bio.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/x509.h \
+ /usr/include/openssl/buffer.h /usr/include/openssl/buffererr.h \
+ /usr/include/openssl/evp.h /usr/include/openssl/core_dispatch.h \
+ /usr/include/openssl/indicator.h /usr/include/openssl/params.h \
+ /usr/include/openssl/bn.h /usr/include/openssl/bnerr.h \
+ /usr/include/openssl/evperr.h /usr/include/openssl/objects.h \
+ /usr/include/openssl/obj_mac.h /usr/include/openssl/asn1.h \
+ /usr/include/openssl/asn1err.h /usr/include/openssl/objectserr.h \
+ /usr/include/openssl/ec.h /usr/include/string.h /usr/include/strings.h \
+ /usr/include/openssl/ecerr.h /usr/include/openssl/rsa.h \
+ /usr/include/openssl/rsaerr.h /usr/include/openssl/dsa.h \
+ /usr/include/openssl/dh.h /usr/include/openssl/dherr.h \
+ /usr/include/openssl/dsaerr.h /usr/include/openssl/sha.h \
+ /usr/include/openssl/x509err.h /usr/include/openssl/x509_vfy.h \
+ /usr/include/openssl/lhash.h /usr/include/openssl/pkcs7.h \
+ /usr/include/openssl/pkcs7err.h /usr/include/openssl/http.h \
+ /usr/include/openssl/conf.h /usr/include/openssl/conferr.h \
+ /usr/include/openssl/conftypes.h /usr/include/openssl/pem.h \
+ /usr/include/openssl/pemerr.h /usr/include/openssl/hmac.h \
+ /usr/include/openssl/async.h /usr/include/openssl/asyncerr.h \
+ /usr/include/openssl/ct.h /usr/include/openssl/cterr.h \
+ /usr/include/openssl/sslerr.h /usr/include/openssl/sslerr_legacy.h \
+ /usr/include/openssl/prov_ssl.h /usr/include/openssl/ssl2.h \
+ /usr/include/openssl/ssl3.h /usr/include/openssl/tls1.h \
+ /usr/include/openssl/dtls1.h /usr/include/openssl/srtp.h \
+ /usr/include/openssl/quic.h ../lib/subafn.h /usr/include/netdb.h \
+ /usr/include/netinet/in.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h /usr/include/bits/socket.h \
+ /usr/include/bits/socket_type.h /usr/include/bits/sockaddr.h \
+ /usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h ../lib/devsql.h \
+ ../lib/unisql.h ../lib/gessql.h ../lib/unieml.h ../lib/devsql.h \
+ ../lib/modrec.h
diff --git a/app/scanner.c b/app/scanner.c
new file mode 100644 (file)
index 0000000..a9a566a
--- /dev/null
@@ -0,0 +1,398 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*      Handle all remote IP credibility level          */
+/*      By scanning database for new entry within       */
+/*      database tables remotes and try to establish    */
+/*      remote server "credibility" according remote IP.*/
+/*                                                      */
+/*                                                     */
+/********************************************************/
+#include        <dirent.h>
+#include        <errno.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <syslog.h>
+#include        <unistd.h>
+
+#include        "devsql.h"
+#include        "devlog.h"
+#include        "gessql.h"
+#include        "unidns.h"
+#include        "unipar.h"
+#include        "uniprc.h"
+#include        "unisig.h"
+#include        "subafn.h"
+#include        "subrou.h"
+
+#define SCANNER "scanner"     //application name
+
+typedef struct  {
+    LOGPTR *logptr;     //session log refrence pointer
+    SQLPTR *sqlptr;     //sesion database reference pointer
+    }SCATYP;
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to check the remote ip credibility    */
+/*      remote ip credibility.                          */
+/*                                                     */
+/********************************************************/
+static void check_credibility(SCATYP *scanref,BLKTYP **dnsblk,int num,char *rmtip)
+
+{
+#define OPEP    "scanner.c:check_credibilty,"
+
+SRVTYP *srv;
+AFNTYP *afn;
+char *reversip;
+int phase;
+_Bool proceed;
+
+srv=(SRVTYP *)0;
+afn=(AFNTYP *)0;
+reversip=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {      
+    case 0      :       //get the remoteip AFN
+      if ((afn=afn_getoneipnum(rmtip))==(AFNTYP *)0) {
+        (void) rou_alert(0,"%s Unable to get afn data for remote <%s> (WRONG IP?)",
+                            OPEP,rmtip);
+        (void) sql_mngremote(scanref->sqlptr,sql_delete,rmtip,&srv);
+        phase=999;      //Trouble trouble
+        }
+      break;
+    case 1      :       //loading rmtip record
+      if (sql_mngremote(scanref->sqlptr,sql_select,rmtip,&srv)==false) {
+        (void) rou_alert(0,"%s Unable to get remote <%s> data (database?)",
+                            OPEP,rmtip);
+        phase=999;      //Trouble trouble
+        }
+      break;
+    case 2      :       //getting the reverss-address
+      reversip=afn_reversipnum(afn);
+      srv->reverse=rou_freestr(srv->reverse);
+      srv->reverse=dns_get_reverse_addr(reversip);
+      break;
+    case 3      :       //updating record according blacklisting
+      srv->lastscan=time((time_t *)0);
+      if (dnsblk!=(BLKTYP **)0) {
+        int delta;
+        char *bank;
+
+        delta=0;
+        bank=(char *)0;
+        while (*dnsblk!=(BLKTYP *)0) {
+          char *report;
+          char *listed;
+          char cst[100];
+
+          report=(char *)0;
+          listed=dns_is_blacklisted(*dnsblk,reversip);
+          (void) snprintf(cst,sizeof(cst),"IP[%02d]: %s\t",num,rmtip);
+          if (listed!=(char *)0) {
+            delta+=(*dnsblk)->delta;
+            (void) rou_asprintf(&report,"%s%s",cst,listed);
+            if (srv->listing==(char *)0)
+              srv->listing=strdup(listed);
+            }
+          else
+            (void) rou_asprintf(&report,"%s%s -> NOT listed",cst,
+                                                             (*dnsblk)->sitename);
+          (void) log_fprintlog(scanref->logptr,true,report); 
+          report=rou_freestr(report);
+          listed=rou_freestr(listed);
+          dnsblk++;
+          }
+        srv->credit+=delta;
+        bank=eml_showcredit(rmtip,srv->reverse,delta,srv->credit);
+        (void) log_fprintlog(scanref->logptr,true,"IP[%02d]: %s",num,bank);
+        (void) log_fprintlog(scanref->logptr,false,"");
+        bank=rou_freestr(bank);
+        }
+      break;
+    case 4      :       //updating record
+      if (sql_mngremote(scanref->sqlptr,sql_update,rmtip,&srv)==false) {
+        (void) rou_alert(0,"%s Unable to update remote <%s> data (database?)",
+                            OPEP,rmtip);
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+srv=sql_freesrv(srv);
+reversip=rou_freestr(reversip);
+afn=afn_freeipnum(afn);
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to load the list of dnsbls server     */
+/*      remote ip credibility.                          */
+/*                                                     */
+/********************************************************/
+static BLKTYP **load_dnsbls()
+
+{
+#define OPEP    "scanner.c:load_dnsbls,"
+
+const char *blcklist="BLACKLISTER";
+
+BLKTYP **dnsblk;
+FILE *blkfile;
+const char *filename;
+int phase;
+_Bool proceed;
+
+dnsblk=(BLKTYP **)0;
+blkfile=(FILE *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Getting dnsbls server list filename
+      if ((filename=getenv(blcklist))==(char *)0) {
+        (void) rou_alert(0,"%s <%s> env variable missing (config?!)",
+                            OPEP,blcklist);
+        phase=999;      //no need to go further
+        } 
+      break;
+    case 1      :       //opening the file
+      if (filename!=(char *)0) {        //always
+        char *fullpath;
+
+        fullpath=rou_apppath(filename);
+        if ((blkfile=fopen(fullpath,"r"))==(FILE *)0) {
+          (void) rou_alert(0,"%s Unable to open file <%s> (error=<%s>)",
+                            OPEP,fullpath,strerror(errno));
+          phase=999;    //trouble trouble
+          }
+        fullpath=rou_freestr(fullpath);
+        }
+      break;
+    case 2      :       //scaning file
+      if (blkfile!=(FILE *)0) {         //always
+        char line[300];
+
+        while (fgets(line,sizeof(line)-1,blkfile)!=(char *)0) {
+          (void) rou_clean_conf_line(line);
+          dnsblk=dns_addblklist(dnsblk,line);
+          }
+        (void) fclose(blkfile);
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return dnsblk;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to check database and check new       */
+/*      remote ip credibility.                          */
+/*                                                     */
+/********************************************************/
+static void doscanning(int frequency)
+
+{
+#define OPEP    "scanner.c:doscanning,"
+
+unsigned long cycle;
+SCATYP scanref;
+BLKTYP **dnsblk;
+char **rmtip;
+int delay;
+int phase;
+_Bool proceed;
+
+cycle=0;
+(void) memset(&scanref,'\000',sizeof(scanref));
+dnsblk=load_dnsbls();
+rmtip=(char **)0;
+delay=60;       //60 second sleeping time
+if (debug>0)
+  delay/=10;
+phase=0;
+proceed=(dnsblk!=(BLKTYP **)0);
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //locking access
+      if (prc_locking(appname,true,5)==false) {
+        (void) rou_alert(0,"%s Unable to lock %s exclusif acccess (system?)",
+                            OPEP,appname);
+        proceed=false;
+        }
+      break;
+    case 1      :       //check about signal
+      cycle++;
+      (void) prc_settitle("%s:%s, sleeping mode (cycle=%08d)",
+                           APPNAME,appname,cycle);
+      (void) sleep(delay);
+      break;
+    case 2      :       //double check signal
+      if ((hangup==true)||((reload==true))) {
+        (void) rou_alert(0,"%s got hangup or reload signal",OPEP);
+        phase=999;
+        }
+      break;
+    case 3      :       //refreshing the dnsbls list
+      if ((cycle%100)==0) {
+        dnsblk=(BLKTYP **)rou_freelist((void **)dnsblk,(genfree_t)dns_freeblk);
+        dnsblk=load_dnsbls();
+        }
+      proceed=(dnsblk!=(BLKTYP **)0);
+      break;
+    case 4      :       //opening database
+      if ((scanref.sqlptr=sql_opensql())==(SQLPTR *)0) {
+        (void) rou_alert(0,"%s Unable to open database (Config?)",OPEP);
+        phase=999;      //Trouble trouble
+        }
+      break;
+    case 5      :       //check for new entry
+      if ((rmtip=sql_getnewrmtip(scanref.sqlptr,frequency))==(char **)0) {
+        scanref.sqlptr=sql_closesql(scanref.sqlptr);
+        phase=0;        //nothing to be done
+        }
+      break;
+    case 6      :  {    //opening log
+      char *sessid;
+      int numip;
+      char cmt[200];
+
+      sessid=eml_getmainsesid();
+      numip=rou_nbrlist((void **)rmtip);
+      (void) snprintf(cmt,sizeof(cmt),"%d remote IP to scan",numip);
+      if ((scanref.logptr=log_openlog(sessid,cmt))==(LOGPTR *)0) {
+        (void) rou_alert(0,"%s Unable to open log file (Bug?)",OPEP);
+        rmtip=(char **)rou_freelist((void **)rmtip,(genfree_t)rou_freestr);
+        phase=999;
+        }
+      sessid=rou_freestr(sessid);
+      }
+      break;
+    case 7      :       //check entries
+      for (int i=0;rmtip[i]!=(char *)0;i++) {
+        (void) check_credibility(&scanref,dnsblk,i+1,rmtip[i]);
+        if ((hangup==true)||((reload==true))) 
+          break;        //No need to check other remote
+        }
+      rmtip=(char **)rou_freelist((void **)rmtip,(genfree_t)rou_freestr);
+      break;
+    case 8      :       //updating logs
+      if (scanref.logptr!=(LOGPTR *)0) {
+        long debut;
+        long fin;
+
+        scanref.logptr=log_closelog(scanref.logptr,&debut,&fin);
+        }
+      break;
+    case 9      :       //let continue
+      scanref.sqlptr=sql_closesql(scanref.sqlptr);
+      if (foreground==false)
+        phase=1;        //looping quickly to do next batch
+      break;
+    default     :       //SAFE Guard
+      (void) prc_locking(appname,false,1);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+dnsblk=(BLKTYP **)rou_freelist((void **)dnsblk,(genfree_t)dns_freeblk);
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Main routine                                    */
+/*             Start a channel to a remote ip.port     */
+/*              read file and transmit contecnts to     */
+/*              remote SMTP server.                     */
+/*                                                     */
+/********************************************************/
+int main(int argc,char *argv[])
+
+{
+#define OPEP            "scanner.c:main,"
+#ifdef  MODEDEBUG
+#define DOITAGAIN       -(60*10)        //10 minutes in seconds
+#else
+#define DOITAGAIN       -(3600*24)      //1 day in seconds
+#endif
+
+int status;
+ARGTYP *params;
+int phase;
+_Bool proceed;
+
+status=0;
+params=(ARGTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //checking parameters
+      if ((params=par_getparams(argc,argv,"c:d:D:fhi:r:v"))==(ARGTYP *)0) {
+        proceed=false;      //no need to go further
+        }
+      break;
+    case 1      :       //Preparing scan
+      (void) prc_preptitle(argc,argv,environ);
+      (void) rou_setappname(SCANNER);
+      (void) rou_modesubrou(true);
+      (void) prc_chgid(argv[0]);
+      (void) sig_modeunisig(true);
+      (void) sig_trapsignal(true,sig_alrm);
+      (void) rou_loadconfig(config,true);
+      break;
+    case 2      :       //checking if we need to go background
+      if (foreground==false) {
+        if (prc_divedivedive()!=0)
+          phase=999;    //direct exit
+        }
+      break;
+    case 3      :       //do program main purpose
+      (void) doscanning(DOITAGAIN);
+      break;
+    default     :       //end of task
+      params=par_freeparams(params);
+      (void) rou_loadconfig(config,false);
+      (void) sig_trapsignal(false,sig_alrm);
+      (void) sig_modeunisig(false);
+      (void) rou_modesubrou(false);
+      (void) prc_cleantitle();
+      (void) closelog();
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+(void) exit(status);
+
+#undef  TSLEEP
+#undef  OPEP
+}
diff --git a/app/scanner.d b/app/scanner.d
new file mode 100644 (file)
index 0000000..617fd51
--- /dev/null
@@ -0,0 +1,76 @@
+scanner.o scanner.d : scanner.c /usr/include/stdc-predef.h /usr/include/dirent.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/dirent.h \
+ /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h \
+ /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/dirent_ext.h /usr/include/errno.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/stdlib.h \
+ /usr/include/bits/libc-header-start.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/endian.h /usr/include/bits/endianness.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/syslog.h /usr/include/sys/syslog.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/syslog-path.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h \
+ ../lib/devsql.h ../lib/unisql.h ../lib/devlog.h /usr/include/stdio.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h ../lib/gessql.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ ../lib/unieml.h ../lib/subafn.h /usr/include/netdb.h \
+ /usr/include/netinet/in.h /usr/include/bits/stdint-uintn.h \
+ /usr/include/sys/socket.h /usr/include/bits/types/struct_iovec.h \
+ /usr/include/bits/socket.h /usr/include/bits/socket_type.h \
+ /usr/include/bits/sockaddr.h /usr/include/asm/socket.h \
+ /usr/include/asm-generic/socket.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h ../lib/devsql.h \
+ ../lib/unidns.h ../lib/unipar.h ../lib/uniprc.h ../lib/subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h ../lib/unisig.h \
+ /usr/include/signal.h /usr/include/bits/signum-generic.h \
+ /usr/include/bits/signum-arch.h /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/types/stack_t.h \
+ /usr/include/sys/ucontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigstksz.h /usr/include/bits/ss_flags.h \
+ /usr/include/bits/types/struct_sigstack.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h ../lib/subafn.h ../lib/subrou.h
diff --git a/app/sender.c b/app/sender.c
new file mode 100644 (file)
index 0000000..c519459
--- /dev/null
@@ -0,0 +1,373 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*      SMTP protocol sender.                           */
+/*     Take a a file with todo email to send.          */
+/*                                                      */
+/*      Format is:                                      */
+/*              sender [trans1 trabs2...]               */
+/*                                                     */
+/********************************************************/
+#include        <dirent.h>
+#include        <errno.h>
+#include        <stdlib.h>
+#include        <stdio.h>
+#include        <string.h>
+#include        <syslog.h>
+#include        <unistd.h>
+
+#include       "subrou.h"
+#include       "unieml.h"
+#include       "unipar.h"
+#include       "uniprc.h"
+#include       "unisig.h"
+#include       "geseml.h"
+#include       "gessql.h"
+#include       "lvleml.h"
+
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to update the database with respond   */
+/*      Record associated with TRA.                     */
+/*      Return the number of record stored within the   */
+/*      database.                                       */
+/*                                                      */
+/********************************************************/
+static int update_tradb(TRATYP **tra)
+
+{
+#define OPEP    "geseml.c:eml_update_tradb,"
+
+int num;
+
+num=0;
+if (tra!=(TRATYP **)0) {
+  SQLPTR *sqlptr;
+  int phase;
+  _Bool proceed;
+
+  sqlptr=(SQLPTR *)0;
+  phase=0;
+  proceed=(*tra!=(TRATYP *)0);
+  while (proceed==true) {
+    switch (phase) {
+      case 0    :       //opening the database
+        if ((sqlptr=sql_opensql())==(SQLPTR *)0) {
+          (void) rou_alert(0,"%s Unable to open database (system?)",OPEP);
+          phase=999;
+          }
+        break;
+      case 1    :       //checking all transation
+        (void) sql_update_tradb(sqlptr,tra);
+        break;
+      case 2    :       //closing the database
+        sqlptr=sql_closesql(sqlptr);
+        break;
+      default   :       //SAFE Guard
+        proceed=false;
+        break;
+      }
+    phase++;
+    }
+  }
+return num;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     procedure to dispatch email                     */
+/*                                                     */
+/********************************************************/
+static _Bool dispatcher(TRATYP **tra)
+
+{
+#define OPEP    "sender.c:dispatcher,"
+_Bool status;
+
+status=true;
+if (tra!=(TRATYP **)0) {
+  switch ((*tra)->code) {
+    case 'L'  :       //local delivery
+      (void) eml_local_email(tra);
+      break;
+    case 'R'  :       //Remote delivery
+      (void) eml_remote_email(tra);
+      break;
+    case 'W'  :       //Remote delivery
+      (void) eml_rebounce_email(*tra);
+      break;
+    default   :
+      (void) rou_alert(0,"%s unexpected code '%c'",OPEP,(*tra)->code);
+      status=false;
+      break;
+    }
+  }
+return status;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     procedure to scan all files                     */
+/*                                                     */
+/********************************************************/
+static _Bool scantodo(char *fname)
+
+{
+#define OPEP    "sender.c:scantodo,"
+
+_Bool status;
+FILE *qfile;
+TRATYP **tra;
+int phase;
+_Bool proceed;
+
+status=false;
+qfile=(FILE *)0;
+tra=(TRATYP **)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) fprintf(stdout,"%s JMPDBG phase='%d'\n",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Getting the list of file
+      if (eml_renameqfile(fname,EXTODO,EXDOING)==false) {
+        (void) rou_alert(0,"%s Unable to rename file <%s.%s>",OPEP,fname,EXDOING);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 1      :       //Building the "trans" list
+      if ((qfile=eml_openqfile(fname,EXDOING))==(FILE *)0) {
+        (void) rou_alert(0,"%s Unable to open file <%s.%s>",OPEP,fname,EXDOING);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 2      :       //reading the todo file
+      tra=eml_scanqfile(tra,qfile);
+      if (tra==(TRATYP **)0) { //No record found
+        (void) rou_alert(0,"%s No transport directive within <%s.%s> (Bug?)",
+                            OPEP,fname,EXDOING); 
+        phase=999;
+        }
+      break;
+    case 3      :       //dispatching email
+      if (dispatcher(tra)==false) {       //trouble backtracking file
+        (void) rou_alert(0,"%s JMPDBG <%s> dispatcher FALSE (Bug?)",OPEP,fname); 
+    
+        (void)eml_renameqfile(fname,EXDOING,EXTODO);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 4      :       //rewinding file before update
+      if (fseek(qfile,0,SEEK_SET)<0) {
+        (void) rou_alert(0,"%s Unable to rewing fname file <%s> (error=<%s> Bug?)",
+                            OPEP,fname,strerror(errno)); 
+        phase=999;      //Trouble trouble
+        }
+      break;
+    case 5      :       //updating transfile 
+      (void) eml_dump_list_tra(qfile,tra);
+      (void) update_tradb(tra);
+      tra=eml_freeall_tra(tra);
+      break;
+    case 6      :       //closing file
+      qfile=eml_closeqfile(qfile);
+      break;
+    case 7      :       //job completed renameing file
+      if (eml_renameqfile(fname,EXDOING,EXDONE)==false) {
+        (void) rou_alert(0,"%s Unable to rename file <%s.%s>",OPEP,fname,EXDONE);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 8      :       //job fully compltedt, good
+      status=true;
+      break;
+    default     :       //SAFE Guard
+      qfile=eml_closeqfile(qfile);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     procedure to can a list of todo file.           */
+/*                                                     */
+/********************************************************/
+static void sending(int argc,char *argv[])
+
+{
+#define OPEP    "sender.c:sending,"
+#define TODO    "todo"
+
+unsigned long cycle;
+char **list;
+int delay;
+int phase;
+_Bool proceed;
+
+cycle=0;
+list=(char **)0;
+delay=20;
+if (debug>0)
+  delay/=10;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Build list from argv;
+      if (argc>0) {
+        for (int i=0;i<argc;i++) 
+          list=(char **)rou_addlist((void **)list,(void *)strdup(argv[i]));
+        phase++;        //No nee to build list
+        foreground=true;//on pass only
+        }
+      break;
+    case 1      :       //building the list according directory contents
+      list=eml_getqfilelist(list,TODO);
+      break;
+    case 2      :       //do we have a list
+      if (list!=(char **)0) {
+        int num;
+        char **scan;
+
+        num=0;
+        scan=list;
+        while (*scan!=(char *)0) {
+          char *fname;
+          char *ptr;
+
+          fname=*scan;
+          (void) prc_settitle("%s:%s, sending <%s>",APPNAME,appname,fname);
+          (void) rou_alert(0,"%s fname file <%s>",OPEP,fname);
+          if ((ptr=strrchr(fname,'.'))==(char *)0) {
+            (void) rou_alert(0,"%s unable to find <%s> extension",OPEP,fname);
+            break;
+            }
+          *ptr='\000';
+          ptr++;
+          if (strcmp(ptr,EXTODO)!=0) {
+            (void) rou_alert(0,"%s wrong extension for file <%s.%s>",
+                                OPEP,fname,ptr);
+            break;
+            }
+          (void) rou_alert(0,"%s scanning file <%s>",appname,fname);
+          if (scantodo(fname)==false)
+            break;
+          scan++;
+          num++;
+          }
+        list=(char **)rou_freelist((void **)list,(genfree_t)rou_freestr);
+        (void) rou_alert(0,"%s Scanned '%d' files",OPEP,num);
+        (void) usleep(10000);
+        phase=0;
+        }
+      break;
+    case 3      :       //do we need to loop again
+      phase=0;          //Lets say looping requested
+      cycle++;
+      (void) prc_settitle("%s:%s, sleeping mode (cycle=%08d)",
+                          APPNAME,appname,cycle);
+      (void) sleep(delay);
+      if ((hangup==true)||((reload==true))) {
+        (void) rou_alert(0,"%s got hangup or reload signal",OPEP);
+        phase=999;
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+
+#undef  TODO
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Main routine                                    */
+/*             Start a channel to a remote ip.port     */
+/*              read file and transmit contecnts to     */
+/*              remote SMTP server.                     */
+/*                                                     */
+/********************************************************/
+int main(int argc,char *argv[])
+
+{
+#define OPEP    "sender.c:main,"
+
+int status;
+ARGTYP *params;
+int phase;
+_Bool proceed;
+
+status=0;
+params=(ARGTYP *)0;
+foreground=false;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) fprintf(stdout,"%s (pid=%08d) JMPDBG phase='%d'\n",OPEP,getpid(),phase);
+  switch (phase) {
+    case 0      :       //checking parameters
+      if ((params=par_getparams(argc,argv,"c:d:D:fh:r:v"))==(ARGTYP *)0) {
+        proceed=false;      //no need to go further
+        }
+      break;
+    case 1      :       //Preparing scan
+      (void) prc_preptitle(argc,argv,environ);
+      (void) rou_setappname(SENDER);
+      (void) rou_modesubrou(true);
+      (void) sig_modeunisig(true);
+      (void) prc_chgid(argv[0]);
+      (void) rou_loadconfig(config,true);
+      if (foreground==false) {
+        if (prc_divedivedive()!=0)
+          phase=999;    //direct exit
+        }
+      break;
+    case 2      :       //locking process
+      if (prc_locking(appname,true,5)==false) {
+        (void) rou_alert(0,"%s Unable to lock %s exclusif acccess (system?)",
+                            OPEP,appname);
+        proceed=false;
+        }
+      break;
+    case 3      :       //sending email
+      (void) sending(params->argc,params->argv);
+      break;
+    case 4      :       //Unlocking access
+      (void) prc_locking(appname,false,1);
+      break;
+    default     :       //end of task
+      (void) prc_cleantitle();
+      params=par_freeparams(params);
+      (void)  rou_loadconfig(config,false);
+      (void) sig_modeunisig(false);
+      (void) rou_modesubrou(false);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+(void) exit(status);
+}
diff --git a/app/sender.d b/app/sender.d
new file mode 100644 (file)
index 0000000..ad26278
--- /dev/null
@@ -0,0 +1,123 @@
+sender.o sender.d : sender.c /usr/include/stdc-predef.h /usr/include/dirent.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/dirent.h \
+ /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h \
+ /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/dirent_ext.h /usr/include/errno.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/stdlib.h \
+ /usr/include/bits/libc-header-start.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/endian.h /usr/include/bits/endianness.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/stdio.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/syslog.h /usr/include/sys/syslog.h \
+ /usr/include/bits/syslog-path.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h \
+ ../lib/subrou.h /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h ../lib/unieml.h \
+ ../lib/subafn.h /usr/include/netdb.h /usr/include/netinet/in.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h /usr/include/bits/socket.h \
+ /usr/include/bits/socket_type.h /usr/include/bits/sockaddr.h \
+ /usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h ../lib/unipar.h \
+ ../lib/uniprc.h ../lib/subrou.h ../lib/unisig.h /usr/include/signal.h \
+ /usr/include/bits/signum-generic.h /usr/include/bits/signum-arch.h \
+ /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/types/stack_t.h \
+ /usr/include/sys/ucontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigstksz.h /usr/include/bits/ss_flags.h \
+ /usr/include/bits/types/struct_sigstack.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h ../lib/geseml.h ../lib/unisql.h \
+ ../lib/gessql.h ../lib/unieml.h ../lib/devsql.h ../lib/lvleml.h \
+ ../lib/devsoc.h ../lib/unitls.h /usr/include/openssl/ssl.h \
+ /usr/include/openssl/macros.h /usr/include/openssl/opensslconf.h \
+ /usr/include/openssl/configuration.h /usr/include/openssl/opensslv.h \
+ /usr/include/openssl/e_os2.h /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-least.h /usr/include/openssl/e_ostime.h \
+ /usr/include/sys/time.h /usr/include/openssl/comp.h \
+ /usr/include/openssl/crypto.h /usr/include/openssl/safestack.h \
+ /usr/include/openssl/stack.h /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h /usr/include/openssl/core.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/comperr.h /usr/include/openssl/bio.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/x509.h \
+ /usr/include/openssl/buffer.h /usr/include/openssl/buffererr.h \
+ /usr/include/openssl/evp.h /usr/include/openssl/core_dispatch.h \
+ /usr/include/openssl/indicator.h /usr/include/openssl/params.h \
+ /usr/include/openssl/bn.h /usr/include/openssl/bnerr.h \
+ /usr/include/openssl/evperr.h /usr/include/openssl/objects.h \
+ /usr/include/openssl/obj_mac.h /usr/include/openssl/asn1.h \
+ /usr/include/openssl/asn1err.h /usr/include/openssl/objectserr.h \
+ /usr/include/openssl/ec.h /usr/include/openssl/ecerr.h \
+ /usr/include/openssl/rsa.h /usr/include/openssl/rsaerr.h \
+ /usr/include/openssl/dsa.h /usr/include/openssl/dh.h \
+ /usr/include/openssl/dherr.h /usr/include/openssl/dsaerr.h \
+ /usr/include/openssl/sha.h /usr/include/openssl/x509err.h \
+ /usr/include/openssl/x509_vfy.h /usr/include/openssl/lhash.h \
+ /usr/include/openssl/pkcs7.h /usr/include/openssl/pkcs7err.h \
+ /usr/include/openssl/http.h /usr/include/openssl/conf.h \
+ /usr/include/openssl/conferr.h /usr/include/openssl/conftypes.h \
+ /usr/include/openssl/pem.h /usr/include/openssl/pemerr.h \
+ /usr/include/openssl/hmac.h /usr/include/openssl/async.h \
+ /usr/include/openssl/asyncerr.h /usr/include/openssl/ct.h \
+ /usr/include/openssl/cterr.h /usr/include/openssl/sslerr.h \
+ /usr/include/openssl/sslerr_legacy.h /usr/include/openssl/prov_ssl.h \
+ /usr/include/openssl/ssl2.h /usr/include/openssl/ssl3.h \
+ /usr/include/openssl/tls1.h /usr/include/openssl/dtls1.h \
+ /usr/include/openssl/srtp.h /usr/include/openssl/quic.h ../lib/gestcp.h \
+ ../lib/unidns.h ../lib/devlog.h ../lib/gesspf.h ../lib/geseml.h
diff --git a/app/sorter.c b/app/sorter.c
new file mode 100644 (file)
index 0000000..2c0e19f
--- /dev/null
@@ -0,0 +1,236 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*      SMTP protocol domain destination sorter         */
+/*     Used to take the trans directive and generate   */
+/*      a todo list a email to be send.                 */
+/*                                                      */
+/*                                                     */
+/********************************************************/
+#include        <dirent.h>
+#include        <stdlib.h>
+#include        <syslog.h>
+#include        <unistd.h>
+
+#include       "subrou.h"
+#include       "unidns.h"
+#include       "unieml.h"
+#include       "unipar.h"
+#include       "uniprc.h"
+#include       "unisig.h"
+#include       "geseml.h"
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     procedure to scan all files                     */
+/*                                                     */
+/********************************************************/
+static _Bool scantrans(const char *ext,_Bool todo)
+
+{
+#define OPEP    "sorter.c:scantrans,"
+
+_Bool action;
+char **fname;
+TRATYP **trans;
+int phase;
+_Bool proceed;
+
+action=false;
+fname=(char **)0;
+trans=(TRATYP **)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) fprintf(stdout,"%s JMPDBG phase='%d' ext=<%s>\n",OPEP,phase,ext);
+  switch (phase) {
+    case 0      :       //Getting the list of file
+      (void) prc_settitle("%s:%s, scanning ext=%s",APPNAME,appname,ext);
+      fname=eml_getqfilelist(fname,ext);
+      if (fname==(char **)0)
+        phase=999;      //Nothing to do
+      break;
+    case 1      :       //Building the "trans" list
+      action=true;
+      if (fname!=(char **)0) {  //always
+        char **ptr;
+
+        ptr=fname; 
+        while (*ptr!=(char *)0) {
+          FILE *qfile;
+
+          (void) rou_alert(5,"%s fame=<%s>",OPEP,*ptr);
+          if ((qfile=eml_openqfile(*ptr,(const char *)0))!=(FILE *)0) {
+            trans=eml_scanqfile(trans,qfile);
+            (void) eml_closeqfile(qfile);
+            (void) eml_deleteqfile(*ptr);
+            }
+          ptr++;
+          }
+        fname=(char **)rou_freelist((void *)fname,(genfree_t)rou_freestr); 
+        }
+      break;
+    case 2      :       //scanning translit
+      switch (todo) {
+        case true     :       //Doing todo file
+          (void) eml_todoqfile(trans);
+          break;
+        case false    :
+          (void) eml_doneqfile(trans);
+          break;
+        }
+      trans=eml_freeall_tra(trans);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+#undef  OPEP
+return action;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     sorter central task                             */
+/*                                                     */
+/********************************************************/
+static void sorting()
+
+{
+#define OPEP    "sorter.c:sorting,"
+#define TSLEEP  10
+
+unsigned long cycle;
+_Bool action;
+int delay;
+int phase;
+_Bool proceed;
+
+cycle=0;
+action=false;
+delay=1;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Locking access
+      if (prc_locking(appname,true,5)==false) {
+        (void) rou_alert(0,"%s Unable to lock %s exclusif acccess (system?)",
+                            OPEP,appname);
+        proceed=false;
+        }
+      break;
+    case 1      :       //got signal
+      action=false;
+      if ((hangup==true)||((reload==true))) {
+        (void) rou_alert(0,"%s got hangup or reload signal",OPEP);
+        phase=999;
+        }
+      break;
+    case 2      :       //got signal
+      action|=scantrans(EXTRANS,true);
+      break;
+    case 3      :       //got signal
+      action|=scantrans(EXDONE,false);
+      break;
+    case 4      :       //one passe only?
+      if (foreground==true)
+        phase=999;      //Immediate ending
+      break;
+    case 5      :       //lets time pass according "busy"
+      cycle++;
+      delay=TSLEEP;
+      if (action==true)
+        delay=1;
+      (void) prc_settitle("%s:%s, sleeping mode (cycle=%08d)",
+                          APPNAME,appname,cycle);
+      (void) sleep(delay);
+      phase=0;          //Looping
+      break;
+    default     :       //SAFE Guard
+      (void) prc_locking(appname,false,1);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+
+#undef OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Main routine                                    */
+/*             Start a channel to a remote ip.port     */
+/*              read file and transmit contecnts to     */
+/*              remote SMTP server.                     */
+/*                                                     */
+/********************************************************/
+int main(int argc,char *argv[])
+
+{
+#define OPEP    "sorter.c:main,"
+
+int status;
+ARGTYP *params;
+int phase;
+_Bool proceed;
+
+status=0;
+params=(ARGTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //checking parameters
+      if ((params=par_getparams(argc,argv,"c:d:D:fhi:r:v"))==(ARGTYP *)0) {
+        proceed=false;      //no need to go further
+        }
+      break;
+    case 1      :       //Preparing scan
+      (void) prc_preptitle(argc,argv,environ);
+      (void) rou_setappname(SORTER);
+      (void) rou_modesubrou(true);
+      (void) prc_chgid(argv[0]);
+      (void) sig_modeunisig(true);
+      (void) sig_trapsignal(true,sig_alrm);
+      (void) rou_loadconfig(config,true);
+      break;
+    case 2      :       //checking if we need to go background
+      if (foreground==false) {
+        if (prc_divedivedive()!=0)
+          phase=999;    //direct exit
+        }
+      break;
+    case 3      :       //do program main purpose
+      (void) sorting();
+      break;
+    default     :       //end of task
+      (void) prc_cleantitle();
+      params=par_freeparams(params);
+      (void) rou_loadconfig(config,false);
+      (void) sig_trapsignal(false,sig_alrm);
+      (void) sig_modeunisig(false);
+      (void) rou_modesubrou(false);
+      (void) closelog();
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+(void) exit(status);
+
+#undef  TSLEEP
+#undef  OPEP
+}
diff --git a/app/sorter.d b/app/sorter.d
new file mode 100644 (file)
index 0000000..5a087d6
--- /dev/null
@@ -0,0 +1,74 @@
+sorter.o sorter.d : sorter.c /usr/include/stdc-predef.h /usr/include/dirent.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/dirent.h \
+ /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h \
+ /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/dirent_ext.h /usr/include/stdlib.h \
+ /usr/include/bits/libc-header-start.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/endian.h /usr/include/bits/endianness.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/syslog.h \
+ /usr/include/sys/syslog.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/syslog-path.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h \
+ ../lib/subrou.h /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ ../lib/unidns.h ../lib/subafn.h /usr/include/netdb.h \
+ /usr/include/netinet/in.h /usr/include/bits/stdint-uintn.h \
+ /usr/include/sys/socket.h /usr/include/bits/types/struct_iovec.h \
+ /usr/include/bits/socket.h /usr/include/bits/socket_type.h \
+ /usr/include/bits/sockaddr.h /usr/include/asm/socket.h \
+ /usr/include/asm-generic/socket.h /usr/include/asm/sockios.h \
+ /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h ../lib/unieml.h \
+ /usr/include/stdio.h /usr/include/bits/types/__fpos_t.h \
+ /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h ../lib/unipar.h ../lib/uniprc.h \
+ ../lib/subrou.h ../lib/unisig.h /usr/include/signal.h \
+ /usr/include/bits/signum-generic.h /usr/include/bits/signum-arch.h \
+ /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/types/stack_t.h \
+ /usr/include/sys/ucontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigstksz.h /usr/include/bits/ss_flags.h \
+ /usr/include/bits/types/struct_sigstack.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h ../lib/geseml.h ../lib/unisql.h
diff --git a/app/toremake b/app/toremake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bin b/bin
new file mode 120000 (symlink)
index 0000000..a311833
--- /dev/null
+++ b/bin
@@ -0,0 +1 @@
+bin-posql
\ No newline at end of file
diff --git a/certs/localhost-cert.pem b/certs/localhost-cert.pem
new file mode 100644 (file)
index 0000000..12e130d
--- /dev/null
@@ -0,0 +1,147 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            2a:01:e0:a5:fb:80:80:00:00:00:29
+        Signature Algorithm: sha384WithRSAEncryption
+        Issuer: C=CA, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Midle Ground CA (2024) - SHA384 - 3
+        Validity
+            Not Before: Apr  6 13:59:06 2025 GMT
+            Not After : Apr  6 13:59:06 2050 GMT
+        Subject: C=CA, ST=Quebec, L=Montreal, O=SAFE Inc., OU=Mailleur email developpement test, CN=localhost.localdomain
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:ba:5f:35:d2:04:d5:f4:da:f0:b1:99:6f:f3:42:
+                    42:0e:59:8d:7b:6c:24:97:6d:24:df:e8:fa:83:7c:
+                    ae:92:fd:d1:58:1c:14:7e:20:71:5e:44:5a:b0:e8:
+                    7a:ce:45:33:95:aa:27:ef:52:fe:bd:5a:23:7b:4f:
+                    8b:24:4d:0b:f7:9c:99:25:b8:b9:af:8c:46:f7:a9:
+                    6c:18:ff:39:7c:a9:ff:9a:f5:f0:d7:d7:ca:dc:6c:
+                    5d:c7:09:02:83:87:37:1a:2f:f7:05:b8:39:af:9f:
+                    ab:24:3b:24:48:e6:72:af:36:10:0d:c6:dc:bd:c2:
+                    de:05:4f:4a:fd:d8:5b:35:24:b9:bc:ce:ab:37:3a:
+                    f9:ff:2d:44:c2:33:f2:52:4a:36:5c:5f:80:a7:cd:
+                    f7:1b:6d:55:b7:d6:13:25:72:d5:55:4d:54:ff:a1:
+                    1e:7d:85:8b:e0:e2:16:d1:d1:22:21:07:99:ad:9a:
+                    51:25:02:71:7b:56:e5:77:16:07:18:c6:fd:de:c4:
+                    e0:e5:55:01:78:f6:3c:53:ab:35:1d:44:f8:26:af:
+                    c3:11:a2:2d:63:73:29:c5:bd:b4:17:f7:83:7e:0d:
+                    d9:0a:d9:a1:27:f2:7c:bc:ee:95:76:68:ec:c4:7f:
+                    35:64:44:d4:6d:43:46:99:40:52:cd:b4:c0:11:3a:
+                    d7:ab:7c:cf:87:b5:41:32:a4:23:2d:3f:cc:fa:b2:
+                    6b:5b:dd:a5:58:14:7a:24:cb:3a:26:04:49:8f:07:
+                    8b:5b:d0:be:c2:ee:24:de:d1:74:cb:04:48:be:f9:
+                    74:5a:17:52:1e:0e:c0:ea:02:d1:7a:1e:e7:a2:95:
+                    b4:77:1a:96:6d:34:80:78:85:0f:84:e6:3b:60:27:
+                    75:5d:33:60:6d:6b:d9:da:b9:af:a9:cf:bc:ae:c3:
+                    29:ca:a3:1b:4f:3b:7f:fa:ba:d6:01:f0:07:3f:7d:
+                    00:45:e6:6e:7f:aa:67:ad:90:db:1d:2a:eb:e4:be:
+                    43:13:5e:2e:dc:de:a1:36:fd:20:90:1f:ac:8c:3c:
+                    c9:0e:32:96:ef:a8:19:1d:30:87:d1:f2:42:c7:55:
+                    e7:46:24:9c:d2:4a:1f:42:01:f7:eb:68:5f:d6:b5:
+                    9a:3a:e2:51:90:94:59:9d:bc:83:dd:d8:89:e9:34:
+                    af:d6:43:66:8f:87:85:2b:c6:b1:43:b6:09:92:ba:
+                    f4:e3:d6:3b:c1:5b:2d:76:cd:56:ac:7f:bb:fb:60:
+                    30:a5:13:76:73:35:5e:67:df:dd:c5:fe:ab:e5:4f:
+                    dd:80:56:19:a6:e0:d8:53:b2:20:5c:5c:34:2f:a1:
+                    31:0b:12:27:71:b6:ea:5b:2e:fa:a3:88:2f:f7:24:
+                    5b:93:0d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            Authority Information Access: 
+                CA Issuers - URI:http://certificates.safe.ca/cacert/safeMDL.pem
+                OCSP - URI:http://certificates/safe.ca/chkcertstats
+            X509v3 Certificate Policies: 
+                Policy: 1.3.6.1.4.1.7438.1.2
+                  CPS: http://certificates.safe.ca/policies
+                Policy: 2.23.140.1.1
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 CRL Distribution Points: 
+                Full Name:
+                  URI:http://certificates.safe.ca/repository/revoklist.pem
+            X509v3 Subject Alternative Name: 
+                DNS:localhost.localdomain, IP Address:127.127.0.1
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection
+            X509v3 Subject Key Identifier: 
+                2D:BD:65:E1:4E:2B:7D:E0:80:3E:D2:66:6E:ED:81:87:4A:52:CD:6C
+            X509v3 Authority Key Identifier: 
+                9C:BE:0B:C0:22:76:F5:CF:BC:FD:78:9A:92:77:20:FE:BF:96:1E:D8
+    Signature Algorithm: sha384WithRSAEncryption
+    Signature Value:
+        ac:e8:1e:50:ec:0e:b8:02:c6:ac:70:19:ee:37:7f:53:24:69:
+        cc:ca:6e:34:6b:9b:14:cf:71:67:bb:7d:f9:60:a7:92:59:9b:
+        de:56:8b:86:9a:8e:59:da:ad:4a:90:a4:2e:2d:c4:a0:17:01:
+        18:84:c4:dd:5c:93:97:dd:d9:ad:d4:0d:63:33:3b:4e:3f:24:
+        0e:6c:13:6c:3f:3b:5e:ca:27:a1:5a:1c:30:e4:eb:da:d0:e5:
+        b1:4f:ce:fb:c4:8c:76:da:e6:f6:00:a5:03:58:9b:6d:79:84:
+        92:2c:10:66:96:b9:7f:73:57:97:7e:6a:f2:74:d3:ac:0a:3e:
+        b2:11:c4:f5:1e:4d:87:1f:8e:6d:29:ed:ef:97:8f:70:f7:37:
+        61:3e:ae:4a:d6:6d:6e:80:c5:bb:de:9b:bd:06:bb:a8:94:28:
+        ed:f6:c9:4a:7f:e2:9a:44:e1:96:07:25:60:74:19:d1:fb:86:
+        32:16:1c:c4:99:dd:de:ab:fe:7a:88:af:8e:3a:fa:36:c3:92:
+        b3:82:ba:50:18:02:42:2c:b4:6a:d2:ba:a3:8e:fb:72:6a:d1:
+        8a:b4:3f:b3:9e:27:90:18:b8:50:04:b8:1e:14:d6:e1:98:ff:
+        ed:78:5d:5e:76:b0:6e:8d:e0:ea:e3:00:5e:c7:f3:eb:ed:71:
+        c6:c0:de:f1:e2:bb:03:14:f1:27:0f:a6:2e:6c:38:0a:ca:3e:
+        ef:e1:4c:d8:a4:dc:7d:6a:ec:e5:3e:b5:a7:53:7b:2a:32:76:
+        da:a8:e1:1b:8c:76:6b:8b:b8:75:51:65:25:e0:c2:31:c7:0b:
+        b1:a6:a2:10:b7:45:4d:fc:69:67:84:c6:81:c8:e7:5e:b8:fc:
+        fb:8d:64:e3:28:dc:b3:41:be:8e:58:7a:8e:9e:89:ee:51:f1:
+        bf:5e:82:a6:29:a8:98:fe:ef:fb:7c:70:f7:8c:ee:4e:07:47:
+        10:4e:75:ad:21:a2:ad:9a:4e:e7:3a:01:2e:bc:81:63:b7:7c:
+        4c:ea:32:4c:12:78:20:81:9d:f2:8d:5f:1f:4b:82:67:55:1f:
+        95:ff:d8:7f:5b:50:74:fd:18:4c:74:3e:4e:cf:5b:26:d6:73:
+        29:ea:dd:9b:a0:a5:0b:5d:5e:0d:36:fd:f5:d1:d0:91:9a:6e:
+        81:b9:4a:93:d7:94:bb:75:e5:fa:ff:9f:5f:1e:f4:d3:63:9e:
+        09:03:00:b5:aa:77:0d:9f:2f:25:41:99:f3:52:04:08:3d:f4:
+        d5:ab:37:3a:0c:1b:fe:db:66:45:8b:b3:88:7e:1d:9c:98:98:
+        a7:f7:00:30:06:78:f6:0e:ea:aa:e6:24:3d:3f:31:39:80:3e:
+        a8:74:a2:1e:42:51:d7:13
+-----BEGIN CERTIFICATE-----
+MIIHZDCCBUygAwIBAgILKgHgpfuAgAAAACkwDQYJKoZIhvcNAQEMBQAwejELMAkG
+A1UEBhMCQ0ExEjAQBgNVBAoMCVNBRkUgSW5jLjEkMCIGA1UECwwbRGlnaXRhbCBD
+ZXJ0aWZpY2F0ZSBTaWduaW5nMTEwLwYDVQQDDChTQUZFIE1pZGxlIEdyb3VuZCBD
+QSAoMjAyNCkgLSBTSEEzODQgLSAzMCAXDTI1MDQwNjEzNTkwNloYDzIwNTAwNDA2
+MTM1OTA2WjCBkTELMAkGA1UEBhMCQ0ExDzANBgNVBAgMBlF1ZWJlYzERMA8GA1UE
+BwwITW9udHJlYWwxEjAQBgNVBAoMCVNBRkUgSW5jLjEqMCgGA1UECwwhTWFpbGxl
+dXIgZW1haWwgZGV2ZWxvcHBlbWVudCB0ZXN0MR4wHAYDVQQDDBVsb2NhbGhvc3Qu
+bG9jYWxkb21haW4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC6XzXS
+BNX02vCxmW/zQkIOWY17bCSXbSTf6PqDfK6S/dFYHBR+IHFeRFqw6HrORTOVqifv
+Uv69WiN7T4skTQv3nJkluLmvjEb3qWwY/zl8qf+a9fDX18rcbF3HCQKDhzcaL/cF
+uDmvn6skOyRI5nKvNhANxty9wt4FT0r92Fs1JLm8zqs3Ovn/LUTCM/JSSjZcX4Cn
+zfcbbVW31hMlctVVTVT/oR59hYvg4hbR0SIhB5mtmlElAnF7VuV3FgcYxv3exODl
+VQF49jxTqzUdRPgmr8MRoi1jcynFvbQX94N+DdkK2aEn8ny87pV2aOzEfzVkRNRt
+Q0aZQFLNtMAROterfM+HtUEypCMtP8z6smtb3aVYFHokyzomBEmPB4tb0L7C7iTe
+0XTLBEi++XRaF1IeDsDqAtF6HueilbR3GpZtNIB4hQ+E5jtgJ3VdM2Bta9naua+p
+z7yuwynKoxtPO3/6utYB8Ac/fQBF5m5/qmetkNsdKuvkvkMTXi7c3qE2/SCQH6yM
+PMkOMpbvqBkdMIfR8kLHVedGJJzSSh9CAffraF/WtZo64lGQlFmdvIPd2InpNK/W
+Q2aPh4UrxrFDtgmSuvTj1jvBWy12zVasf7v7YDClE3ZzNV5n393F/qvlT92AVhmm
+4NhTsiBcXDQvoTELEidxtupbLvqjiC/3JFuTDQIDAQABo4IBzzCCAcswDgYDVR0P
+AQH/BAQDAgWgMIGABggrBgEFBQcBAQR0MHIwOgYIKwYBBQUHMAKGLmh0dHA6Ly9j
+ZXJ0aWZpY2F0ZXMuc2FmZS5jYS9jYWNlcnQvc2FmZU1ETC5wZW0wNAYIKwYBBQUH
+MAGGKGh0dHA6Ly9jZXJ0aWZpY2F0ZXMvc2FmZS5jYS9jaGtjZXJ0c3RhdHMwUwYD
+VR0gBEwwSjA/BgkrBgEEAboOAQIwMjAwBggrBgEFBQcCARYkaHR0cDovL2NlcnRp
+ZmljYXRlcy5zYWZlLmNhL3BvbGljaWVzMAcGBWeBDAEBMAkGA1UdEwQCMAAwRQYD
+VR0fBD4wPDA6oDigNoY0aHR0cDovL2NlcnRpZmljYXRlcy5zYWZlLmNhL3JlcG9z
+aXRvcnkvcmV2b2tsaXN0LnBlbTAmBgNVHREEHzAdghVsb2NhbGhvc3QubG9jYWxk
+b21haW6HBH9/AAEwJwYDVR0lBCAwHgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEF
+BQcDBDAdBgNVHQ4EFgQULb1l4U4rfeCAPtJmbu2Bh0pSzWwwHwYDVR0jBBgwFoAU
+nL4LwCJ29c+8/Xiakncg/r+WHtgwDQYJKoZIhvcNAQEMBQADggIBAKzoHlDsDrgC
+xqxwGe43f1MkaczKbjRrmxTPcWe7fflgp5JZm95Wi4aajlnarUqQpC4txKAXARiE
+xN1ck5fd2a3UDWMzO04/JA5sE2w/O17KJ6FaHDDk69rQ5bFPzvvEjHba5vYApQNY
+m215hJIsEGaWuX9zV5d+avJ006wKPrIRxPUeTYcfjm0p7e+Xj3D3N2E+rkrWbW6A
+xbvem70Gu6iUKO32yUp/4ppE4ZYHJWB0GdH7hjIWHMSZ3d6r/nqIr446+jbDkrOC
+ulAYAkIstGrSuqOO+3Jq0Yq0P7OeJ5AYuFAEuB4U1uGY/+14XV52sG6N4OrjAF7H
+8+vtccbA3vHiuwMU8ScPpi5sOArKPu/hTNik3H1q7OU+tadTeyoydtqo4RuMdmuL
+uHVRZSXgwjHHC7GmohC3RU38aWeExoHI5164/PuNZOMo3LNBvo5Yeo6eie5R8b9e
+gqYpqJj+7/t8cPeM7k4HRxBOda0hoq2aTuc6AS68gWO3fEzqMkwSeCCBnfKNXx9L
+gmdVH5X/2H9bUHT9GEx0Pk7PWybWcynq3ZugpQtdXg02/fXR0JGaboG5SpPXlLt1
+5fr/n18e9NNjngkDALWqdw2fLyVBmfNSBAg99NWrNzoMG/7bZkWLs4h+HZyYmKf3
+ADAGePYO6qrmJD0/MTmAPqh0oh5CUdcT
+-----END CERTIFICATE-----
diff --git a/certs/localhost-chain-cert.pem b/certs/localhost-chain-cert.pem
new file mode 100644 (file)
index 0000000..89ac0c5
--- /dev/null
@@ -0,0 +1,257 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            2a:01:e0:a5:fb:80:80:00:00:00:29
+        Signature Algorithm: sha384WithRSAEncryption
+        Issuer: C=CA, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Midle Ground CA (2024) - SHA384 - 3
+        Validity
+            Not Before: Apr  6 13:59:06 2025 GMT
+            Not After : Apr  6 13:59:06 2050 GMT
+        Subject: C=CA, ST=Quebec, L=Montreal, O=SAFE Inc., OU=Mailleur email developpement test, CN=localhost.localdomain
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:ba:5f:35:d2:04:d5:f4:da:f0:b1:99:6f:f3:42:
+                    42:0e:59:8d:7b:6c:24:97:6d:24:df:e8:fa:83:7c:
+                    ae:92:fd:d1:58:1c:14:7e:20:71:5e:44:5a:b0:e8:
+                    7a:ce:45:33:95:aa:27:ef:52:fe:bd:5a:23:7b:4f:
+                    8b:24:4d:0b:f7:9c:99:25:b8:b9:af:8c:46:f7:a9:
+                    6c:18:ff:39:7c:a9:ff:9a:f5:f0:d7:d7:ca:dc:6c:
+                    5d:c7:09:02:83:87:37:1a:2f:f7:05:b8:39:af:9f:
+                    ab:24:3b:24:48:e6:72:af:36:10:0d:c6:dc:bd:c2:
+                    de:05:4f:4a:fd:d8:5b:35:24:b9:bc:ce:ab:37:3a:
+                    f9:ff:2d:44:c2:33:f2:52:4a:36:5c:5f:80:a7:cd:
+                    f7:1b:6d:55:b7:d6:13:25:72:d5:55:4d:54:ff:a1:
+                    1e:7d:85:8b:e0:e2:16:d1:d1:22:21:07:99:ad:9a:
+                    51:25:02:71:7b:56:e5:77:16:07:18:c6:fd:de:c4:
+                    e0:e5:55:01:78:f6:3c:53:ab:35:1d:44:f8:26:af:
+                    c3:11:a2:2d:63:73:29:c5:bd:b4:17:f7:83:7e:0d:
+                    d9:0a:d9:a1:27:f2:7c:bc:ee:95:76:68:ec:c4:7f:
+                    35:64:44:d4:6d:43:46:99:40:52:cd:b4:c0:11:3a:
+                    d7:ab:7c:cf:87:b5:41:32:a4:23:2d:3f:cc:fa:b2:
+                    6b:5b:dd:a5:58:14:7a:24:cb:3a:26:04:49:8f:07:
+                    8b:5b:d0:be:c2:ee:24:de:d1:74:cb:04:48:be:f9:
+                    74:5a:17:52:1e:0e:c0:ea:02:d1:7a:1e:e7:a2:95:
+                    b4:77:1a:96:6d:34:80:78:85:0f:84:e6:3b:60:27:
+                    75:5d:33:60:6d:6b:d9:da:b9:af:a9:cf:bc:ae:c3:
+                    29:ca:a3:1b:4f:3b:7f:fa:ba:d6:01:f0:07:3f:7d:
+                    00:45:e6:6e:7f:aa:67:ad:90:db:1d:2a:eb:e4:be:
+                    43:13:5e:2e:dc:de:a1:36:fd:20:90:1f:ac:8c:3c:
+                    c9:0e:32:96:ef:a8:19:1d:30:87:d1:f2:42:c7:55:
+                    e7:46:24:9c:d2:4a:1f:42:01:f7:eb:68:5f:d6:b5:
+                    9a:3a:e2:51:90:94:59:9d:bc:83:dd:d8:89:e9:34:
+                    af:d6:43:66:8f:87:85:2b:c6:b1:43:b6:09:92:ba:
+                    f4:e3:d6:3b:c1:5b:2d:76:cd:56:ac:7f:bb:fb:60:
+                    30:a5:13:76:73:35:5e:67:df:dd:c5:fe:ab:e5:4f:
+                    dd:80:56:19:a6:e0:d8:53:b2:20:5c:5c:34:2f:a1:
+                    31:0b:12:27:71:b6:ea:5b:2e:fa:a3:88:2f:f7:24:
+                    5b:93:0d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            Authority Information Access: 
+                CA Issuers - URI:http://certificates.safe.ca/cacert/safeMDL.pem
+                OCSP - URI:http://certificates/safe.ca/chkcertstats
+            X509v3 Certificate Policies: 
+                Policy: 1.3.6.1.4.1.7438.1.2
+                  CPS: http://certificates.safe.ca/policies
+                Policy: 2.23.140.1.1
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 CRL Distribution Points: 
+                Full Name:
+                  URI:http://certificates.safe.ca/repository/revoklist.pem
+            X509v3 Subject Alternative Name: 
+                DNS:localhost.localdomain, IP Address:127.127.0.1
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection
+            X509v3 Subject Key Identifier: 
+                2D:BD:65:E1:4E:2B:7D:E0:80:3E:D2:66:6E:ED:81:87:4A:52:CD:6C
+            X509v3 Authority Key Identifier: 
+                9C:BE:0B:C0:22:76:F5:CF:BC:FD:78:9A:92:77:20:FE:BF:96:1E:D8
+    Signature Algorithm: sha384WithRSAEncryption
+    Signature Value:
+        ac:e8:1e:50:ec:0e:b8:02:c6:ac:70:19:ee:37:7f:53:24:69:
+        cc:ca:6e:34:6b:9b:14:cf:71:67:bb:7d:f9:60:a7:92:59:9b:
+        de:56:8b:86:9a:8e:59:da:ad:4a:90:a4:2e:2d:c4:a0:17:01:
+        18:84:c4:dd:5c:93:97:dd:d9:ad:d4:0d:63:33:3b:4e:3f:24:
+        0e:6c:13:6c:3f:3b:5e:ca:27:a1:5a:1c:30:e4:eb:da:d0:e5:
+        b1:4f:ce:fb:c4:8c:76:da:e6:f6:00:a5:03:58:9b:6d:79:84:
+        92:2c:10:66:96:b9:7f:73:57:97:7e:6a:f2:74:d3:ac:0a:3e:
+        b2:11:c4:f5:1e:4d:87:1f:8e:6d:29:ed:ef:97:8f:70:f7:37:
+        61:3e:ae:4a:d6:6d:6e:80:c5:bb:de:9b:bd:06:bb:a8:94:28:
+        ed:f6:c9:4a:7f:e2:9a:44:e1:96:07:25:60:74:19:d1:fb:86:
+        32:16:1c:c4:99:dd:de:ab:fe:7a:88:af:8e:3a:fa:36:c3:92:
+        b3:82:ba:50:18:02:42:2c:b4:6a:d2:ba:a3:8e:fb:72:6a:d1:
+        8a:b4:3f:b3:9e:27:90:18:b8:50:04:b8:1e:14:d6:e1:98:ff:
+        ed:78:5d:5e:76:b0:6e:8d:e0:ea:e3:00:5e:c7:f3:eb:ed:71:
+        c6:c0:de:f1:e2:bb:03:14:f1:27:0f:a6:2e:6c:38:0a:ca:3e:
+        ef:e1:4c:d8:a4:dc:7d:6a:ec:e5:3e:b5:a7:53:7b:2a:32:76:
+        da:a8:e1:1b:8c:76:6b:8b:b8:75:51:65:25:e0:c2:31:c7:0b:
+        b1:a6:a2:10:b7:45:4d:fc:69:67:84:c6:81:c8:e7:5e:b8:fc:
+        fb:8d:64:e3:28:dc:b3:41:be:8e:58:7a:8e:9e:89:ee:51:f1:
+        bf:5e:82:a6:29:a8:98:fe:ef:fb:7c:70:f7:8c:ee:4e:07:47:
+        10:4e:75:ad:21:a2:ad:9a:4e:e7:3a:01:2e:bc:81:63:b7:7c:
+        4c:ea:32:4c:12:78:20:81:9d:f2:8d:5f:1f:4b:82:67:55:1f:
+        95:ff:d8:7f:5b:50:74:fd:18:4c:74:3e:4e:cf:5b:26:d6:73:
+        29:ea:dd:9b:a0:a5:0b:5d:5e:0d:36:fd:f5:d1:d0:91:9a:6e:
+        81:b9:4a:93:d7:94:bb:75:e5:fa:ff:9f:5f:1e:f4:d3:63:9e:
+        09:03:00:b5:aa:77:0d:9f:2f:25:41:99:f3:52:04:08:3d:f4:
+        d5:ab:37:3a:0c:1b:fe:db:66:45:8b:b3:88:7e:1d:9c:98:98:
+        a7:f7:00:30:06:78:f6:0e:ea:aa:e6:24:3d:3f:31:39:80:3e:
+        a8:74:a2:1e:42:51:d7:13
+-----BEGIN CERTIFICATE-----
+MIIHZDCCBUygAwIBAgILKgHgpfuAgAAAACkwDQYJKoZIhvcNAQEMBQAwejELMAkG
+A1UEBhMCQ0ExEjAQBgNVBAoMCVNBRkUgSW5jLjEkMCIGA1UECwwbRGlnaXRhbCBD
+ZXJ0aWZpY2F0ZSBTaWduaW5nMTEwLwYDVQQDDChTQUZFIE1pZGxlIEdyb3VuZCBD
+QSAoMjAyNCkgLSBTSEEzODQgLSAzMCAXDTI1MDQwNjEzNTkwNloYDzIwNTAwNDA2
+MTM1OTA2WjCBkTELMAkGA1UEBhMCQ0ExDzANBgNVBAgMBlF1ZWJlYzERMA8GA1UE
+BwwITW9udHJlYWwxEjAQBgNVBAoMCVNBRkUgSW5jLjEqMCgGA1UECwwhTWFpbGxl
+dXIgZW1haWwgZGV2ZWxvcHBlbWVudCB0ZXN0MR4wHAYDVQQDDBVsb2NhbGhvc3Qu
+bG9jYWxkb21haW4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC6XzXS
+BNX02vCxmW/zQkIOWY17bCSXbSTf6PqDfK6S/dFYHBR+IHFeRFqw6HrORTOVqifv
+Uv69WiN7T4skTQv3nJkluLmvjEb3qWwY/zl8qf+a9fDX18rcbF3HCQKDhzcaL/cF
+uDmvn6skOyRI5nKvNhANxty9wt4FT0r92Fs1JLm8zqs3Ovn/LUTCM/JSSjZcX4Cn
+zfcbbVW31hMlctVVTVT/oR59hYvg4hbR0SIhB5mtmlElAnF7VuV3FgcYxv3exODl
+VQF49jxTqzUdRPgmr8MRoi1jcynFvbQX94N+DdkK2aEn8ny87pV2aOzEfzVkRNRt
+Q0aZQFLNtMAROterfM+HtUEypCMtP8z6smtb3aVYFHokyzomBEmPB4tb0L7C7iTe
+0XTLBEi++XRaF1IeDsDqAtF6HueilbR3GpZtNIB4hQ+E5jtgJ3VdM2Bta9naua+p
+z7yuwynKoxtPO3/6utYB8Ac/fQBF5m5/qmetkNsdKuvkvkMTXi7c3qE2/SCQH6yM
+PMkOMpbvqBkdMIfR8kLHVedGJJzSSh9CAffraF/WtZo64lGQlFmdvIPd2InpNK/W
+Q2aPh4UrxrFDtgmSuvTj1jvBWy12zVasf7v7YDClE3ZzNV5n393F/qvlT92AVhmm
+4NhTsiBcXDQvoTELEidxtupbLvqjiC/3JFuTDQIDAQABo4IBzzCCAcswDgYDVR0P
+AQH/BAQDAgWgMIGABggrBgEFBQcBAQR0MHIwOgYIKwYBBQUHMAKGLmh0dHA6Ly9j
+ZXJ0aWZpY2F0ZXMuc2FmZS5jYS9jYWNlcnQvc2FmZU1ETC5wZW0wNAYIKwYBBQUH
+MAGGKGh0dHA6Ly9jZXJ0aWZpY2F0ZXMvc2FmZS5jYS9jaGtjZXJ0c3RhdHMwUwYD
+VR0gBEwwSjA/BgkrBgEEAboOAQIwMjAwBggrBgEFBQcCARYkaHR0cDovL2NlcnRp
+ZmljYXRlcy5zYWZlLmNhL3BvbGljaWVzMAcGBWeBDAEBMAkGA1UdEwQCMAAwRQYD
+VR0fBD4wPDA6oDigNoY0aHR0cDovL2NlcnRpZmljYXRlcy5zYWZlLmNhL3JlcG9z
+aXRvcnkvcmV2b2tsaXN0LnBlbTAmBgNVHREEHzAdghVsb2NhbGhvc3QubG9jYWxk
+b21haW6HBH9/AAEwJwYDVR0lBCAwHgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEF
+BQcDBDAdBgNVHQ4EFgQULb1l4U4rfeCAPtJmbu2Bh0pSzWwwHwYDVR0jBBgwFoAU
+nL4LwCJ29c+8/Xiakncg/r+WHtgwDQYJKoZIhvcNAQEMBQADggIBAKzoHlDsDrgC
+xqxwGe43f1MkaczKbjRrmxTPcWe7fflgp5JZm95Wi4aajlnarUqQpC4txKAXARiE
+xN1ck5fd2a3UDWMzO04/JA5sE2w/O17KJ6FaHDDk69rQ5bFPzvvEjHba5vYApQNY
+m215hJIsEGaWuX9zV5d+avJ006wKPrIRxPUeTYcfjm0p7e+Xj3D3N2E+rkrWbW6A
+xbvem70Gu6iUKO32yUp/4ppE4ZYHJWB0GdH7hjIWHMSZ3d6r/nqIr446+jbDkrOC
+ulAYAkIstGrSuqOO+3Jq0Yq0P7OeJ5AYuFAEuB4U1uGY/+14XV52sG6N4OrjAF7H
+8+vtccbA3vHiuwMU8ScPpi5sOArKPu/hTNik3H1q7OU+tadTeyoydtqo4RuMdmuL
+uHVRZSXgwjHHC7GmohC3RU38aWeExoHI5164/PuNZOMo3LNBvo5Yeo6eie5R8b9e
+gqYpqJj+7/t8cPeM7k4HRxBOda0hoq2aTuc6AS68gWO3fEzqMkwSeCCBnfKNXx9L
+gmdVH5X/2H9bUHT9GEx0Pk7PWybWcynq3ZugpQtdXg02/fXR0JGaboG5SpPXlLt1
+5fr/n18e9NNjngkDALWqdw2fLyVBmfNSBAg99NWrNzoMG/7bZkWLs4h+HZyYmKf3
+ADAGePYO6qrmJD0/MTmAPqh0oh5CUdcT
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            2a:01:e0:a5:fb:80:10:00:00:00:02
+        Signature Algorithm: ecdsa-with-SHA384
+        Issuer: C=CA, L=Montreal, ST=Quebec, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Root CA 1
+        Validity
+            Not Before: Jan 20 17:19:55 2024 GMT
+            Not After : Jun  7 17:19:55 2051 GMT
+        Subject: C=CA, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Midle Ground CA (2024) - SHA384 - 3
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:d1:52:9b:dc:10:57:6d:9a:0e:09:5b:1b:aa:fb:
+                    76:8c:65:b3:f2:ca:75:36:8f:c0:cb:82:d8:2f:5b:
+                    0e:25:0c:5f:fc:18:94:41:87:5d:75:eb:92:ec:2a:
+                    87:14:ec:5f:cc:f6:8f:bf:db:4e:a3:07:aa:ec:90:
+                    3a:48:43:b9:01:84:42:fb:34:0b:06:5f:d8:e4:6d:
+                    e7:55:8f:f6:ad:98:c4:7d:6f:a8:39:de:f8:70:94:
+                    71:f3:2f:24:1b:3b:ab:42:70:d8:6c:06:ef:81:af:
+                    fa:f7:68:77:66:0e:60:12:df:80:bb:b4:92:4a:1f:
+                    3e:52:2d:f5:9a:e3:ba:26:d3:88:68:aa:11:88:0f:
+                    b8:be:7e:e3:d7:88:ce:86:09:1a:a3:2c:ce:74:c1:
+                    d7:d6:7a:c4:b5:04:1e:25:ef:b7:15:6a:16:27:4d:
+                    0f:ed:af:46:fc:a0:57:a2:6d:fe:91:c3:c7:1f:87:
+                    06:fe:5a:e2:a8:de:33:67:ae:6d:06:84:f2:15:1d:
+                    9d:ff:11:cf:be:6f:a9:a5:13:13:0b:ef:67:19:1f:
+                    ea:a8:ed:f0:db:f2:1f:ba:8c:a5:1e:b3:54:b7:68:
+                    c3:37:85:db:01:2e:83:4d:e0:06:be:93:54:b0:dc:
+                    31:23:98:15:b7:ec:b5:82:57:7a:7c:34:6c:3b:2b:
+                    3b:fa:b3:12:9a:63:63:d9:54:fd:bf:a1:ee:3c:a4:
+                    47:83:04:60:b9:9b:74:8f:f7:92:93:1d:f5:ea:98:
+                    87:c4:c9:de:d6:b8:5f:bf:fc:2e:41:e0:55:38:65:
+                    80:54:02:c6:d9:bd:7d:51:96:ba:55:ad:bf:01:ce:
+                    31:21:54:1e:56:16:79:7b:97:1a:53:92:86:80:54:
+                    ef:e9:75:ad:21:45:37:82:54:52:ed:c3:37:8c:11:
+                    ab:63:dd:64:ae:15:b4:f5:cc:02:2f:61:ab:42:d6:
+                    c5:a1:c0:dd:19:ef:70:f1:7f:6d:31:af:4e:60:bb:
+                    83:a1:f7:49:a5:de:94:dd:31:c1:74:4b:11:73:da:
+                    4d:f4:4e:90:9e:ae:dd:c0:61:d6:6b:54:3f:3a:78:
+                    c3:8b:e4:0e:ba:c6:9c:f3:3f:fb:6c:34:7c:ff:3d:
+                    65:d7:0b:ec:4c:19:37:51:37:c5:3b:34:7e:55:85:
+                    10:82:33:30:7f:ff:95:63:5b:45:3c:45:90:34:fb:
+                    1c:5e:ef:64:a3:a7:a8:58:0f:d0:97:6a:de:5a:8f:
+                    29:51:6b:14:01:b1:ec:59:74:47:0e:d9:d0:1a:78:
+                    df:16:e5:fe:5b:8b:95:48:0f:26:20:58:ef:14:6a:
+                    97:ca:c0:b3:7d:ac:7f:8a:6c:59:be:1b:fc:a0:47:
+                    e7:57:b1
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            Authority Information Access: 
+                CA Issuers - URI:http://certificates.safe.ca/cacert/safeMDL.pem
+                OCSP - URI:http://certificates/safe.ca/chkcertstats
+            X509v3 Certificate Policies: 
+                Policy: 1.3.6.1.4.1.7438.1.1
+                  CPS: http://certificates.safe.ca/repository/
+                Policy: 2.23.140.1.2.1
+            X509v3 Basic Constraints: critical
+                CA:TRUE, pathlen:0
+            X509v3 CRL Distribution Points: 
+                Full Name:
+                  URI:http://certificates.safe.ca/repository/revoklist.pem
+            X509v3 Subject Alternative Name: 
+                DNS:certificates.safe.ca, IP Address:192.219.254.53
+            X509v3 Authority Key Identifier: 
+                87:DD:FB:32:49:26:5E:13:F8:B7:F2:DF:EF:9C:F6:85:34:37:7A:D9
+            X509v3 Subject Key Identifier: 
+                9C:BE:0B:C0:22:76:F5:CF:BC:FD:78:9A:92:77:20:FE:BF:96:1E:D8
+    Signature Algorithm: ecdsa-with-SHA384
+    Signature Value:
+        30:46:02:21:00:ff:21:78:ff:d7:43:e7:9d:7d:dd:e6:f1:89:
+        f9:39:8a:14:e0:46:ca:b2:f2:59:a1:09:70:a0:2d:8b:66:a1:
+        65:02:21:00:d6:cf:8e:54:06:f0:d3:4c:23:f6:9d:a7:d5:b7:
+        23:6d:b9:c8:18:15:63:a3:92:98:3c:dc:25:18:71:1c:74:68
+-----BEGIN CERTIFICATE-----
+MIIFejCCBR+gAwIBAgILKgHgpfuAEAAAAAIwCgYIKoZIzj0EAwMwgYQxCzAJBgNV
+BAYTAkNBMREwDwYDVQQHDAhNb250cmVhbDEPMA0GA1UECAwGUXVlYmVjMRIwEAYD
+VQQKDAlTQUZFIEluYy4xJDAiBgNVBAsMG0RpZ2l0YWwgQ2VydGlmaWNhdGUgU2ln
+bmluZzEXMBUGA1UEAwwOU0FGRSBSb290IENBIDEwIBcNMjQwMTIwMTcxOTU1WhgP
+MjA1MTA2MDcxNzE5NTVaMHoxCzAJBgNVBAYTAkNBMRIwEAYDVQQKDAlTQUZFIElu
+Yy4xJDAiBgNVBAsMG0RpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzExMC8GA1UE
+AwwoU0FGRSBNaWRsZSBHcm91bmQgQ0EgKDIwMjQpIC0gU0hBMzg0IC0gMzCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANFSm9wQV22aDglbG6r7doxls/LK
+dTaPwMuC2C9bDiUMX/wYlEGHXXXrkuwqhxTsX8z2j7/bTqMHquyQOkhDuQGEQvs0
+CwZf2ORt51WP9q2YxH1vqDne+HCUcfMvJBs7q0Jw2GwG74Gv+vdod2YOYBLfgLu0
+kkofPlIt9ZrjuibTiGiqEYgPuL5+49eIzoYJGqMsznTB19Z6xLUEHiXvtxVqFidN
+D+2vRvygV6Jt/pHDxx+HBv5a4qjeM2eubQaE8hUdnf8Rz75vqaUTEwvvZxkf6qjt
+8NvyH7qMpR6zVLdowzeF2wEug03gBr6TVLDcMSOYFbfstYJXenw0bDsrO/qzEppj
+Y9lU/b+h7jykR4MEYLmbdI/3kpMd9eqYh8TJ3ta4X7/8LkHgVThlgFQCxtm9fVGW
+ulWtvwHOMSFUHlYWeXuXGlOShoBU7+l1rSFFN4JUUu3DN4wRq2PdZK4VtPXMAi9h
+q0LWxaHA3RnvcPF/bTGvTmC7g6H3SaXelN0xwXRLEXPaTfROkJ6u3cBh1mtUPzp4
+w4vkDrrGnPM/+2w0fP89ZdcL7EwZN1E3xTs0flWFEIIzMH//lWNbRTxFkDT7HF7v
+ZKOnqFgP0Jdq3lqPKVFrFAGx7Fl0Rw7Z0Bp43xbl/luLlUgPJiBY7xRql8rAs32s
+f4psWb4b/KBH51exAgMBAAGjggGyMIIBrjAOBgNVHQ8BAf8EBAMCAQYwgYAGCCsG
+AQUFBwEBBHQwcjA6BggrBgEFBQcwAoYuaHR0cDovL2NlcnRpZmljYXRlcy5zYWZl
+LmNhL2NhY2VydC9zYWZlTURMLnBlbTA0BggrBgEFBQcwAYYoaHR0cDovL2NlcnRp
+ZmljYXRlcy9zYWZlLmNhL2Noa2NlcnRzdGF0czBXBgNVHSAEUDBOMEIGCSsGAQQB
+ug4BATA1MDMGCCsGAQUFBwIBFidodHRwOi8vY2VydGlmaWNhdGVzLnNhZmUuY2Ev
+cmVwb3NpdG9yeS8wCAYGZ4EMAQIBMBIGA1UdEwEB/wQIMAYBAf8CAQAwRQYDVR0f
+BD4wPDA6oDigNoY0aHR0cDovL2NlcnRpZmljYXRlcy5zYWZlLmNhL3JlcG9zaXRv
+cnkvcmV2b2tsaXN0LnBlbTAlBgNVHREEHjAcghRjZXJ0aWZpY2F0ZXMuc2FmZS5j
+YYcEwNv+NTAfBgNVHSMEGDAWgBSH3fsySSZeE/i38t/vnPaFNDd62TAdBgNVHQ4E
+FgQUnL4LwCJ29c+8/Xiakncg/r+WHtgwCgYIKoZIzj0EAwMDSQAwRgIhAP8heP/X
+Q+edfd3m8Yn5OYoU4EbKsvJZoQlwoC2LZqFlAiEA1s+OVAbw00wj9p2n1bcjbbnI
+GBVjo5KYPNwlGHEcdGg=
+-----END CERTIFICATE-----
diff --git a/certs/localhost-key.pem b/certs/localhost-key.pem
new file mode 100644 (file)
index 0000000..bc8f96b
--- /dev/null
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC6XzXSBNX02vCx
+mW/zQkIOWY17bCSXbSTf6PqDfK6S/dFYHBR+IHFeRFqw6HrORTOVqifvUv69WiN7
+T4skTQv3nJkluLmvjEb3qWwY/zl8qf+a9fDX18rcbF3HCQKDhzcaL/cFuDmvn6sk
+OyRI5nKvNhANxty9wt4FT0r92Fs1JLm8zqs3Ovn/LUTCM/JSSjZcX4CnzfcbbVW3
+1hMlctVVTVT/oR59hYvg4hbR0SIhB5mtmlElAnF7VuV3FgcYxv3exODlVQF49jxT
+qzUdRPgmr8MRoi1jcynFvbQX94N+DdkK2aEn8ny87pV2aOzEfzVkRNRtQ0aZQFLN
+tMAROterfM+HtUEypCMtP8z6smtb3aVYFHokyzomBEmPB4tb0L7C7iTe0XTLBEi+
++XRaF1IeDsDqAtF6HueilbR3GpZtNIB4hQ+E5jtgJ3VdM2Bta9naua+pz7yuwynK
+oxtPO3/6utYB8Ac/fQBF5m5/qmetkNsdKuvkvkMTXi7c3qE2/SCQH6yMPMkOMpbv
+qBkdMIfR8kLHVedGJJzSSh9CAffraF/WtZo64lGQlFmdvIPd2InpNK/WQ2aPh4Ur
+xrFDtgmSuvTj1jvBWy12zVasf7v7YDClE3ZzNV5n393F/qvlT92AVhmm4NhTsiBc
+XDQvoTELEidxtupbLvqjiC/3JFuTDQIDAQABAoICAAgIP3EQPUpivzngQ4dn5SHq
+MSNPG1qdXRf7WgSdtIZoQwP5ndZ7vncSGhiCxHwsVldOcI8rdAgkIOBZIsWLPodK
+6/zIs9QBhzN4TUzcfxVE3FvI2rrqtXb/gqEn3cBebP0HcS2BbZcImh8jH2K2791m
+RpXZar0Q73GqI7DCCNhG/2xIOe/U7l5VjJ+UDo8GXMZpZUmWIl6/vRCBvOyBwPj+
+rrmfr4k4Xrlv9QzQ5xgmVDS7sGCYdNycd6JExESImvtlX83ydnBIqHvwpobe6JT+
+QTl61FrzZSr9DIx14ehmX2LM01m95StdWvqx9fZYByyNnNwiqKHFOGxeLGLxtsa9
+8q9r2NlCWISUrZxs8DJUqOEHBVexocFzoYHTj023nb78LqBIyESnphOQaAXiaFSq
+0IWCh7/5BZsG5jcQaDkWBHbv2XcYFtiS8tQ4AvacE77SINWtmcCtYb5e3Zn3oB/r
+i20AiQ3CWe5zO5Q9DfJJRj2k+UPXphkuu6uYbATxwYgfpHLeCwWGp10ABBt52Osn
+4pyCKY4B6aEDfMY2eta/J/dUHN/0091u7tg7jvgwaJ7ZMR2vAGczLt0nkWFuKyVY
+QAdlv1ebBN0jnhYFkELMKJJNYOJom7TaXLlqeGvKfjSavT5ZIBlZb3Hv0JYCZFP4
+L66kGMTD08hjsNg6SWCRAoIBAQDfV1gzXmUt9wgelo9QncIwbEegEC94QwVFNDXH
+n90ityNd/6g8dqKcMnOS3m2R08eiFtGTjlokXTv0zL118pu4UN83ee4Lx0p/jfS2
+TWPKYOzltMIKcaSyBF+P4fdMe2QWOYvnhqWDv4hIDuomezuwgB6U+BTC+v3Vihtz
+6IAOBfhGh40POC50I++ps2yfANcc4w0/a99tEUp3Pchsiw263w1f0OJZ7yBQL6fA
+LeKBRxrljKRFIFCP0JM8XnxyQLz872kaqSwkmu0m+/ickD9Sr+8Gict9EVmuQKBF
+e1TW+Kg1Pxi+L/n3csN4RxA+ZBJWKC77yly69W93b+2omiQJAoIBAQDVn/Drra44
+VXtC0irMvfD4v6Qb64wwcBLMgE1qaopso7vZDTKlzQoBgxVK+q8q4kGn4KyHQc94
+Prc/lgVEcxoyxD7GaQwKROTeBapN7WmzYtK9Tp/yqZegoRzkXjO0RxxcQLA5h/Yo
+l7k1O8kewfOCrmLx9KUSzo6jWVzB/j5/8Qw1XA6WNvUSnUR61H4LFDwndypzd+V5
+RB3QolRDnz0eDZcSEDdutJ8NcK/Xs2eSGcUgawkD7mbifa96L+JFC+ds9ZrWg9KW
+A66KSD7ooPBbU0LP3U/N3KAn9ESoZD+K4qkAvLpsNo9WRcks7lyz7yddc1XI0bKJ
+irwzzgwwyl/lAoIBAQCSz4+i9MH93QzxTncWvx0PR54tkXYiz6t/Ojq/5k4uq8QL
+3ipw1Gs/0co3GQ9ddKEd43IQVNrcXI009gD0r229UyvoA7Z+95n5Dw91ZpnaRqxy
+ssd6pgllG4rVwewV0maQVXOp1dCnMhgbhfJVKVyrQQzZT9oV8Xc6D3bMUmEa1sHF
+WwVWo8+leJnqK4+TVbIRww7KSdask1kn2O0OrLlxxX29JkokZBQRocHbik4CPQNp
+MXIqHnvWjXy7ikCzueqBOb8VT289ALozjh52jDuT1yKAYcPpiqYw7Tptcgzq4rlv
+hvJ00oS2LDJhthTvbki/adq4mOUr4HpNQL4wvmJBAoIBADvxs5MOm3KnWf04KFZn
+fjTu4cgL4ry81imYXyDbYJh3oYCYmXu4+3PNKq5thbXM44uOGhuiNeuR/a3UFQpO
+Gi3gr5TRaVWAW5CJmm5fjmeN9YC1jWB0gl3Kl+blIDEYOzcP1+Ns6YAegxQTAK6m
+0gP7lzw54BBU8eWYb9Tn2PXh2AEpIda4+TAN/WdSNUQ6m16fq8PXAwdyO8ONqYOI
+LYg/XALvlf50pKUK1+z5JLj0jkI4hsEZprkrd5Ab26xwWLJUgOxs9SyvP80B1NVG
+Q+v8gyT8/yXPIQnVG2b9Km94ht/0FYPEE5ir4HXBNTYwh6VLdejyHt1FluYajYUr
+ZJkCggEAaDYyDksmSEb14qAZzTYU2DIi2CglhOvEs8QSObR3KPYBD5roROIVat3X
+LEtnnoNkCWGAnSC8PH3iNxh6EqA6Csjf2B/UKBYPy6jyV9HTkKa9aUOMoVYpzhMM
+qzo4mCO7n2SKqlNzSLkzNLG5pcyOvVKkW9kEr6ruC3LG/Qd2Nx6xfF+Ien+YAwvP
+W+17N2lDDdoa1YlXxIHu/DW5gCskF+WqkcsZAStsK01n1Tb9NcJiVC4QEPu8y57O
+RulAtP4OgwwFhzQ1dhT92UFZrH+W28l3F571Nvx5ADPnRtLOB4umT0styUKm322Q
+yVX+KcXoeFoZZcQR5s0qu1ZkJNhizw==
+-----END PRIVATE KEY-----
diff --git a/certs/mailleur_server-cert_x509.pem b/certs/mailleur_server-cert_x509.pem
new file mode 100644 (file)
index 0000000..0ae6ef6
--- /dev/null
@@ -0,0 +1,147 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            2a:01:e0:a5:fb:80:80:00:00:00:28
+        Signature Algorithm: sha384WithRSAEncryption
+        Issuer: C=CA, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Midle Ground CA (2024) - SHA384 - 3
+        Validity
+            Not Before: Apr  6 11:54:45 2025 GMT
+            Not After : Apr  6 11:54:45 2050 GMT
+        Subject: C=CA, ST=Quebec, L=Montreal, O=SAFE Inc., OU=Mailleur email developpement test, CN=mailleur.example.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:b2:6e:35:f7:8c:eb:07:0d:a1:f8:10:fd:dd:8d:
+                    d9:9e:cf:9f:4b:39:4d:ee:81:5e:b3:5e:a2:67:81:
+                    9e:b7:ec:b8:bb:08:12:5d:07:01:23:bc:3d:24:82:
+                    a7:b0:a6:b1:56:57:6e:e6:b9:95:8f:fb:7f:12:fd:
+                    ec:91:4b:81:6d:e8:8f:5d:33:c3:e6:db:24:66:f5:
+                    f2:cd:1e:86:8f:23:b6:38:2e:46:c9:94:cd:4c:b6:
+                    37:41:44:5f:8f:08:36:f7:90:77:97:f6:1d:81:a8:
+                    44:94:23:30:a8:19:41:bc:b8:d4:52:d9:4c:57:45:
+                    ea:1e:2e:a8:60:9b:c7:34:6d:81:66:5d:68:f8:a5:
+                    67:31:5a:49:14:13:7c:68:af:d0:ab:6e:e5:2c:da:
+                    5c:b1:20:78:ff:4b:3c:1e:5a:81:a0:91:66:7f:a9:
+                    6c:2d:df:b6:4f:89:53:db:62:40:01:ea:ab:d8:9f:
+                    1d:4b:5c:dc:2d:95:83:73:a2:77:c8:3f:ce:fe:39:
+                    11:2e:b2:38:17:3d:bc:50:73:50:d0:1a:5b:76:9e:
+                    44:76:6d:c9:14:53:61:05:31:a6:66:1d:ba:a9:88:
+                    52:bb:28:a9:bb:de:3e:05:3f:11:6b:ee:14:0b:0f:
+                    f2:79:6a:3c:56:c9:f6:78:0f:d5:2f:8b:7b:ad:23:
+                    75:f3:53:e7:b4:ab:82:c4:8d:f1:84:f8:82:3e:97:
+                    a6:85:84:18:fc:89:e7:12:95:c9:ed:28:c5:6c:d8:
+                    84:de:f7:d5:fd:a8:c1:e6:2d:55:75:14:9c:1b:5f:
+                    89:91:0a:58:0e:ff:92:67:2e:f0:9e:c4:48:30:1a:
+                    be:1d:64:35:ba:87:92:ae:d7:24:5f:08:28:37:b2:
+                    ec:c9:5a:36:84:66:1e:f2:94:73:bc:7e:83:3e:0c:
+                    da:ff:8f:ea:1c:13:94:53:d6:71:8f:a1:52:27:c5:
+                    0f:31:0b:7d:3a:96:23:f5:cc:bf:4a:9b:8e:08:5a:
+                    15:ec:4a:6b:db:25:16:21:2c:9b:52:4d:71:d3:6c:
+                    6f:55:63:cc:28:37:23:58:cf:63:dd:38:79:24:ab:
+                    46:f9:96:20:6b:b1:77:b4:b8:f1:cf:1e:7e:ae:f5:
+                    8b:5b:89:98:e5:df:71:d1:b5:66:cd:6f:b6:c2:8f:
+                    0e:75:59:e1:5f:cf:ab:b3:87:ce:6d:c1:ba:44:68:
+                    79:70:ae:0b:07:1f:d5:7a:33:0b:13:fd:39:98:5b:
+                    46:19:e4:a2:bf:f4:06:48:12:01:c7:fc:c8:cc:15:
+                    81:d6:2e:82:3f:7e:57:b8:a8:06:d8:70:81:f7:c3:
+                    42:4c:af:48:7a:26:38:96:e2:6f:fc:b3:e6:9f:b8:
+                    6f:2b:0d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            Authority Information Access: 
+                CA Issuers - URI:http://certificates.safe.ca/cacert/safeMDL.pem
+                OCSP - URI:http://certificates/safe.ca/chkcertstats
+            X509v3 Certificate Policies: 
+                Policy: 1.3.6.1.4.1.7438.1.2
+                  CPS: http://certificates.safe.ca/policies
+                Policy: 2.23.140.1.1
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 CRL Distribution Points: 
+                Full Name:
+                  URI:http://certificates.safe.ca/repository/revoklist.pem
+            X509v3 Subject Alternative Name: 
+                DNS:mailleur.example.com, IP Address:127.127.10.25
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection
+            X509v3 Subject Key Identifier: 
+                89:FC:FA:27:85:1A:75:70:74:B6:47:9C:2A:B6:1F:90:58:BF:C5:C1
+            X509v3 Authority Key Identifier: 
+                9C:BE:0B:C0:22:76:F5:CF:BC:FD:78:9A:92:77:20:FE:BF:96:1E:D8
+    Signature Algorithm: sha384WithRSAEncryption
+    Signature Value:
+        68:21:b6:11:60:a6:ea:31:af:9a:31:64:be:88:f9:60:17:d7:
+        4b:d5:95:a8:cd:bd:04:10:2a:3d:f9:61:25:e1:44:87:5d:53:
+        81:47:a5:d4:46:32:a8:ef:f3:be:1c:36:71:19:77:4a:f8:15:
+        43:38:c5:61:62:09:7c:1e:d6:0c:72:b9:9c:97:eb:c2:ce:b6:
+        d0:14:0b:7c:01:8c:1d:05:bf:7e:29:09:9f:13:e2:e5:1d:d8:
+        89:78:0e:f8:af:87:2b:94:dc:c3:27:9c:cd:db:33:f7:60:7c:
+        9a:0f:ab:09:79:dd:5f:b4:b2:60:54:a5:88:9c:76:3f:6e:cc:
+        91:b3:a6:13:63:64:be:b0:2e:37:9c:80:db:e1:95:11:12:e8:
+        0a:5b:bd:b0:b9:49:ee:cd:6c:34:64:ef:85:57:39:d7:2f:8d:
+        a5:b6:4d:98:8f:c5:2e:2d:d5:97:8b:cc:49:84:ba:29:a9:e9:
+        40:b7:2a:56:f4:0f:cb:02:d2:2f:ec:5d:4c:2c:6b:88:e1:b6:
+        01:cb:2c:6e:c8:49:a6:2c:48:d3:81:9c:0d:6f:07:c5:56:7e:
+        47:f4:7d:14:81:62:e1:29:b6:28:91:7f:db:fc:47:7c:7f:e1:
+        fe:9c:08:ab:64:22:f8:cf:bb:8f:9e:75:1e:07:ac:dd:56:88:
+        95:ca:84:42:1f:b0:90:ca:3a:7b:24:00:43:44:57:3a:83:67:
+        f7:15:65:14:bf:96:85:39:d9:53:6c:e3:f2:dc:0f:e2:bb:d7:
+        0f:81:71:00:16:bd:1f:82:2b:af:f7:2b:49:04:9f:1c:fe:3b:
+        bb:da:26:a9:ba:8b:5f:70:f4:68:a3:fc:88:4b:cf:34:47:54:
+        eb:3a:65:4d:24:c7:66:60:61:55:03:81:10:a4:b3:30:3e:40:
+        ca:e8:0c:e8:24:9f:0e:20:5c:1f:ea:65:bd:5b:23:cd:95:f8:
+        1c:74:ef:25:2c:55:7a:d8:85:eb:33:fe:98:8e:cd:bc:d1:6e:
+        f9:38:71:5f:8b:cb:09:2e:3d:78:b4:37:4b:70:60:60:85:f0:
+        34:78:e7:4c:05:47:a7:ba:46:58:7b:4a:2e:8c:03:5f:49:ea:
+        a8:c4:81:4e:08:c7:50:e2:43:b2:22:d2:cb:6a:e6:69:71:4e:
+        a5:f5:49:a5:fd:03:07:25:e8:43:2e:fd:fa:1d:f5:9c:24:90:
+        7d:29:e7:40:f8:e5:b0:ea:02:a4:c5:e5:3a:f8:d0:a7:88:31:
+        1a:3d:8d:3a:fd:ac:cd:02:6a:4a:08:d4:dd:a9:a3:76:45:2a:
+        d1:43:3a:17:be:75:50:33:43:66:7b:1c:a6:cc:0e:0d:90:5c:
+        85:7d:6c:cc:b0:56:44:45
+-----BEGIN CERTIFICATE-----
+MIIHYjCCBUqgAwIBAgILKgHgpfuAgAAAACgwDQYJKoZIhvcNAQEMBQAwejELMAkG
+A1UEBhMCQ0ExEjAQBgNVBAoMCVNBRkUgSW5jLjEkMCIGA1UECwwbRGlnaXRhbCBD
+ZXJ0aWZpY2F0ZSBTaWduaW5nMTEwLwYDVQQDDChTQUZFIE1pZGxlIEdyb3VuZCBD
+QSAoMjAyNCkgLSBTSEEzODQgLSAzMCAXDTI1MDQwNjExNTQ0NVoYDzIwNTAwNDA2
+MTE1NDQ1WjCBkDELMAkGA1UEBhMCQ0ExDzANBgNVBAgMBlF1ZWJlYzERMA8GA1UE
+BwwITW9udHJlYWwxEjAQBgNVBAoMCVNBRkUgSW5jLjEqMCgGA1UECwwhTWFpbGxl
+dXIgZW1haWwgZGV2ZWxvcHBlbWVudCB0ZXN0MR0wGwYDVQQDDBRtYWlsbGV1ci5l
+eGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALJuNfeM
+6wcNofgQ/d2N2Z7Pn0s5Te6BXrNeomeBnrfsuLsIEl0HASO8PSSCp7CmsVZXbua5
+lY/7fxL97JFLgW3oj10zw+bbJGb18s0eho8jtjguRsmUzUy2N0FEX48INveQd5f2
+HYGoRJQjMKgZQby41FLZTFdF6h4uqGCbxzRtgWZdaPilZzFaSRQTfGiv0Ktu5Sza
+XLEgeP9LPB5agaCRZn+pbC3ftk+JU9tiQAHqq9ifHUtc3C2Vg3Oid8g/zv45ES6y
+OBc9vFBzUNAaW3aeRHZtyRRTYQUxpmYduqmIUrsoqbvePgU/EWvuFAsP8nlqPFbJ
+9ngP1S+Le60jdfNT57SrgsSN8YT4gj6XpoWEGPyJ5xKVye0oxWzYhN731f2oweYt
+VXUUnBtfiZEKWA7/kmcu8J7ESDAavh1kNbqHkq7XJF8IKDey7MlaNoRmHvKUc7x+
+gz4M2v+P6hwTlFPWcY+hUifFDzELfTqWI/XMv0qbjghaFexKa9slFiEsm1JNcdNs
+b1VjzCg3I1jPY904eSSrRvmWIGuxd7S48c8efq71i1uJmOXfcdG1Zs1vtsKPDnVZ
+4V/Pq7OHzm3BukRoeXCuCwcf1XozCxP9OZhbRhnkor/0BkgSAcf8yMwVgdYugj9+
+V7ioBthwgffDQkyvSHomOJbib/yz5p+4bysNAgMBAAGjggHOMIIByjAOBgNVHQ8B
+Af8EBAMCBaAwgYAGCCsGAQUFBwEBBHQwcjA6BggrBgEFBQcwAoYuaHR0cDovL2Nl
+cnRpZmljYXRlcy5zYWZlLmNhL2NhY2VydC9zYWZlTURMLnBlbTA0BggrBgEFBQcw
+AYYoaHR0cDovL2NlcnRpZmljYXRlcy9zYWZlLmNhL2Noa2NlcnRzdGF0czBTBgNV
+HSAETDBKMD8GCSsGAQQBug4BAjAyMDAGCCsGAQUFBwIBFiRodHRwOi8vY2VydGlm
+aWNhdGVzLnNhZmUuY2EvcG9saWNpZXMwBwYFZ4EMAQEwCQYDVR0TBAIwADBFBgNV
+HR8EPjA8MDqgOKA2hjRodHRwOi8vY2VydGlmaWNhdGVzLnNhZmUuY2EvcmVwb3Np
+dG9yeS9yZXZva2xpc3QucGVtMCUGA1UdEQQeMByCFG1haWxsZXVyLmV4YW1wbGUu
+Y29thwR/fwoZMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUH
+AwQwHQYDVR0OBBYEFIn8+ieFGnVwdLZHnCq2H5BYv8XBMB8GA1UdIwQYMBaAFJy+
+C8AidvXPvP14mpJ3IP6/lh7YMA0GCSqGSIb3DQEBDAUAA4ICAQBoIbYRYKbqMa+a
+MWS+iPlgF9dL1ZWozb0EECo9+WEl4USHXVOBR6XURjKo7/O+HDZxGXdK+BVDOMVh
+Ygl8HtYMcrmcl+vCzrbQFAt8AYwdBb9+KQmfE+LlHdiJeA74r4crlNzDJ5zN2zP3
+YHyaD6sJed1ftLJgVKWInHY/bsyRs6YTY2S+sC43nIDb4ZUREugKW72wuUnuzWw0
+ZO+FVznXL42ltk2Yj8UuLdWXi8xJhLopqelAtypW9A/LAtIv7F1MLGuI4bYByyxu
+yEmmLEjTgZwNbwfFVn5H9H0UgWLhKbYokX/b/Ed8f+H+nAirZCL4z7uPnnUeB6zd
+VoiVyoRCH7CQyjp7JABDRFc6g2f3FWUUv5aFOdlTbOPy3A/iu9cPgXEAFr0fgiuv
+9ytJBJ8c/ju72iapuotfcPRoo/yIS880R1TrOmVNJMdmYGFVA4EQpLMwPkDK6Azo
+JJ8OIFwf6mW9WyPNlfgcdO8lLFV62IXrM/6Yjs280W75OHFfi8sJLj14tDdLcGBg
+hfA0eOdMBUenukZYe0oujANfSeqoxIFOCMdQ4kOyItLLauZpcU6l9Uml/QMHJehD
+Lv36HfWcJJB9KedA+OWw6gKkxeU6+NCniDEaPY06/azNAmpKCNTdqaN2RSrRQzoX
+vnVQM0NmexymzA4NkFyFfWzMsFZERQ==
+-----END CERTIFICATE-----
diff --git a/certs/mailleur_server-chain-cert_x509.pem b/certs/mailleur_server-chain-cert_x509.pem
new file mode 100644 (file)
index 0000000..ddd18fa
--- /dev/null
@@ -0,0 +1,257 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            2a:01:e0:a5:fb:80:80:00:00:00:28
+        Signature Algorithm: sha384WithRSAEncryption
+        Issuer: C=CA, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Midle Ground CA (2024) - SHA384 - 3
+        Validity
+            Not Before: Apr  6 11:54:45 2025 GMT
+            Not After : Apr  6 11:54:45 2050 GMT
+        Subject: C=CA, ST=Quebec, L=Montreal, O=SAFE Inc., OU=Mailleur email developpement test, CN=mailleur.example.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:b2:6e:35:f7:8c:eb:07:0d:a1:f8:10:fd:dd:8d:
+                    d9:9e:cf:9f:4b:39:4d:ee:81:5e:b3:5e:a2:67:81:
+                    9e:b7:ec:b8:bb:08:12:5d:07:01:23:bc:3d:24:82:
+                    a7:b0:a6:b1:56:57:6e:e6:b9:95:8f:fb:7f:12:fd:
+                    ec:91:4b:81:6d:e8:8f:5d:33:c3:e6:db:24:66:f5:
+                    f2:cd:1e:86:8f:23:b6:38:2e:46:c9:94:cd:4c:b6:
+                    37:41:44:5f:8f:08:36:f7:90:77:97:f6:1d:81:a8:
+                    44:94:23:30:a8:19:41:bc:b8:d4:52:d9:4c:57:45:
+                    ea:1e:2e:a8:60:9b:c7:34:6d:81:66:5d:68:f8:a5:
+                    67:31:5a:49:14:13:7c:68:af:d0:ab:6e:e5:2c:da:
+                    5c:b1:20:78:ff:4b:3c:1e:5a:81:a0:91:66:7f:a9:
+                    6c:2d:df:b6:4f:89:53:db:62:40:01:ea:ab:d8:9f:
+                    1d:4b:5c:dc:2d:95:83:73:a2:77:c8:3f:ce:fe:39:
+                    11:2e:b2:38:17:3d:bc:50:73:50:d0:1a:5b:76:9e:
+                    44:76:6d:c9:14:53:61:05:31:a6:66:1d:ba:a9:88:
+                    52:bb:28:a9:bb:de:3e:05:3f:11:6b:ee:14:0b:0f:
+                    f2:79:6a:3c:56:c9:f6:78:0f:d5:2f:8b:7b:ad:23:
+                    75:f3:53:e7:b4:ab:82:c4:8d:f1:84:f8:82:3e:97:
+                    a6:85:84:18:fc:89:e7:12:95:c9:ed:28:c5:6c:d8:
+                    84:de:f7:d5:fd:a8:c1:e6:2d:55:75:14:9c:1b:5f:
+                    89:91:0a:58:0e:ff:92:67:2e:f0:9e:c4:48:30:1a:
+                    be:1d:64:35:ba:87:92:ae:d7:24:5f:08:28:37:b2:
+                    ec:c9:5a:36:84:66:1e:f2:94:73:bc:7e:83:3e:0c:
+                    da:ff:8f:ea:1c:13:94:53:d6:71:8f:a1:52:27:c5:
+                    0f:31:0b:7d:3a:96:23:f5:cc:bf:4a:9b:8e:08:5a:
+                    15:ec:4a:6b:db:25:16:21:2c:9b:52:4d:71:d3:6c:
+                    6f:55:63:cc:28:37:23:58:cf:63:dd:38:79:24:ab:
+                    46:f9:96:20:6b:b1:77:b4:b8:f1:cf:1e:7e:ae:f5:
+                    8b:5b:89:98:e5:df:71:d1:b5:66:cd:6f:b6:c2:8f:
+                    0e:75:59:e1:5f:cf:ab:b3:87:ce:6d:c1:ba:44:68:
+                    79:70:ae:0b:07:1f:d5:7a:33:0b:13:fd:39:98:5b:
+                    46:19:e4:a2:bf:f4:06:48:12:01:c7:fc:c8:cc:15:
+                    81:d6:2e:82:3f:7e:57:b8:a8:06:d8:70:81:f7:c3:
+                    42:4c:af:48:7a:26:38:96:e2:6f:fc:b3:e6:9f:b8:
+                    6f:2b:0d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            Authority Information Access: 
+                CA Issuers - URI:http://certificates.safe.ca/cacert/safeMDL.pem
+                OCSP - URI:http://certificates/safe.ca/chkcertstats
+            X509v3 Certificate Policies: 
+                Policy: 1.3.6.1.4.1.7438.1.2
+                  CPS: http://certificates.safe.ca/policies
+                Policy: 2.23.140.1.1
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 CRL Distribution Points: 
+                Full Name:
+                  URI:http://certificates.safe.ca/repository/revoklist.pem
+            X509v3 Subject Alternative Name: 
+                DNS:mailleur.example.com, IP Address:127.127.10.25
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection
+            X509v3 Subject Key Identifier: 
+                89:FC:FA:27:85:1A:75:70:74:B6:47:9C:2A:B6:1F:90:58:BF:C5:C1
+            X509v3 Authority Key Identifier: 
+                9C:BE:0B:C0:22:76:F5:CF:BC:FD:78:9A:92:77:20:FE:BF:96:1E:D8
+    Signature Algorithm: sha384WithRSAEncryption
+    Signature Value:
+        68:21:b6:11:60:a6:ea:31:af:9a:31:64:be:88:f9:60:17:d7:
+        4b:d5:95:a8:cd:bd:04:10:2a:3d:f9:61:25:e1:44:87:5d:53:
+        81:47:a5:d4:46:32:a8:ef:f3:be:1c:36:71:19:77:4a:f8:15:
+        43:38:c5:61:62:09:7c:1e:d6:0c:72:b9:9c:97:eb:c2:ce:b6:
+        d0:14:0b:7c:01:8c:1d:05:bf:7e:29:09:9f:13:e2:e5:1d:d8:
+        89:78:0e:f8:af:87:2b:94:dc:c3:27:9c:cd:db:33:f7:60:7c:
+        9a:0f:ab:09:79:dd:5f:b4:b2:60:54:a5:88:9c:76:3f:6e:cc:
+        91:b3:a6:13:63:64:be:b0:2e:37:9c:80:db:e1:95:11:12:e8:
+        0a:5b:bd:b0:b9:49:ee:cd:6c:34:64:ef:85:57:39:d7:2f:8d:
+        a5:b6:4d:98:8f:c5:2e:2d:d5:97:8b:cc:49:84:ba:29:a9:e9:
+        40:b7:2a:56:f4:0f:cb:02:d2:2f:ec:5d:4c:2c:6b:88:e1:b6:
+        01:cb:2c:6e:c8:49:a6:2c:48:d3:81:9c:0d:6f:07:c5:56:7e:
+        47:f4:7d:14:81:62:e1:29:b6:28:91:7f:db:fc:47:7c:7f:e1:
+        fe:9c:08:ab:64:22:f8:cf:bb:8f:9e:75:1e:07:ac:dd:56:88:
+        95:ca:84:42:1f:b0:90:ca:3a:7b:24:00:43:44:57:3a:83:67:
+        f7:15:65:14:bf:96:85:39:d9:53:6c:e3:f2:dc:0f:e2:bb:d7:
+        0f:81:71:00:16:bd:1f:82:2b:af:f7:2b:49:04:9f:1c:fe:3b:
+        bb:da:26:a9:ba:8b:5f:70:f4:68:a3:fc:88:4b:cf:34:47:54:
+        eb:3a:65:4d:24:c7:66:60:61:55:03:81:10:a4:b3:30:3e:40:
+        ca:e8:0c:e8:24:9f:0e:20:5c:1f:ea:65:bd:5b:23:cd:95:f8:
+        1c:74:ef:25:2c:55:7a:d8:85:eb:33:fe:98:8e:cd:bc:d1:6e:
+        f9:38:71:5f:8b:cb:09:2e:3d:78:b4:37:4b:70:60:60:85:f0:
+        34:78:e7:4c:05:47:a7:ba:46:58:7b:4a:2e:8c:03:5f:49:ea:
+        a8:c4:81:4e:08:c7:50:e2:43:b2:22:d2:cb:6a:e6:69:71:4e:
+        a5:f5:49:a5:fd:03:07:25:e8:43:2e:fd:fa:1d:f5:9c:24:90:
+        7d:29:e7:40:f8:e5:b0:ea:02:a4:c5:e5:3a:f8:d0:a7:88:31:
+        1a:3d:8d:3a:fd:ac:cd:02:6a:4a:08:d4:dd:a9:a3:76:45:2a:
+        d1:43:3a:17:be:75:50:33:43:66:7b:1c:a6:cc:0e:0d:90:5c:
+        85:7d:6c:cc:b0:56:44:45
+-----BEGIN CERTIFICATE-----
+MIIHYjCCBUqgAwIBAgILKgHgpfuAgAAAACgwDQYJKoZIhvcNAQEMBQAwejELMAkG
+A1UEBhMCQ0ExEjAQBgNVBAoMCVNBRkUgSW5jLjEkMCIGA1UECwwbRGlnaXRhbCBD
+ZXJ0aWZpY2F0ZSBTaWduaW5nMTEwLwYDVQQDDChTQUZFIE1pZGxlIEdyb3VuZCBD
+QSAoMjAyNCkgLSBTSEEzODQgLSAzMCAXDTI1MDQwNjExNTQ0NVoYDzIwNTAwNDA2
+MTE1NDQ1WjCBkDELMAkGA1UEBhMCQ0ExDzANBgNVBAgMBlF1ZWJlYzERMA8GA1UE
+BwwITW9udHJlYWwxEjAQBgNVBAoMCVNBRkUgSW5jLjEqMCgGA1UECwwhTWFpbGxl
+dXIgZW1haWwgZGV2ZWxvcHBlbWVudCB0ZXN0MR0wGwYDVQQDDBRtYWlsbGV1ci5l
+eGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALJuNfeM
+6wcNofgQ/d2N2Z7Pn0s5Te6BXrNeomeBnrfsuLsIEl0HASO8PSSCp7CmsVZXbua5
+lY/7fxL97JFLgW3oj10zw+bbJGb18s0eho8jtjguRsmUzUy2N0FEX48INveQd5f2
+HYGoRJQjMKgZQby41FLZTFdF6h4uqGCbxzRtgWZdaPilZzFaSRQTfGiv0Ktu5Sza
+XLEgeP9LPB5agaCRZn+pbC3ftk+JU9tiQAHqq9ifHUtc3C2Vg3Oid8g/zv45ES6y
+OBc9vFBzUNAaW3aeRHZtyRRTYQUxpmYduqmIUrsoqbvePgU/EWvuFAsP8nlqPFbJ
+9ngP1S+Le60jdfNT57SrgsSN8YT4gj6XpoWEGPyJ5xKVye0oxWzYhN731f2oweYt
+VXUUnBtfiZEKWA7/kmcu8J7ESDAavh1kNbqHkq7XJF8IKDey7MlaNoRmHvKUc7x+
+gz4M2v+P6hwTlFPWcY+hUifFDzELfTqWI/XMv0qbjghaFexKa9slFiEsm1JNcdNs
+b1VjzCg3I1jPY904eSSrRvmWIGuxd7S48c8efq71i1uJmOXfcdG1Zs1vtsKPDnVZ
+4V/Pq7OHzm3BukRoeXCuCwcf1XozCxP9OZhbRhnkor/0BkgSAcf8yMwVgdYugj9+
+V7ioBthwgffDQkyvSHomOJbib/yz5p+4bysNAgMBAAGjggHOMIIByjAOBgNVHQ8B
+Af8EBAMCBaAwgYAGCCsGAQUFBwEBBHQwcjA6BggrBgEFBQcwAoYuaHR0cDovL2Nl
+cnRpZmljYXRlcy5zYWZlLmNhL2NhY2VydC9zYWZlTURMLnBlbTA0BggrBgEFBQcw
+AYYoaHR0cDovL2NlcnRpZmljYXRlcy9zYWZlLmNhL2Noa2NlcnRzdGF0czBTBgNV
+HSAETDBKMD8GCSsGAQQBug4BAjAyMDAGCCsGAQUFBwIBFiRodHRwOi8vY2VydGlm
+aWNhdGVzLnNhZmUuY2EvcG9saWNpZXMwBwYFZ4EMAQEwCQYDVR0TBAIwADBFBgNV
+HR8EPjA8MDqgOKA2hjRodHRwOi8vY2VydGlmaWNhdGVzLnNhZmUuY2EvcmVwb3Np
+dG9yeS9yZXZva2xpc3QucGVtMCUGA1UdEQQeMByCFG1haWxsZXVyLmV4YW1wbGUu
+Y29thwR/fwoZMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUH
+AwQwHQYDVR0OBBYEFIn8+ieFGnVwdLZHnCq2H5BYv8XBMB8GA1UdIwQYMBaAFJy+
+C8AidvXPvP14mpJ3IP6/lh7YMA0GCSqGSIb3DQEBDAUAA4ICAQBoIbYRYKbqMa+a
+MWS+iPlgF9dL1ZWozb0EECo9+WEl4USHXVOBR6XURjKo7/O+HDZxGXdK+BVDOMVh
+Ygl8HtYMcrmcl+vCzrbQFAt8AYwdBb9+KQmfE+LlHdiJeA74r4crlNzDJ5zN2zP3
+YHyaD6sJed1ftLJgVKWInHY/bsyRs6YTY2S+sC43nIDb4ZUREugKW72wuUnuzWw0
+ZO+FVznXL42ltk2Yj8UuLdWXi8xJhLopqelAtypW9A/LAtIv7F1MLGuI4bYByyxu
+yEmmLEjTgZwNbwfFVn5H9H0UgWLhKbYokX/b/Ed8f+H+nAirZCL4z7uPnnUeB6zd
+VoiVyoRCH7CQyjp7JABDRFc6g2f3FWUUv5aFOdlTbOPy3A/iu9cPgXEAFr0fgiuv
+9ytJBJ8c/ju72iapuotfcPRoo/yIS880R1TrOmVNJMdmYGFVA4EQpLMwPkDK6Azo
+JJ8OIFwf6mW9WyPNlfgcdO8lLFV62IXrM/6Yjs280W75OHFfi8sJLj14tDdLcGBg
+hfA0eOdMBUenukZYe0oujANfSeqoxIFOCMdQ4kOyItLLauZpcU6l9Uml/QMHJehD
+Lv36HfWcJJB9KedA+OWw6gKkxeU6+NCniDEaPY06/azNAmpKCNTdqaN2RSrRQzoX
+vnVQM0NmexymzA4NkFyFfWzMsFZERQ==
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            2a:01:e0:a5:fb:80:10:00:00:00:02
+        Signature Algorithm: ecdsa-with-SHA384
+        Issuer: C=CA, L=Montreal, ST=Quebec, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Root CA 1
+        Validity
+            Not Before: Jan 20 17:19:55 2024 GMT
+            Not After : Jun  7 17:19:55 2051 GMT
+        Subject: C=CA, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Midle Ground CA (2024) - SHA384 - 3
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:d1:52:9b:dc:10:57:6d:9a:0e:09:5b:1b:aa:fb:
+                    76:8c:65:b3:f2:ca:75:36:8f:c0:cb:82:d8:2f:5b:
+                    0e:25:0c:5f:fc:18:94:41:87:5d:75:eb:92:ec:2a:
+                    87:14:ec:5f:cc:f6:8f:bf:db:4e:a3:07:aa:ec:90:
+                    3a:48:43:b9:01:84:42:fb:34:0b:06:5f:d8:e4:6d:
+                    e7:55:8f:f6:ad:98:c4:7d:6f:a8:39:de:f8:70:94:
+                    71:f3:2f:24:1b:3b:ab:42:70:d8:6c:06:ef:81:af:
+                    fa:f7:68:77:66:0e:60:12:df:80:bb:b4:92:4a:1f:
+                    3e:52:2d:f5:9a:e3:ba:26:d3:88:68:aa:11:88:0f:
+                    b8:be:7e:e3:d7:88:ce:86:09:1a:a3:2c:ce:74:c1:
+                    d7:d6:7a:c4:b5:04:1e:25:ef:b7:15:6a:16:27:4d:
+                    0f:ed:af:46:fc:a0:57:a2:6d:fe:91:c3:c7:1f:87:
+                    06:fe:5a:e2:a8:de:33:67:ae:6d:06:84:f2:15:1d:
+                    9d:ff:11:cf:be:6f:a9:a5:13:13:0b:ef:67:19:1f:
+                    ea:a8:ed:f0:db:f2:1f:ba:8c:a5:1e:b3:54:b7:68:
+                    c3:37:85:db:01:2e:83:4d:e0:06:be:93:54:b0:dc:
+                    31:23:98:15:b7:ec:b5:82:57:7a:7c:34:6c:3b:2b:
+                    3b:fa:b3:12:9a:63:63:d9:54:fd:bf:a1:ee:3c:a4:
+                    47:83:04:60:b9:9b:74:8f:f7:92:93:1d:f5:ea:98:
+                    87:c4:c9:de:d6:b8:5f:bf:fc:2e:41:e0:55:38:65:
+                    80:54:02:c6:d9:bd:7d:51:96:ba:55:ad:bf:01:ce:
+                    31:21:54:1e:56:16:79:7b:97:1a:53:92:86:80:54:
+                    ef:e9:75:ad:21:45:37:82:54:52:ed:c3:37:8c:11:
+                    ab:63:dd:64:ae:15:b4:f5:cc:02:2f:61:ab:42:d6:
+                    c5:a1:c0:dd:19:ef:70:f1:7f:6d:31:af:4e:60:bb:
+                    83:a1:f7:49:a5:de:94:dd:31:c1:74:4b:11:73:da:
+                    4d:f4:4e:90:9e:ae:dd:c0:61:d6:6b:54:3f:3a:78:
+                    c3:8b:e4:0e:ba:c6:9c:f3:3f:fb:6c:34:7c:ff:3d:
+                    65:d7:0b:ec:4c:19:37:51:37:c5:3b:34:7e:55:85:
+                    10:82:33:30:7f:ff:95:63:5b:45:3c:45:90:34:fb:
+                    1c:5e:ef:64:a3:a7:a8:58:0f:d0:97:6a:de:5a:8f:
+                    29:51:6b:14:01:b1:ec:59:74:47:0e:d9:d0:1a:78:
+                    df:16:e5:fe:5b:8b:95:48:0f:26:20:58:ef:14:6a:
+                    97:ca:c0:b3:7d:ac:7f:8a:6c:59:be:1b:fc:a0:47:
+                    e7:57:b1
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            Authority Information Access: 
+                CA Issuers - URI:http://certificates.safe.ca/cacert/safeMDL.pem
+                OCSP - URI:http://certificates/safe.ca/chkcertstats
+            X509v3 Certificate Policies: 
+                Policy: 1.3.6.1.4.1.7438.1.1
+                  CPS: http://certificates.safe.ca/repository/
+                Policy: 2.23.140.1.2.1
+            X509v3 Basic Constraints: critical
+                CA:TRUE, pathlen:0
+            X509v3 CRL Distribution Points: 
+                Full Name:
+                  URI:http://certificates.safe.ca/repository/revoklist.pem
+            X509v3 Subject Alternative Name: 
+                DNS:certificates.safe.ca, IP Address:192.219.254.53
+            X509v3 Authority Key Identifier: 
+                87:DD:FB:32:49:26:5E:13:F8:B7:F2:DF:EF:9C:F6:85:34:37:7A:D9
+            X509v3 Subject Key Identifier: 
+                9C:BE:0B:C0:22:76:F5:CF:BC:FD:78:9A:92:77:20:FE:BF:96:1E:D8
+    Signature Algorithm: ecdsa-with-SHA384
+    Signature Value:
+        30:46:02:21:00:ff:21:78:ff:d7:43:e7:9d:7d:dd:e6:f1:89:
+        f9:39:8a:14:e0:46:ca:b2:f2:59:a1:09:70:a0:2d:8b:66:a1:
+        65:02:21:00:d6:cf:8e:54:06:f0:d3:4c:23:f6:9d:a7:d5:b7:
+        23:6d:b9:c8:18:15:63:a3:92:98:3c:dc:25:18:71:1c:74:68
+-----BEGIN CERTIFICATE-----
+MIIFejCCBR+gAwIBAgILKgHgpfuAEAAAAAIwCgYIKoZIzj0EAwMwgYQxCzAJBgNV
+BAYTAkNBMREwDwYDVQQHDAhNb250cmVhbDEPMA0GA1UECAwGUXVlYmVjMRIwEAYD
+VQQKDAlTQUZFIEluYy4xJDAiBgNVBAsMG0RpZ2l0YWwgQ2VydGlmaWNhdGUgU2ln
+bmluZzEXMBUGA1UEAwwOU0FGRSBSb290IENBIDEwIBcNMjQwMTIwMTcxOTU1WhgP
+MjA1MTA2MDcxNzE5NTVaMHoxCzAJBgNVBAYTAkNBMRIwEAYDVQQKDAlTQUZFIElu
+Yy4xJDAiBgNVBAsMG0RpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzExMC8GA1UE
+AwwoU0FGRSBNaWRsZSBHcm91bmQgQ0EgKDIwMjQpIC0gU0hBMzg0IC0gMzCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANFSm9wQV22aDglbG6r7doxls/LK
+dTaPwMuC2C9bDiUMX/wYlEGHXXXrkuwqhxTsX8z2j7/bTqMHquyQOkhDuQGEQvs0
+CwZf2ORt51WP9q2YxH1vqDne+HCUcfMvJBs7q0Jw2GwG74Gv+vdod2YOYBLfgLu0
+kkofPlIt9ZrjuibTiGiqEYgPuL5+49eIzoYJGqMsznTB19Z6xLUEHiXvtxVqFidN
+D+2vRvygV6Jt/pHDxx+HBv5a4qjeM2eubQaE8hUdnf8Rz75vqaUTEwvvZxkf6qjt
+8NvyH7qMpR6zVLdowzeF2wEug03gBr6TVLDcMSOYFbfstYJXenw0bDsrO/qzEppj
+Y9lU/b+h7jykR4MEYLmbdI/3kpMd9eqYh8TJ3ta4X7/8LkHgVThlgFQCxtm9fVGW
+ulWtvwHOMSFUHlYWeXuXGlOShoBU7+l1rSFFN4JUUu3DN4wRq2PdZK4VtPXMAi9h
+q0LWxaHA3RnvcPF/bTGvTmC7g6H3SaXelN0xwXRLEXPaTfROkJ6u3cBh1mtUPzp4
+w4vkDrrGnPM/+2w0fP89ZdcL7EwZN1E3xTs0flWFEIIzMH//lWNbRTxFkDT7HF7v
+ZKOnqFgP0Jdq3lqPKVFrFAGx7Fl0Rw7Z0Bp43xbl/luLlUgPJiBY7xRql8rAs32s
+f4psWb4b/KBH51exAgMBAAGjggGyMIIBrjAOBgNVHQ8BAf8EBAMCAQYwgYAGCCsG
+AQUFBwEBBHQwcjA6BggrBgEFBQcwAoYuaHR0cDovL2NlcnRpZmljYXRlcy5zYWZl
+LmNhL2NhY2VydC9zYWZlTURMLnBlbTA0BggrBgEFBQcwAYYoaHR0cDovL2NlcnRp
+ZmljYXRlcy9zYWZlLmNhL2Noa2NlcnRzdGF0czBXBgNVHSAEUDBOMEIGCSsGAQQB
+ug4BATA1MDMGCCsGAQUFBwIBFidodHRwOi8vY2VydGlmaWNhdGVzLnNhZmUuY2Ev
+cmVwb3NpdG9yeS8wCAYGZ4EMAQIBMBIGA1UdEwEB/wQIMAYBAf8CAQAwRQYDVR0f
+BD4wPDA6oDigNoY0aHR0cDovL2NlcnRpZmljYXRlcy5zYWZlLmNhL3JlcG9zaXRv
+cnkvcmV2b2tsaXN0LnBlbTAlBgNVHREEHjAcghRjZXJ0aWZpY2F0ZXMuc2FmZS5j
+YYcEwNv+NTAfBgNVHSMEGDAWgBSH3fsySSZeE/i38t/vnPaFNDd62TAdBgNVHQ4E
+FgQUnL4LwCJ29c+8/Xiakncg/r+WHtgwCgYIKoZIzj0EAwMDSQAwRgIhAP8heP/X
+Q+edfd3m8Yn5OYoU4EbKsvJZoQlwoC2LZqFlAiEA1s+OVAbw00wj9p2n1bcjbbnI
+GBVjo5KYPNwlGHEcdGg=
+-----END CERTIFICATE-----
diff --git a/certs/mailleur_server-key.pem b/certs/mailleur_server-key.pem
new file mode 100644 (file)
index 0000000..f8aea13
--- /dev/null
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCybjX3jOsHDaH4
+EP3djdmez59LOU3ugV6zXqJngZ637Li7CBJdBwEjvD0kgqewprFWV27muZWP+38S
+/eyRS4Ft6I9dM8Pm2yRm9fLNHoaPI7Y4LkbJlM1MtjdBRF+PCDb3kHeX9h2BqESU
+IzCoGUG8uNRS2UxXReoeLqhgm8c0bYFmXWj4pWcxWkkUE3xor9CrbuUs2lyxIHj/
+SzweWoGgkWZ/qWwt37ZPiVPbYkAB6qvYnx1LXNwtlYNzonfIP87+OREusjgXPbxQ
+c1DQGlt2nkR2bckUU2EFMaZmHbqpiFK7KKm73j4FPxFr7hQLD/J5ajxWyfZ4D9Uv
+i3utI3XzU+e0q4LEjfGE+II+l6aFhBj8iecSlcntKMVs2ITe99X9qMHmLVV1FJwb
+X4mRClgO/5JnLvCexEgwGr4dZDW6h5Ku1yRfCCg3suzJWjaEZh7ylHO8foM+DNr/
+j+ocE5RT1nGPoVInxQ8xC306liP1zL9Km44IWhXsSmvbJRYhLJtSTXHTbG9VY8wo
+NyNYz2PdOHkkq0b5liBrsXe0uPHPHn6u9YtbiZjl33HRtWbNb7bCjw51WeFfz6uz
+h85twbpEaHlwrgsHH9V6MwsT/TmYW0YZ5KK/9AZIEgHH/MjMFYHWLoI/fle4qAbY
+cIH3w0JMr0h6JjiW4m/8s+afuG8rDQIDAQABAoICAB9WdHOBJDElr44AO/oKxQ8b
+o3xEeP+76HxD0W2w6gWfFZXBtenxRj22vJ1aNiX0LN6/deqfaxlx4BBfmQousvFx
+ACbsJcq4r9TGewmUBCmFKeaFmCik0gbYVO2JWugl1vOVg6+kieug+ER1RsErSGyj
+IKVZZUtJq/P3mVp4WZ0HY4vtOXe8IxrbosycHIRNZq6JSEZfppp0W9PorIsfNbeT
++ebPhICtExm2gowcHiZIBz0BOxWmnkQCR6dKQFPjAxGKx84F/ox0awvNKQMXsfss
+ElWjdAa2GyrcnL9AFu3aE7+rxV3Jy2ZD/KUSWCJWP22cHzNV/sv46eYTDxpqSTdz
+G7kj4QhEB5XuDbt7S+CZm9PTBxeUylZX8442rWEwjJsA+gCJw5gD1Y6brrWBb/op
+NUWU14usY3OuTXCmtUUYt5n34YpSo9i7No1EPAxwdZWS7CwO/d8qI4/ieK2dazl2
+CftrO5U/13iZEdflH/Q/VUQ8xIR9fCZ2igUT3o1hSLCZWTPS8N+cHxhM81Nb0q5c
+e80zUTJDGT7YSNJEmixZ2lWMFAWYpEgOp6qWRKCaYY0j71DlXzQcJuqbBoroswh8
+GJZFusIfQYVeaxK2blCrlurYh0lvCSjc2zls9CMM/6aCo4NU6Tao5sBSsbiE+OHj
+QJH+J6dnup9gmQKjCflhAoIBAQDq0NEEiyG4+GmOmt8J2kDeA6fl0fvoJNkZQJ+q
+wGMCAQZvRVe8+mSS5oRsp5U9E2ZnafowRdhzk8ODYmBSDZ2Nv2ZbU3lbaBz1PzSs
+L0iLQq5fs88ygVVCQguZ69snsv1/Tf3cG6FXuhkIhsPHL666QdePqukcLTcVk814
+d1FUnhf3CKCQ7F2HycSfcYu4ombBc5hQ3zzXlS9HtcJri7mmlw0s70USEuR9N7yc
+m2y6AN8VQcRptg21OBiCWvu8oqIn8YG2myjYxpNjZ3aqDfGDSnlq/Ik9Uq/f2JvF
+DU1B/D9nXWB2fyDDdsKBIMy4+0uDHyFDp7EmCqIYAzigGK8hAoIBAQDChyamrg84
+58g+6e+Ni5fvrMyYfySA9AliUaBw+Oo18j6wU21EGUIVZNKTxeInluddl9o+73GM
+77Htd6nI3l8tOFCExJDG10hRKzcgqagDUPyYZS8To5qUnQifQdvg0DVyEYyu+jrj
+FSyjpP2IxTfqBD7I5xHUlsdOcZvKnuJaFix/SIRlQGZNkUuIRH8GazU3l5cJjkYQ
+qckK5sKDeqtC4MdEbf1DnwLTQafZrNaPMlrEFIAJe53qRuJa7PGx3hvaJIqJHuct
+4ju2Soq0Fz+2zYoOs0O2Qpy9An6tkGc9/xHCRz1mrk5T+F/ga+Z2DdkkJQE3iN9j
+gkrw1yjL+1ptAoIBAQC+0wH5qkO2eRH3VP6KmDZ89gWc69/GkSNotS7MadpMB58+
+D+E0b1wKUZEuZ6YNY1WmFua+EhTMIDoT7H+QTwZJ6lB5SUXnNZ3K5BmfEDHpEkB4
+/7wTNC2gUeHxrU2FXp1JtozmMgIVM+QiHLq/hY1+SgGu/FrCjjAktd+V1YlftbaO
+CBHMlHmR3zONy42bd0cAUTtoBXMBU3h/00LzeTJfIoQPj0bXYEkVRZVew6RBKHIi
+aTcAHXXYXoT0cpH/gz86LLaYOlGgkQe4EZ3KptgtNPmlKyMwlaDdzb8RDTVKuX6h
+Nq4svjVXA40fuRMGBRohrcuvmniJDL0bvIpo+IZhAoIBAQCY/Bs8mMUl9rs4J1ib
+U/5QovBguhUmSUYcCaAJWOkxNetxVpsnJEohcyJczxJ2GiqpI6SjPEJsGZIv9J0V
+eTUW5iQE1mYZyvN3ymnXiTW2LfoJjAIDCvamC/3c6bmXy4dMx1qBvg+TyXJK3JX7
++0ZnB6Gs9HTEYTQrkBN6vYu8V5X1ID3nN0d9l1f0uhHAJY47wz8oZ7jYn/6ZUTQG
+nPGVbhuoyGuWw7ZAGHF+MnynL23MqWasjoh51/dqlM/cF5aF75j4D9K19icJzQPa
+cDJOfMUnnHoTG74Ss6SWsWtQiYoY5Vf3qfu19C7K23DqxSdRUrzrRPySUMTJNh1C
+ltHdAoIBACLYtKTz+39eWJspcui51HkyjnRjK0yAB46vZG38vJESa/Ljm61uhRBB
+DJNpw37OUWD8QUmPzAW5WbiX/XnC1JCdgOcLDuIQHQ9NZy/Js2vcodT5GUuiCI5o
+74LzV0uUfGHS/cef73Om+dXFud+Yed3ekx4gTkqIWvQqSwFJ9FY8nHblsjYnJCPV
+3MuRXQUhus9HRlYKhFBNpAp+N/eniqR/NKFY5rEKgX0ixZXLO//5SLoMtbs3g/F+
+VUWPvocU6H6Gdta6Ro5rAXKIspFwGvWDLFAjHEbPbTYC2rZoRCa90UyJWLkhhitZ
+Plr6wPq89gaTcWQaNVjwDVAuPmCSHv4=
+-----END PRIVATE KEY-----
diff --git a/certs/root-safe_CA.pem b/certs/root-safe_CA.pem
new file mode 100644 (file)
index 0000000..3d0f12d
--- /dev/null
@@ -0,0 +1,89 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            02:d3:24:58:50:9a:63:62:01:9c:77:99:26:1d:07:d4:9a:6f:b8:31
+        Signature Algorithm: ecdsa-with-SHA384
+        Issuer: C=CA, L=Montreal, ST=Quebec, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Root CA 1
+        Validity
+            Not Before: Apr 25 13:26:55 2021 GMT
+            Not After : Apr 25 13:26:55 2041 GMT
+        Subject: C=CA, L=Montreal, ST=Quebec, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Root CA 1
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (256 bit)
+                pub:
+                    04:2b:ba:82:6e:03:14:e8:ee:9b:1d:0b:e3:80:e8:
+                    ef:1f:68:fd:b5:4d:24:50:9d:7a:04:07:b9:d1:c3:
+                    70:db:aa:c9:10:c3:3d:7f:26:a0:88:22:21:51:39:
+                    fd:51:10:8e:67:31:9e:5c:b2:be:60:94:01:75:7e:
+                    c3:ef:b3:ac:6c
+                ASN1 OID: prime256v1
+                NIST CURVE: P-256
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Subject Key Identifier: 
+                87:DD:FB:32:49:26:5E:13:F8:B7:F2:DF:EF:9C:F6:85:34:37:7A:D9
+            X509v3 Authority Key Identifier: 
+                87:DD:FB:32:49:26:5E:13:F8:B7:F2:DF:EF:9C:F6:85:34:37:7A:D9
+            X509v3 Certificate Policies: 
+                Policy: 1.3.6.1.4.1.7438.1.1
+                  CPS: http://certificates.safe.ca/repository/
+                Policy: 2.23.140.1.2.1
+    Signature Algorithm: ecdsa-with-SHA384
+    Signature Value:
+        30:45:02:20:50:43:0d:35:ec:ec:53:6e:3c:74:4e:7b:53:3f:
+        df:2d:d4:5d:8a:2a:0b:7a:21:c4:6e:93:04:70:b5:b5:74:2a:
+        02:21:00:a6:6e:95:77:72:9d:0e:bc:f0:15:5f:b7:02:cc:e1:
+        ac:b1:3d:f5:a6:99:01:23:30:3a:76:03:6b:ee:ac:92:fa
+-----BEGIN CERTIFICATE-----
+MIICyjCCAnCgAwIBAgIUAtMkWFCaY2IBnHeZJh0H1JpvuDEwCgYIKoZIzj0EAwMw
+gYQxCzAJBgNVBAYTAkNBMREwDwYDVQQHDAhNb250cmVhbDEPMA0GA1UECAwGUXVl
+YmVjMRIwEAYDVQQKDAlTQUZFIEluYy4xJDAiBgNVBAsMG0RpZ2l0YWwgQ2VydGlm
+aWNhdGUgU2lnbmluZzEXMBUGA1UEAwwOU0FGRSBSb290IENBIDEwHhcNMjEwNDI1
+MTMyNjU1WhcNNDEwNDI1MTMyNjU1WjCBhDELMAkGA1UEBhMCQ0ExETAPBgNVBAcM
+CE1vbnRyZWFsMQ8wDQYDVQQIDAZRdWViZWMxEjAQBgNVBAoMCVNBRkUgSW5jLjEk
+MCIGA1UECwwbRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTaWduaW5nMRcwFQYDVQQDDA5T
+QUZFIFJvb3QgQ0EgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCu6gm4DFOju
+mx0L44Do7x9o/bVNJFCdegQHudHDcNuqyRDDPX8moIgiIVE5/VEQjmcxnlyyvmCU
+AXV+w++zrGyjgb0wgbowDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
+HQYDVR0OBBYEFIfd+zJJJl4T+Lfy3++c9oU0N3rZMB8GA1UdIwQYMBaAFIfd+zJJ
+Jl4T+Lfy3++c9oU0N3rZMFcGA1UdIARQME4wQgYJKwYBBAG6DgEBMDUwMwYIKwYB
+BQUHAgEWJ2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuc2FmZS5jYS9yZXBvc2l0b3J5LzAI
+BgZngQwBAgEwCgYIKoZIzj0EAwMDSAAwRQIgUEMNNezsU248dE57Uz/fLdRdiioL
+eiHEbpMEcLW1dCoCIQCmbpV3cp0OvPAVX7cCzOGssT31ppkBIzA6dgNr7qyS+g==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
+TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
+cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
+WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
+ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
+MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
+h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
+A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
+T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
+B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
+B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
+KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
+OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
+jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
+qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
+rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
+hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
+ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
+3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
+NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
+ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
+TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
+jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
+oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
+4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
+mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
+emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
+-----END CERTIFICATE-----
diff --git a/certs/safeMDL.pem b/certs/safeMDL.pem
new file mode 100644 (file)
index 0000000..331cd2c
--- /dev/null
@@ -0,0 +1,110 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            2a:01:e0:a5:fb:80:10:00:00:00:02
+        Signature Algorithm: ecdsa-with-SHA384
+        Issuer: C=CA, L=Montreal, ST=Quebec, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Root CA 1
+        Validity
+            Not Before: Jan 20 17:19:55 2024 GMT
+            Not After : Jun  7 17:19:55 2051 GMT
+        Subject: C=CA, O=SAFE Inc., OU=Digital Certificate Signing, CN=SAFE Midle Ground CA (2024) - SHA384 - 3
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:d1:52:9b:dc:10:57:6d:9a:0e:09:5b:1b:aa:fb:
+                    76:8c:65:b3:f2:ca:75:36:8f:c0:cb:82:d8:2f:5b:
+                    0e:25:0c:5f:fc:18:94:41:87:5d:75:eb:92:ec:2a:
+                    87:14:ec:5f:cc:f6:8f:bf:db:4e:a3:07:aa:ec:90:
+                    3a:48:43:b9:01:84:42:fb:34:0b:06:5f:d8:e4:6d:
+                    e7:55:8f:f6:ad:98:c4:7d:6f:a8:39:de:f8:70:94:
+                    71:f3:2f:24:1b:3b:ab:42:70:d8:6c:06:ef:81:af:
+                    fa:f7:68:77:66:0e:60:12:df:80:bb:b4:92:4a:1f:
+                    3e:52:2d:f5:9a:e3:ba:26:d3:88:68:aa:11:88:0f:
+                    b8:be:7e:e3:d7:88:ce:86:09:1a:a3:2c:ce:74:c1:
+                    d7:d6:7a:c4:b5:04:1e:25:ef:b7:15:6a:16:27:4d:
+                    0f:ed:af:46:fc:a0:57:a2:6d:fe:91:c3:c7:1f:87:
+                    06:fe:5a:e2:a8:de:33:67:ae:6d:06:84:f2:15:1d:
+                    9d:ff:11:cf:be:6f:a9:a5:13:13:0b:ef:67:19:1f:
+                    ea:a8:ed:f0:db:f2:1f:ba:8c:a5:1e:b3:54:b7:68:
+                    c3:37:85:db:01:2e:83:4d:e0:06:be:93:54:b0:dc:
+                    31:23:98:15:b7:ec:b5:82:57:7a:7c:34:6c:3b:2b:
+                    3b:fa:b3:12:9a:63:63:d9:54:fd:bf:a1:ee:3c:a4:
+                    47:83:04:60:b9:9b:74:8f:f7:92:93:1d:f5:ea:98:
+                    87:c4:c9:de:d6:b8:5f:bf:fc:2e:41:e0:55:38:65:
+                    80:54:02:c6:d9:bd:7d:51:96:ba:55:ad:bf:01:ce:
+                    31:21:54:1e:56:16:79:7b:97:1a:53:92:86:80:54:
+                    ef:e9:75:ad:21:45:37:82:54:52:ed:c3:37:8c:11:
+                    ab:63:dd:64:ae:15:b4:f5:cc:02:2f:61:ab:42:d6:
+                    c5:a1:c0:dd:19:ef:70:f1:7f:6d:31:af:4e:60:bb:
+                    83:a1:f7:49:a5:de:94:dd:31:c1:74:4b:11:73:da:
+                    4d:f4:4e:90:9e:ae:dd:c0:61:d6:6b:54:3f:3a:78:
+                    c3:8b:e4:0e:ba:c6:9c:f3:3f:fb:6c:34:7c:ff:3d:
+                    65:d7:0b:ec:4c:19:37:51:37:c5:3b:34:7e:55:85:
+                    10:82:33:30:7f:ff:95:63:5b:45:3c:45:90:34:fb:
+                    1c:5e:ef:64:a3:a7:a8:58:0f:d0:97:6a:de:5a:8f:
+                    29:51:6b:14:01:b1:ec:59:74:47:0e:d9:d0:1a:78:
+                    df:16:e5:fe:5b:8b:95:48:0f:26:20:58:ef:14:6a:
+                    97:ca:c0:b3:7d:ac:7f:8a:6c:59:be:1b:fc:a0:47:
+                    e7:57:b1
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            Authority Information Access: 
+                CA Issuers - URI:http://certificates.safe.ca/cacert/safeMDL.pem
+                OCSP - URI:http://certificates/safe.ca/chkcertstats
+            X509v3 Certificate Policies: 
+                Policy: 1.3.6.1.4.1.7438.1.1
+                  CPS: http://certificates.safe.ca/repository/
+                Policy: 2.23.140.1.2.1
+            X509v3 Basic Constraints: critical
+                CA:TRUE, pathlen:0
+            X509v3 CRL Distribution Points: 
+                Full Name:
+                  URI:http://certificates.safe.ca/repository/revoklist.pem
+            X509v3 Subject Alternative Name: 
+                DNS:certificates.safe.ca, IP Address:192.219.254.53
+            X509v3 Authority Key Identifier: 
+                87:DD:FB:32:49:26:5E:13:F8:B7:F2:DF:EF:9C:F6:85:34:37:7A:D9
+            X509v3 Subject Key Identifier: 
+                9C:BE:0B:C0:22:76:F5:CF:BC:FD:78:9A:92:77:20:FE:BF:96:1E:D8
+    Signature Algorithm: ecdsa-with-SHA384
+    Signature Value:
+        30:46:02:21:00:ff:21:78:ff:d7:43:e7:9d:7d:dd:e6:f1:89:
+        f9:39:8a:14:e0:46:ca:b2:f2:59:a1:09:70:a0:2d:8b:66:a1:
+        65:02:21:00:d6:cf:8e:54:06:f0:d3:4c:23:f6:9d:a7:d5:b7:
+        23:6d:b9:c8:18:15:63:a3:92:98:3c:dc:25:18:71:1c:74:68
+-----BEGIN CERTIFICATE-----
+MIIFejCCBR+gAwIBAgILKgHgpfuAEAAAAAIwCgYIKoZIzj0EAwMwgYQxCzAJBgNV
+BAYTAkNBMREwDwYDVQQHDAhNb250cmVhbDEPMA0GA1UECAwGUXVlYmVjMRIwEAYD
+VQQKDAlTQUZFIEluYy4xJDAiBgNVBAsMG0RpZ2l0YWwgQ2VydGlmaWNhdGUgU2ln
+bmluZzEXMBUGA1UEAwwOU0FGRSBSb290IENBIDEwIBcNMjQwMTIwMTcxOTU1WhgP
+MjA1MTA2MDcxNzE5NTVaMHoxCzAJBgNVBAYTAkNBMRIwEAYDVQQKDAlTQUZFIElu
+Yy4xJDAiBgNVBAsMG0RpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzExMC8GA1UE
+AwwoU0FGRSBNaWRsZSBHcm91bmQgQ0EgKDIwMjQpIC0gU0hBMzg0IC0gMzCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANFSm9wQV22aDglbG6r7doxls/LK
+dTaPwMuC2C9bDiUMX/wYlEGHXXXrkuwqhxTsX8z2j7/bTqMHquyQOkhDuQGEQvs0
+CwZf2ORt51WP9q2YxH1vqDne+HCUcfMvJBs7q0Jw2GwG74Gv+vdod2YOYBLfgLu0
+kkofPlIt9ZrjuibTiGiqEYgPuL5+49eIzoYJGqMsznTB19Z6xLUEHiXvtxVqFidN
+D+2vRvygV6Jt/pHDxx+HBv5a4qjeM2eubQaE8hUdnf8Rz75vqaUTEwvvZxkf6qjt
+8NvyH7qMpR6zVLdowzeF2wEug03gBr6TVLDcMSOYFbfstYJXenw0bDsrO/qzEppj
+Y9lU/b+h7jykR4MEYLmbdI/3kpMd9eqYh8TJ3ta4X7/8LkHgVThlgFQCxtm9fVGW
+ulWtvwHOMSFUHlYWeXuXGlOShoBU7+l1rSFFN4JUUu3DN4wRq2PdZK4VtPXMAi9h
+q0LWxaHA3RnvcPF/bTGvTmC7g6H3SaXelN0xwXRLEXPaTfROkJ6u3cBh1mtUPzp4
+w4vkDrrGnPM/+2w0fP89ZdcL7EwZN1E3xTs0flWFEIIzMH//lWNbRTxFkDT7HF7v
+ZKOnqFgP0Jdq3lqPKVFrFAGx7Fl0Rw7Z0Bp43xbl/luLlUgPJiBY7xRql8rAs32s
+f4psWb4b/KBH51exAgMBAAGjggGyMIIBrjAOBgNVHQ8BAf8EBAMCAQYwgYAGCCsG
+AQUFBwEBBHQwcjA6BggrBgEFBQcwAoYuaHR0cDovL2NlcnRpZmljYXRlcy5zYWZl
+LmNhL2NhY2VydC9zYWZlTURMLnBlbTA0BggrBgEFBQcwAYYoaHR0cDovL2NlcnRp
+ZmljYXRlcy9zYWZlLmNhL2Noa2NlcnRzdGF0czBXBgNVHSAEUDBOMEIGCSsGAQQB
+ug4BATA1MDMGCCsGAQUFBwIBFidodHRwOi8vY2VydGlmaWNhdGVzLnNhZmUuY2Ev
+cmVwb3NpdG9yeS8wCAYGZ4EMAQIBMBIGA1UdEwEB/wQIMAYBAf8CAQAwRQYDVR0f
+BD4wPDA6oDigNoY0aHR0cDovL2NlcnRpZmljYXRlcy5zYWZlLmNhL3JlcG9zaXRv
+cnkvcmV2b2tsaXN0LnBlbTAlBgNVHREEHjAcghRjZXJ0aWZpY2F0ZXMuc2FmZS5j
+YYcEwNv+NTAfBgNVHSMEGDAWgBSH3fsySSZeE/i38t/vnPaFNDd62TAdBgNVHQ4E
+FgQUnL4LwCJ29c+8/Xiakncg/r+WHtgwCgYIKoZIzj0EAwMDSQAwRgIhAP8heP/X
+Q+edfd3m8Yn5OYoU4EbKsvJZoQlwoC2LZqFlAiEA1s+OVAbw00wj9p2n1bcjbbnI
+GBVjo5KYPNwlGHEcdGg=
+-----END CERTIFICATE-----
diff --git a/conf/.mailleur.conf.swp b/conf/.mailleur.conf.swp
new file mode 100644 (file)
index 0000000..b1399f6
Binary files /dev/null and b/conf/.mailleur.conf.swp differ
diff --git a/conf/blacklister.conf b/conf/blacklister.conf
new file mode 100644 (file)
index 0000000..ff146c4
--- /dev/null
@@ -0,0 +1,26 @@
+#dnsbl.sorbs.net
+#t1.dnsbl.net.au
+#checked 2025-07-12 by preference order
+#The SpamCop Blocking List (SCBL) lists IP addresses which
+#have transmitted reported email to SpamCop users. SpamCop
+-1     bl.spamcop.net                  127.0.0.2
+#Spamhaus Block List (SBL) is a realtime database of IP addresses
+#of spam sources, including known spammers, spam gangs,
+#spam operations and spam support services. 
+-1     sbl.spamhaus.org                127.0.0.2,127.0.0.3,127.0.0.9
+#b.barracudacentral.org will return the standard
+#127.0.0.2 IP address when queried if the SMTP server is listed.
+-6     b.barracudacentral.org          127.0.0.2
+#www.blocklist.de is a free and voluntary service provided
+#by a Fraud/Abuse-specialist,
+-1     bl.blocklist.de                 127.0.0.9,127.0.0.13
+# ips.backscatterer.org for scoring or rejecting misdirected
+#bounces and misdirected autoresponders 
+-1     ips.backscatterer.org           127.0.0.2
+#DroneBL is a realtime monitor of abusable IPs
+#-1    dnsbl.dronebl.org       
+#The s5h blacklist is a real-time IP-based blacklist that is
+#maintained by the System 5 Hosting (S5H) organization.
+#-1    all.s5h.net                     127.0.0.2
+#Junk Email Filter is a front end email spam filtering 
+-1     hostkarma.junkemailfilter.co    127.0.0.2
diff --git a/conf/mailleur.conf b/conf/mailleur.conf
new file mode 100644 (file)
index 0000000..3fb243b
--- /dev/null
@@ -0,0 +1,80 @@
+#file used to set environment configuration
+#------------------------------------------------
+#Defining application name
+APPNAME=mailleur
+#root working directoy definition for the whole application
+ROOTBASE="/"
+#defining local default domain
+DFLTDOMAIN="localdomain"
+#defining the local REALM
+#Caution! the realm is hardcoded within data-base
+#and USED too to hash the user passwd
+#Changing this value coule be a trouble make 
+#(entering again ALL user password)
+REALM="mailleur-email"
+#------------------------------------------------
+#list of listening port to do SMTP protocole
+#format
+#protocol|ipnum|port|iteration[,protocol|ipnum|port|iteration]++
+#example
+#stmtps|ipnum|465|5    -> protocol smtps|IP number|port 465|5 iterations
+#|ipnum|||             -> smtp|ipnum|25|2
+#||,smtps||465|2       -> smtp|0.0.0.0|25|2 + smtps||465|2
+#||                    -> smtp|0.0.0.0|25|2
+#Examples for test purpose
+#SMTPPORTS="|127.127.10.25|1025|,smtps|127.127.10.25|1465|1"
+#SMTPPORTS="|127.127.10.25|1025|"
+#SMTPPORTS="smtps|127.127.10.25|1465|1"
+#Defaults (production)
+SMTPPORTS="|||5,smtps||465|3,smtp||587|2"
+#------------------------------------------------
+#SSL Security level default value
+#CIPHER_LIST="DEFAULT"
+#SSL_SECURITY=2
+#------------------------------------------------
+#Defining SERVER mode Certificate data
+CA_ROOT_SRV="/etc/pki/tls/make-ca/ca-bundle.crt"
+CA_VERIFY_SRV=0        #to check PEER/client remote certificate
+#If certificate set via do_dns_tlsa.sh (letsencrypt)
+#CA_CERT_SRV="/etc/certbot/mailleur/mailleur-fullchain.pem"
+#CA_KEY_SRV="/etc/certbot/mailleur/mailleur-key.pem"
+#If certificate are self signed (default installation)
+#CA_CERT_SRV="/etc/pki/mailleur/mailleur-cert.pem"
+#CA_KEY_SRV="/etc/pki/mailleur/mailleur-key.pem"
+#Symbolic link set at installation
+CA_CERT_SRV="/etc/pki/mailleur/mailleur-cert.pem"
+CA_KEY_SRV="/etc/pki/mailleur/mailleur-key.pem"
+#------------------------------------------------
+#Defining CLIENT mode Certificate data
+CA_ROOT_CLT="/etc/pki/tls/make-ca/ca-bundle.crt"
+CA_VERIFY_CLT=0        #to check PEER/server remote certificate
+#If certificate set via do_dns_tlsa.sh (letsencrypt)
+#CA_CERT_CLT="/etc/certbot/mailleur/mailleur-fullchain.pem"
+#CA_KEY_CLT="/etc/certbot/mailleur/mailleur-key.pem"
+#If certificate are self signed (default installation)
+#CA_CERT_CLT="/etc/pki/mailleur/mailleur-cert.pem"
+#CA_KEY_CLT="/etc/pki/mailleur/mailleur-key.pem"
+#Symbolic link set at installation
+CA_CERT_CLT="/etc/mailleur/pki/mailleur-cert.pem"
+CA_KEY_CLT="/etc/mailleur/pki/mailleur-key.pem"
+#------------------------------------------------
+#Configured for Postgresql database
+#DB_TYPE can be either POSTGRESQL,MYSQL, default POSTGRESQL
+DB_TYPE=TO_BE_DEFINED
+DB_NAME=mailleur
+DB_HOST=localhost
+DB_PORT=TO_BE_DEFINED
+DB_LANG="UTF-8"
+#------------------------------------------------
+#Dovecot storage directory
+DOV_MAILDIR="/var/spool/mailleur/mails"
+#------------------------------------------------
+#The list of public blacklist serveur
+BLACKLISTER=/etc/mailleur/blacklister.conf
+#the list of IP from which we accept to relay email
+RELAYABLE=/etc/mailleur/relayed.conf
+#------------------------------------------------
+#Minimal credit level to which remote is rejected at once
+#if credit is equal or below that level
+RJCTCRED=-50
+#------------------------------------------------
diff --git a/conf/mailleur.conf.dvl b/conf/mailleur.conf.dvl
new file mode 100644 (file)
index 0000000..0389242
--- /dev/null
@@ -0,0 +1,66 @@
+#file used to set environment configuration
+#Used for developpement purpose ONLY
+#------------------------------------------------
+#Defining application name
+APPNAME=mailleur
+#root working directoy definition for the whole application
+ROOTBASE="/home/jmp/safe-mailleur/mailleur/test_area/"
+#defining local default domain
+DFLTDOMAIN="example.com"
+#defining the local REALM
+#Caution! the realm is hardcoded within data-base
+#and USED too to hash the user passwd
+#Changing this value coule be a trouble make 
+#(entering again ALL user password)
+REALM="mailleur-email"
+#------------------------------------------------
+#list of listening port to do SMTP protocole
+#format
+#protocol|ipnum|port|iteration[,protocol|ipnum|port|iteration]++
+#example
+#stmtps|ipnum|465|5    -> protocol smtps|IP number|port 465|5 iterations
+#|ipnum|||             -> smtp|ipnum|25|2
+#||,smtps||465|2       -> smtp|0.0.0.0|25|2 + smtps||465|2
+#||                    -> smtp|0.0.0.0|25|2
+#SMTPPORTS="||1025"
+#SMTPPORTS="|127.127.10.25|1025|,smtps|127.127.10.25|1026|1"
+#SMTPPORTS="|127.127.10.25|1025|"
+#SMTPPORTS="smtps|127.127.10.25|1065|1"
+#------------------------------------------------
+#SSL Security level
+#CIPHER_LIST="ECDHE-ECDSA-AES256-GCM-SHA384"
+#SSL_SECURITY=3
+#------------------------------------------------
+#Defining SERVER mode Certificate data
+CA_ROOT_SRV="/etc/pki/tls/make-ca/ca-bundle.crt"
+CA_CERT_SRV="./certs/mailleur_server-chain-cert_x509.pem"
+CA_KEY_SRV="./certs/mailleur_server-key.pem"
+CA_VERIFY_SRV=0        #to check PEER/client remote certificate
+#------------------------------------------------
+#Defining CLIENT mode Certificate data
+CA_ROOT_CLT="/etc/pki/tls/make-ca/ca-bundle.crt"
+CA_CERT_CLT="./certs/localhost-chain-cert.pem"
+CA_KEY_CLT="./certs/localhost-key.pem"
+CA_VERIFY_CLT=0        #to check PEER/server remote certificate
+#------------------------------------------------
+#Configured for Postgresql database
+#DB_TYPE can be either POSTGRESQL,MYSQL, default POSTGRESQL
+#DB_NAME=mailleur
+#DB_HOST=localhost
+#DB_LANG="UTF-8"
+#DB_TYPE=POSTGRESQL
+#DB_PORT=5432
+#DB_TYPE=MYSQL
+#DB_PORT=3306
+
+#------------------------------------------------
+#Dovecot storage directory
+DOV_MAILDIR="/var/spool/mailleur/mails"
+#------------------------------------------------
+SMTPPORTS="|127.127.10.25|1025|5,smtps|127.127.10.26|1465|3,smtp|127.127.10.26|1587|2"
+#------------------------------------------------
+#The list of public blacklist serveur
+BLACKLISTER=/etc/mailleur/blacklister.conf
+#the list of IP from which we accept to relay email
+RELAYABLE=/etc/mailleur/relayed.conf.dvl
+#------------------------------------------------
diff --git a/conf/relayed.conf b/conf/relayed.conf
new file mode 100644 (file)
index 0000000..114af58
--- /dev/null
@@ -0,0 +1,5 @@
+#--------------------------------------------------------
+##Private address block which email can be relayed from
+127.0.0.0/8                    #local loop number
+#--------------------------------------------------------
+#Setup you own list
diff --git a/conf/relayed.conf.dvl b/conf/relayed.conf.dvl
new file mode 100644 (file)
index 0000000..5c217dc
--- /dev/null
@@ -0,0 +1,6 @@
+#--------------------------------------------------------
+##Private address block which email can be relayed from
+127.0.0.0/8                    #local loop number
+192.168.254.0/24               #local network
+#--------------------------------------------------------
+#Setup you own list
diff --git a/cron/mailleur.cron b/cron/mailleur.cron
new file mode 100644 (file)
index 0000000..bb01438
--- /dev/null
@@ -0,0 +1,6 @@
+#---------------------------------------------------
+#shell script to manage rpplication ecurring homeworking task
+#---------------------------------------------------
+#to update lets encrypt certificate
+#1 1 1 * * root  PATH=/usr/sbin:$PATH /usr/lib/mailleur/support/do_dns_tlsa.sh
+
diff --git a/data-feed/dbgfeed.tst b/data-feed/dbgfeed.tst
new file mode 100644 (file)
index 0000000..efe12e1
--- /dev/null
@@ -0,0 +1,17 @@
+#====================================================
+#starting test 
+T:debug test function
+R:220 mailleur.example.com ESMTP (cleartext) ...
+S:HELO example.com
+R:250 mailleur.example.com link (cleartext) ready...
+C:GOTLS
+R:250 Link now encryp...
+S:EHLO example.com
+R:250-mailleur.example.com link (crypted) ready, your IP/FQDN...
+R:250-SIZE 52428800
+R:250-8BITMIME
+R:250-ENHANCEDSTATUSCODES
+R:250-AUTH PLAIN LOGIN
+R:250 HELP
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/extfeed00.tst b/data-feed/extfeed00.tst
new file mode 100644 (file)
index 0000000..fbe3a87
--- /dev/null
@@ -0,0 +1,18 @@
+#to test feeder againt an production email serveur
+T:external email serveur
+R:220 safemail3.safe.ca ESMTP...
+S:EHLO example.com
+#R:250-safemail3.safe.ca Hum! devel5.safe.ca [192.168.254.70], No MX on your <example.com> hello
+R:250-safemail3.safe.ca Hum!...
+R:250-ENHANCEDSTATUSCODES
+R:250-8BITMIME
+R:250-SIZE
+R:250-AUTH PLAIN LOGIN
+R:250-STARTTLS
+R:250 HELP
+#send a empty ehlo
+S:EHLO
+R:501 syntax error in parameters or arguments (domain part missing), disconnecting
+R:Disconnected
+#S:QUIT
+#R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feed000.tst b/data-feed/feed000.tst
new file mode 100644 (file)
index 0000000..5e1bf21
--- /dev/null
@@ -0,0 +1,66 @@
+#====================================================
+#      WAIT X          - Stop for X seconds
+#      RESTART         - Restart connection with remote
+#      COMMENT START   - START commenting mode
+#      COMMENT STOP    - STOP commenting mode
+# R: incoming data
+# S: outgoing data
+# T: Test titre or Info
+C:COMMENT START
+#====================================================
+#Please update /etc/hosts with with following
+#start of /etc/host addon
+#----------------------------------------------------
+#Definition to validate mailleur daemon (mailleur) server
+#The test serveur itself
+127.127.10.25                   mailleur.example.com    mailleur
+#The example.com email feeder
+127.127.0.1                    feed1.example.com       feed1
+127.127.0.2                    feed2.example.com       feed2
+#remote access
+#127.168.0.1                    no reverss address detection
+127.168.10.1                    foreign1.badserver.tld  foreign1
+#----------------------------------------------------
+#end of /etc/host addon
+#====================================================
+C:COMMENT STOP
+T:(feed000) Sending a very Simple Email with ONE local delivery
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+S:HELO example.com
+#R:250-mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.127.0.2/feed2.example.com]
+R:250 mailleur.example.com, link (cleartext) ready,...
+S:MAIL FROM: <postmaster@example.com>
+R:250 2.1.3 postmaster@example.com sender ok
+S:RCPT TO: <webmaster@example.com>
+R:250 5.6.7 Address will be processed <webmaster@example.com>
+S:RCPT TO: <postmaster@example.com>
+R:250 5.6.7 Address will be processed <postmaster@example.com>
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: (feed000) Very Simple email contents
+C:T
+D:From: Maitre Post <postmaster@example.com>
+D:To: Maitre WEB <webmaster@example.com>
+D:
+D:this email is for local test purpose only
+D:with 2 known recipients beeing as:
+D:postmaster and webmaster.
+D:
+D:Checking line with one '.' transmissioon
+D:.
+D:Previous line is one single dot
+D:
+D:you should have character '|' as last characeter 
+D:of this email (after End Of Text).
+D:End Of Text|
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
+#-------------------------------------------------------------------------
diff --git a/data-feed/feed001.tst b/data-feed/feed001.tst
new file mode 100644 (file)
index 0000000..50655e7
--- /dev/null
@@ -0,0 +1,52 @@
+#====================================================
+T:(feed001) Sending a a simple mail to remote server
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+S:EHLO example.com
+#R:250-mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.127.0.2/feed2.example.com]
+R:250-mailleur.example.com, link (cleartext) ready,...
+R:250-STARTTLS
+R:250-AUTH DIGEST-MD5
+R:250-SIZE 52428800
+R:250-8BITMIME
+R:250 ENHANCEDSTATUSCODES
+C:GOTLS
+S:EHLO example.com
+R:250-mailleur.example.com, link (crypted) ready,...
+R:250-AUTH PLAIN LOGIN DIGEST-MD5
+R:250-SIZE 52428800
+R:250-8BITMIME
+R:250 ENHANCEDSTATUSCODES
+S:MAIL FROM: <postmaster@example.com> SIZE=1024
+R:250 2.1.3 postmaster@example.com sender ok
+S:RCPT TO: <user1@mailref1.example.com>
+R:250 5.6.7 Address will be processed <user1@mailref1.example.com>
+S:RCPT TO: <user2@mailref1.example.com>
+R:250 5.6.7 Address will be processed <user2@mailref1.example.com>
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: (feed001) Very Simple email contents
+D:From: Maitre Post <postmaster@example.com>
+D:To: Remote person <user1@mailref1.example.com>
+D:CC: Other person <user2@mailref1.example.com>
+C:T
+D:
+D:FIRST Line
+D:SECOND Line
+D:LAST Line
+D:.
+D:this is a line with a dot
+D:
+D:.
+D:.single dot
+D:end
+D:ligne court|
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feed002.tst b/data-feed/feed002.tst
new file mode 100644 (file)
index 0000000..361f350
--- /dev/null
@@ -0,0 +1,34 @@
+T:(feed002) Sending a very Simple Email to 2 domains
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+S:HELO example.com
+R:250 mailleur.example.com, link (cleartext) ready,...
+S:MAIL FROM: <trouble@mailref1.example.com>
+R:250 2.1.3 trouble@mailref1.example.com sender ok
+S:RCPT TO: <webmaster@example.com>
+R:250 5.6.7 Address will be processed <webmaster@example.com>
+S:RCPT TO: <user1@mailref1.example.com>
+R:250 5.6.7 Address will be processed <user1@mailref1.example.com>
+S:RCPT TO: <user1@posdb.example.com>
+R:250 5.6.7 Address will be processed <user1@posdb.example.com>
+S:RCPT TO: <user1@mardb.example.com>
+R:250 5.6.7 Address will be processed <user1@mardb.example.com>
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: (feed002) Very Simple email contents 2 domains
+D:From: Maitre Post <postmaster@example.com>
+D:To: Maitre WEB <webmaster@example.com>
+D:
+D:.single dot, next is an empty line starting with a '.'
+D:.
+D:This email is sent to 3 domain X 1 users
+D:
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feed003.tst b/data-feed/feed003.tst
new file mode 100644 (file)
index 0000000..a15ee68
--- /dev/null
@@ -0,0 +1,59 @@
+T:(feed003) Sending two email within the same session
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#=========================================================================
+S:HELO example.com
+R:250 mailleur.example.com, link (cleartext) ready,...
+S:MAIL FROM: <trouble@mailref1.example.com>
+R:250 2.1.3 trouble@mailref1.example.com sender ok
+S:RCPT TO: <user1@mailref1.example.com>
+R:250 5.6.7 Address will be processed <user1@mailref1.example.com>
+S:RCPT TO: <user2@mailref1.example.com>
+R:250 5.6.7 Address will be processed <user2@mailref1.example.com>
+S:RCPT TO: <no-one@example.com>
+R:551 5.6.0 <no-one@example.com> unknown user
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: feed003 email first part
+D:From: Tester <trouble@mailref1.example.com>
+D:To: A User1 <user1@mailref1.example.com>
+D:Cc: A User2 <user2@mailref1.example.com>
+D:
+D:This email was transmitted by feed003 first session
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+#=========================================================================
+#Testing Reset sequence
+S:RSET
+R:250-2.1.0 flushed session ...
+R:250 2.1.0 opening new session...
+#-------------------------------------------------------------------------
+S:HELO example.com
+R:250 mailleur.example.com, link (cleartext) ready,...
+S:MAIL FROM: <postmaster@mailref1.example.com>
+R:250 2.1.3 postmaster@mailref1.example.com sender ok
+S:RCPT TO: <user2@mailref1.example.com>
+R:250 5.6.7 Address will be processed <user2@mailref1.example.com>
+S:RCPT TO: <user1@mailref1.example.com>
+R:250 5.6.7 Address will be processed <user1@mailref1.example.com>
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: feed003 email second part
+D:From: Tester <trouble@mailref1.example.com>
+D:To: A User1 <user1@mailref1.example.com>
+D:Cc: A User2 <user2@mailref1.example.com>
+D:
+D:This email was transmitted by feed003 second session
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+#=========================================================================
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feed004.tst b/data-feed/feed004.tst
new file mode 100644 (file)
index 0000000..71c82e3
--- /dev/null
@@ -0,0 +1,32 @@
+T:(feed004) Email using UTF-8 characters on local delivery
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+S:HELO example.com
+R:250 mailleur.example.com, link (cleartext) ready,...
+S:MAIL FROM: <postmaster@example.com>
+R:250 2.1.3 postmaster@example.com sender ok
+S:RCPT TO: <utf8-áö_üñ@example.com>
+R:250 5.6.7 Address will be processed <utf8-áö_üñ@example.com>
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: (feed004) Email using UTF-8 characters
+C:T
+D:From: Maitre Post <postmaster@example.com>
+D:To: Maitre WEB <webmaster@example.com>
+D:
+D:this email is for local test purpose only
+D:with 2 known recipients beeing as:
+D:postmaster and webmaster.
+D:
+D:This line include accentued latin char
+D:via the reciption email address <áöüñ@example.com>
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
+#-------------------------------------------------------------------------
diff --git a/data-feed/feed005.tst b/data-feed/feed005.tst
new file mode 100644 (file)
index 0000000..4eac57e
--- /dev/null
@@ -0,0 +1,13 @@
+T:(feed005) Email testing remote server credential
+C:CONNECT
+#====================================================
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+C:ORGN: 213.209.157.107
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+S:helo example.com
+R:250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[213.209.157.107/No.Reverse]
+S:MAIL FROM: <trouble@mailref1.example.com>
+R:556-4.5.7 Originator server IP [213.209.157.107] black listed...
+R:556 5.5.4 Closing connection
+R:Disconnected
+#-------------------------------------------------------------------------
diff --git a/data-feed/feed006.tst b/data-feed/feed006.tst
new file mode 100644 (file)
index 0000000..a10ec7f
--- /dev/null
@@ -0,0 +1,40 @@
+T:(feed006) Email testing BAD remote server credential in case of authentication
+C:CONNECT
+#====================================================
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+C:ORGN: 213.209.157.108
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+C:GOTLS
+S:EHLO example.com
+R:250-mailleur.example.com, link (crypted) ready, your IP/FQDN=[213.209.157.108/No.Reverse]
+R:250-AUTH PLAIN LOGIN DIGEST-MD5
+R:250-SIZE 52428800
+R:250-8BITMIME
+R:250 ENHANCEDSTATUSCODES
+S:AUTH LOGIN
+R:334 VXNlcm5hbWU6
+S:dXRmOC3DocO2X8O8w7FAZXhhbXBsZS5jb20=
+R:334 UGFzc3dvcmQ6
+S:dXRmOC3DocO2X8O8w7E=
+R:235 5.7.5 authentication successful
+S:MAIL FROM: <trouble@mailref1.example.com>
+R:250 2.1.3 trouble@mailref1.example.com sender ok
+S:RCPT TO: <user1@posdb.example.com>
+R:250 5.6.7 Address will be processed <user1@posdb.example.com>
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: (feed006) A email to a not existing user after authentication
+D:From: Trouble maker <trouble@mailref1.example.com>
+D:To: Not existing <dom1user3@mailref1.example.com>
+C:T
+D:
+D:This email check relaying after authentication
+D:
+D:
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feed007.tst b/data-feed/feed007.tst
new file mode 100644 (file)
index 0000000..b907720
--- /dev/null
@@ -0,0 +1,35 @@
+T:(feed007) Testing if remote server is within relay list
+#this email should be REJECT as relaying intended
+C:CONNECT
+#====================================================
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+C:ORGN: 192.168.254.25
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+C:GOTLS
+S:EHLO example.com
+R:250-mailleur.example.com, link (crypted) ready, your IP/FQDN=[192.168.254.25/No.Reverse]
+R:250-AUTH PLAIN LOGIN DIGEST-MD5
+R:250-SIZE 52428800
+R:250-8BITMIME
+R:250 ENHANCEDSTATUSCODES
+S:MAIL FROM: <trouble@mailref1.example.com>
+R:250 2.1.3 trouble@mailref1.example.com sender ok
+S:RCPT TO: <user1@posdb.example.com>
+R:250 5.6.7 Address will be processed <user1@posdb.example.com>
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: (feed006) A email to a not existing user after authentication
+D:From: Trouble maker <trouble@mailref1.example.com>
+D:To: Not existing <dom1user3@mailref1.example.com>
+C:T
+D:
+D:This email check relaying after authentication
+D:
+D:
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feed008.tst b/data-feed/feed008.tst
new file mode 100644 (file)
index 0000000..18ea76b
--- /dev/null
@@ -0,0 +1,40 @@
+T:(feed008) Email testing remote server relaying
+C:CONNECT
+#====================================================
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#take a "random" imp number as server remote IP
+C:ORGN: 18.209.86.113
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+S:EHLO keeper-us-east-1d.mxtoolbox.com
+R:250-mailleur.example.com, link (cleartext) ready, your IP/FQDN=[18.209.86.113/keeper-us-east-1d.mxtoolbox.com]
+R:250-STARTTLS
+R:250-AUTH DIGEST-MD5
+R:250-SIZE 52428800
+R:250-8BITMIME
+R:250 ENHANCEDSTATUSCODES
+S:MAIL FROM:<supertool@mxtoolboxsmtpdiag.com>
+R:250 2.1.3 supertool@mxtoolboxsmtpdiag.com sender ok
+S:RCPT TO: <webmaster@example.com>
+R:250 5.6.7 Address will be processed <webmaster@example.com>
+S:RCPT TO:<test@mxtoolboxsmtpdiag.com>
+R:555 2.8.0 No MX nor IP for for domain <mxtoolboxsmtpdiag.com>
+S:RCPT TO: <user1@posdb.example.com>
+R:555 2.8.0 No relay accepted for domain <posdb.example.com>
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: (feed008) sending email to local only
+D:From: Maitre Post <postmaster@example.com>
+D:To: Maitre WEB <webmaster@example.com>
+D:
+D:.single dot, next is an empty line starting with a '.'
+D:.
+D:This email is sent to 3 domain X 1 users
+D:
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feed009.tst b/data-feed/feed009.tst
new file mode 100644 (file)
index 0000000..66b5911
--- /dev/null
@@ -0,0 +1,35 @@
+T:(feed009) Making sure we accept local IP email
+C:CONNECT
+#====================================================
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#take a "random" imp number as server remote IP
+C:ORGN: 209.85.222.45
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+S:EHLO keeper-us-east-1d.mxtoolbox.com
+R:250-mailleur.example.com, link (cleartext) ready, your IP/FQDN=[209.85.222.45/mail-ua1-f45.google.com]
+R:250-STARTTLS
+R:250-AUTH DIGEST-MD5
+R:250-SIZE 52428800
+R:250-8BITMIME
+R:250 ENHANCEDSTATUSCODES
+S:MAIL FROM:<nobody@gmail.com> SIZE=2958
+R:250 2.1.3 nobody@gmail.com sender ok
+S:RCPT TO:<user1@mailleur.example.com>
+R:250 5.6.7 Address will be processed <user1@mailleur.example.com>
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: (feed008) sending email to local only
+D:From: A Nobody <nobody@gmail.com>
+D:To: A simple very local user <user1@mailleur.example.com>
+D:
+D:this ia an email "from" gmail.
+D:test text text
+D:
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feed010.tst b/data-feed/feed010.tst
new file mode 100644 (file)
index 0000000..8620b80
--- /dev/null
@@ -0,0 +1,29 @@
+#====================================================
+T:(feed010) Testing if helo and ehlo fdqdn is  OK
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+S:HELO      [192.219.254.34]     
+R:250 mailleur.example.com, link (cleartext) ready,...
+#====================================================
+S:HELO      192.219.254.35     
+R:250 mailleur.example.com, link (cleartext) ready,...
+#====================================================
+S:HELO     domain
+R:250 mailleur.example.com, link (cleartext) ready,...
+#====================================================
+S:HELO 8.8.8.8
+R:250 mailleur.example.com, link (cleartext) ready,...
+#====================================================
+S:EHLO .com
+R:501 5.5.4 HELO argument is incorrect, closing connection.
+#====================================================
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+S:EHLO domain..com
+R:501 5.5.4 HELO argument is incorrect, closing connection.
+#====================================================
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+S:EHLO [.192.219.254.1]
+R:501 5.5.4 HELO argument is incorrect, closing connection.
+#====================================================
diff --git a/data-feed/feed011.tst b/data-feed/feed011.tst
new file mode 100644 (file)
index 0000000..d99eea4
--- /dev/null
@@ -0,0 +1,8 @@
+T:(feed011) Checkin authentication mode is AUTH DISGEST-MDr5
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+S:AUTH DIGEST-MD5
+R:334 cmVhbG09Im1ha...
+S:dXNlcm5hbWU9ImRpZ2kiLHJlYWxtPSJtYWlsbGV1ci1lbWFpbCIsbm9uY2U9Ik1XdHZVSFpWYVc1d2QyUlhiRWRLU1hCRk1WUmxUR2xpY2s1aWEyMUIiLGNub25jZT0iNU1TQ2Z5YUN0WWxES2c2YkVhK0kiLG5jPTAwMDAwMDAxLHFvcD0iYXV0aCIsZGlnZXN0LXVyaT0ic210cC8xOTIuMjE5LjI1NC4xODEiLHJlc3BvbnNlPWQxYTFlOGFmMDUzZmJhNmQzY2NhMzg3NmQ2YWY1N2ExLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz
+R:504 5.7.4 wrong authentication
+R:Disconnected
diff --git a/data-feed/feed10.tst b/data-feed/feed10.tst
new file mode 100644 (file)
index 0000000..50c2f0b
--- /dev/null
@@ -0,0 +1,16 @@
+#very simple  test to feed SMTP server
+#====================================================
+T:(feed10) Testing if MX is found
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+S:HELO                example.com      
+R:250 mailleur.example.com, link (cleartext) ready,...
+S:MAIL FROM: <postmaster@example.com>
+R:250 2.1.3 postmaster@example.com sender ok
+S:RCPT TO: <user1@subdom0.example.com>
+R:563 5.6.4 No valid MX found for recipient domain name (domain=subdom0.example.com)
+S:RCPT TO: <user1@mailref1.example.com>
+R:250 5.6.7 Address will be processed <user1@mailref1.example.com>
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
+#-------------------------------------------------------------------------
diff --git a/data-feed/feed11.tst b/data-feed/feed11.tst
new file mode 100644 (file)
index 0000000..1f41d5a
--- /dev/null
@@ -0,0 +1,47 @@
+#====================================================
+T:(feed11) Sending an mail to an non exitsing user
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+S:EHLO example.com
+#R:250-mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.127.0.2/feed2.example.com]
+R:250-mailleur.example.com, link (cleartext) ready,...
+R:250-SIZE 52428800
+R:250-STARTTLS
+R:250-8BITMIME
+R:250-ENHANCEDSTATUSCODES
+R:250 AUTH PLAIN LOGIN
+C:GOTLS
+S:EHLO example.com
+R:250-mailleur.example.com, link (crypted) ready,...
+R:250-SIZE 52428800
+R:250-8BITMIME
+R:250-ENHANCEDSTATUSCODES
+R:250 AUTH PLAIN LOGIN
+S:MAIL FROM: <trouble@mailref1.example.com>
+R:250 2.1.3 trouble@mailref1.example.com sender ok
+#---------------------------------------------
+S:RCPT TO: <dom1user3@example.com>
+R:551 5.6.5 <dom1user3@example.com> unknown user
+S:RCPT TO: <dom1user3@mailref1.example.com>
+R:250 5.6.7 Address will be processed <dom1user3@mailref1.example.com>
+S:RCPT TO: <user1@posdb.example.com>
+R:250 5.6.7 Address will be processed <user1@posdb.example.com>
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: (feed11) A email to a not existing user
+D:From: Trouble maker <mailref1.example.com>
+D:To: Not existing <dom1user3@mailref1.example.com>
+C:T
+D:
+D:This mail on remote MX should be sent back to the originator
+D:while the other user@posdb.example.com receipient should got email
+D:
+D:
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feed12.tst b/data-feed/feed12.tst
new file mode 100644 (file)
index 0000000..2e3353f
--- /dev/null
@@ -0,0 +1,18 @@
+#====================================================
+T:(feed12) Trying to send Email while the domain is without MX
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+S:HELO example.com
+R:250 mailleur.example.com, link (cleartext) ready,...
+S:MAIL FROM: <trouble@mailref1.example.com>
+R:250 2.1.3 trouble@mailref1.example.com sender ok
+S:RCPT TO: <user1@subdom0.example.com>
+R:563 5.6.4 No valid MX found for recipient domain name (domain=subdom0.example.com)
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 503 5.5.0 RCPT first. transaction protocol command out of sequence
+C:.
+#-------------------------------------------------------------------------
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feed13.tst b/data-feed/feed13.tst
new file mode 100644 (file)
index 0000000..2161179
--- /dev/null
@@ -0,0 +1,6 @@
+#====================================================
+T:(feed13) Trying to overcome the entry buffer
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+S:SSH-2.0-Go
+S:??^EL^S^TÌK>^\^U3^W^[õ¶T6è^RP??^A^Ycurve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1??^B[ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss,ssh-ed25519,ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss,ssh-ed25519???<9b>aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,arcfour256,arcfour128,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,arcfour256,arcfour128???<9b>aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,arcfour256,arcfour128,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@EL^S^TÌK>^\^U3"^W^[õ¶T6è^RP??^A^Ycurve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1??^B[ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss,ssh-ed25519,ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss,ssh-ed25519???<9b>aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,arcfour256,arcfour128,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,arcfour256,arcfour128???<9b>aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,arcfour256,arcfour128,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@
diff --git a/data-feed/feed20.tst b/data-feed/feed20.tst
new file mode 100644 (file)
index 0000000..30e31b6
--- /dev/null
@@ -0,0 +1,13 @@
+#very simple  test to feed SMTP server
+#====================================================
+T:(feed20) Testing double connectopm
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+S:HELO example.com
+R:250 mailleur.example.com, link (cleartext) ready,...
+C:ORGN: 185.242.226.23
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+C:WAIT 120     $wait "forever"
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
+#-------------------------------------------------------------------------
diff --git a/data-feed/feed21.tst b/data-feed/feed21.tst
new file mode 100644 (file)
index 0000000..05d2ab1
--- /dev/null
@@ -0,0 +1,6 @@
+#very simple  test to feed SMTP server
+#====================================================
+T:(feed20) Testing double connectopm
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+#-------------------------------------------------------------------------
diff --git a/data-feed/feed30.tst b/data-feed/feed30.tst
new file mode 100644 (file)
index 0000000..f3e8d32
--- /dev/null
@@ -0,0 +1,34 @@
+#very simple  test to feed SMTP server
+#====================================================
+T:(feed30) Testing a simple email
+C:CONNECT
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+S:HELO example.com
+R:250 mailleur.example.com, link (cleartext) ready,...
+S:MAIL FROM: <postmaster@example.com>
+R:250 2.1.3 postmaster@example.com sender ok
+S:RCPT TO: <root@example.com>
+R:250 5.6.7 Address will be processed <root@example.com>
+S:RCPT TO: <jmp@example.com>
+R:250 5.6.7 Address will be processed <jmp@example.com>
+#-------------------------------------------------------------------------
+#-sending data
+C:DATA 354 3.5.0 End data with <CR><LF>.<CR><LF>
+D:Subject: (feed030) Very Simple email contents
+C:T
+D:From: Maitre Post <postmaster@example.com>
+D:To: Big Master <root@example.com>
+D:
+D:this email is for local test purpose only
+D:and test log display
+D:
+C:.
+#-------------------------------------------------------------------------
+R:250-3.5.3 Session ID=<...
+R:250-3.5.3 data stream received:...
+R:250 3.5.3 Message accepted for delivery
+#-------------------------------------------------------------------------
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
+#-------------------------------------------------------------------------
diff --git a/data-feed/feedx10.tst b/data-feed/feedx10.tst
new file mode 100644 (file)
index 0000000..9d0928f
--- /dev/null
@@ -0,0 +1,5 @@
+T:(feedx10) Making sure we can reject a remote
+C:CONNECT
+#====================================================
+#take a "invalid" imp number as server remote IP
+R:521 7.0.0 Remote server credit too low
diff --git a/data-feed/feedxx.tst b/data-feed/feedxx.tst
new file mode 100644 (file)
index 0000000..845b533
--- /dev/null
@@ -0,0 +1,39 @@
+T:(feed05) sending a small email after beeing authenticated
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+#-sending authentication request via BASE64
+#https://www.base64encode.org/
+#AUTH LOGIN
+#-Usernanme    'webmaster@example.com' --> 'd2VibWFzdGVyQGV4YW1wbGUuY29t'
+#-password     'mailleur'              --> 'bWFpbGxldXI="
+#AUTH PLAIN
+#-full seq "authid\0webmaster@example.com\0mailleur'
+#              --> YXV0aGlkXDB3ZWJtYXN0ZXJAZXhhbXBsZS5jb21cMG1haWxsZXVy
+#          authid will be discarded by authentication procedure
+#authid can be missing
+S:AUTH PLAIN
+R:334 5.7.1 Please provide auth sequence
+S:YXV0aGlkXDB3ZWJtYXN0ZXJAZXhhbXBsZS5jb21cMG1haWxsZXVy
+R:235 5.7.5 Authentication successful
+S:AUTH PLAIN YXV0aGlkXDB3ZWJtYXN0ZXJAZXhhbXBsZS5jb21cMG1haWxsZXVy
+R:235 5.7.5 Authentication successful
+S:AUTH LOGIN
+#requesting Username:
+R:334 VXNlcm5hbWU6
+#sending 'webmaster@example.com' as B64
+S:d2VibWFzdGVyQGV4YW1wbGUuY29t
+#requesting Password:
+R:334 UGFzc3dvcmQ6
+#sending 'mailleur' as B64
+S:bWFpbGxldXI=
+R:235 5.7.5 Authentication successful
+S:AUTH PLAIN AHVzZXIxQG1haWxwb3N0Zy5leGFtcGxlLmNvbQBtYWlsbGV1cg==
+R:504 5.7.4 authentication failed
+#\0bigre_bigre\0nouser
+S:AUTH PLAIN XDBiaWdyZV9iaWdyZVwwbm91c2Vy
+R:504 5.7.4 authentication failed
+#\0postmaster@example.com\0nopasswd
+S:AUTH PLAIN XDBwb3N0bWFzdGVyQGV4YW1wbGUuY29tXDBub3Bhc3N3ZA==
+R:504 5.7.4 authentication failed
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/feedyy.tst b/data-feed/feedyy.tst
new file mode 100644 (file)
index 0000000..f37727a
--- /dev/null
@@ -0,0 +1,19 @@
+T:(feed05) sending a small email after beeing authenticated
+R:220 mailleur.example.com, ESMTP (cleartext) mailleur...
+#====================================================
+#-sending authentication request via BASE64
+#https://www.base64encode.org/
+#AUTH LOGIN
+#-Usernanme    'webmaster@example.com' --> 'd2VibWFzdGVyQGV4YW1wbGUuY29t'
+#-password     'mailleur'              --> 'bWFpbGxldXI="
+#AUTH PLAIN
+#-full seq "authid\0webmaster@example.com\0mailleur'
+#              --> YXV0aGlkXDB3ZWJtYXN0ZXJAZXhhbXBsZS5jb21cMG1haWxsZXVy
+#          authid will be discarded by authentication procedure
+#authid can be missing
+S:AUTH PLAIN YXV0aGlkXDB3ZWJtYXN0ZXJAZXhhbXBsZS5jb21cMG1haWxsZXVy
+R:235 5.7.5 Authentication successful
+S:MAIL FROM:<user1@postdb.example.com>
+R:250 2.1.3 webmaster@example.com sender ok
+S:QUIT
+R:221 2.0.0 Bye, closing connection...
diff --git a/data-feed/zone.example.com b/data-feed/zone.example.com
new file mode 100644 (file)
index 0000000..f062c1c
--- /dev/null
@@ -0,0 +1,105 @@
+$ORIGIN example.com.
+$TTL 5m      ; 5 min
+@              IN      SOA     dns1.example.com.   root.example.com. (
+               0000000000      ; Serial_Marker
+               600             ; Refresh       (10 min)
+               300             ; retry         (5 min)
+               1209600         ; expiry        (1W)
+               900             ; minimum       (15 min)
+               )
+
+               IN NS   dns1
+               IN NS   dns2
+
+;---------------------------------------------------------------------
+dns1           IN      A       127.0.0.1
+dns2           IN      A       127.0.0.2
+;---------------------------------------------------------------------
+;test MX using IP 127.0.0.255
+@              IN      A       127.0.0.127
+               IN      MX      10 mx1
+               IN      MX      20 mx2
+
+mx1            IN      A       127.0.0.128
+mx2            IN      A       127.0.0.129
+
+;---------------------------------------------------------------------
+$ORIGIN example.com.
+chkmx                  IN      A       127.0.0.255
+                       IN      TXT     (
+                               "v=info "
+                               "chkmx PASS with 127.0.0.255,"
+                               "chkmx FAIL with 127.0.0.1"
+                               )
+                       IN      TXT     (
+                               "v=spf1 "
+                               "mx:spf.example.com/25 "
+                               "-all"
+                               )
+
+chkip4                  IN     A       127.0.1.255
+                        IN     TXT     (
+                               "v=info "
+                               "chkip4 PASS with 127.0.1.255,"
+                               "chkip4 FAIL with 127.0.1.1"
+                               )
+                       IN      TXT     (
+
+                               "v=spf1 "
+                               "ip4:127.0.1.128/25 "
+                               "-all"
+                               )
+
+chkip6                 IN      AAAA fc00:0:0:0:ffff::7f00:002f
+                       IN      TXT     (
+                               "v=info "
+                               "chkip6 PASS with PREFIX::127.0.2.255, "
+                               "chkip6 FAIL with PREFIX::127.0.2.1"
+                               )
+                       IN      TXT     (
+                               "v=spf1 "
+                               "ip6:0:0:0:0:0:ffff:7f00:0280/121 "
+                               "-all"
+                               )
+
+chkaddr                        IN      A       127.0.3.255
+                       IN      TXT     (
+                               "v=info "
+                               "chkaddr PASS with 127.0.3.255,"
+                               "chkaddr FAIL with 127.0.3.1"
+                               "chkaddr PASS with 127.0.1.255,"
+                               "chkaddr FAIL with 127.0.1.1"
+                               "chkaddr PASS with PREFIX::127.0.2.255, "
+                               "chkaddr FAIL with PREFIX::127.0.2.1"
+                               )
+                       IN      TXT     (
+                               "v=spf1 "
+                               "a "
+                               "a:chkip4 "
+                               "a:chkip6/121 "
+                               "-all"
+                               )
+
+;check include
+chkinc                 IN      TXT     (
+                               "v=info "
+                               "chkinc PASS with 127.0.0.255,"
+                               "chkinc FAIL with 127.0.0.1"
+                               "chkinc PASS with 127.0.1.255,"
+                               "chkinc FAIL with 127.0.1.1,"
+                               "chkinc PASS with PREFIX::127.0.2.255, "
+                               "chkinc FAIL with PREFIX::127.0.2.1,"
+                               "chkinc PASS with PREFIX::127.0.3.255,"
+                               "chkinc FAIL with PREFIX::127.0.3.1,"
+                               )
+
+                       IN      TXT     (
+                               "v=spf1 "
+                               "include:chkimx"
+                               "include:chkip4"
+                               "include:chkip6"
+                               "include:chkaddr"
+                               "-all"
+                               )
+
+;---------------------------------------------------------------------
diff --git a/data-spf/spf.tst b/data-spf/spf.tst
new file mode 100644 (file)
index 0000000..c8f868e
--- /dev/null
@@ -0,0 +1,29 @@
+#========================================================
+#data to check if SPF are properly seen
+#NOTE: this test rely on the fact
+#"spf.example.com" is accessible via a DNS request.
+#========================================================
+#Checking MX
+#Acceptable IP number
+spf_pass       chkmx.spf.example.com           127.0.0.255
+#wrong MX ip number
+spf_fail       chkmx.spf.example.com           127.0.0.1
+#Checking IP4 
+spf_pass       chkip4.spf.example.com          127.0.1.255
+spf_fail       chkip4.spf.example.com          127.0.1.1
+#Checking IP6 
+spf_pass       chkip6.spf.example.com          0:0:0:0:0:FFFF:127.0.2.255
+spf_fail       chkip6.spf.example.com          0::ffff:127.0.2.1 
+##checking addr
+spf_fail       chkaddr.spf.example.com         127.0.1.1
+spf_pass       chkaddr.spf.example.com         127.0.1.255
+spf_fail       chkaddr.spf.example.com         127.0.3.1
+spf_pass       chkaddr.spf.example.com         127.0.3.255
+spf_fail       chkaddr.spf.example.com         0::ffff:127.0.1.1
+spf_pass       chkaddr.spf.example.com         0::ffff:127.0.1.255
+spf_fail       chkaddr.spf.example.com         0::ffff:127.0.3.1
+spf_pass       chkaddr.spf.example.com         0::ffff:127.0.3.255
+#--------------------------------------------------------
+#test with gmail.
+spf_pass       gmail.com                       74.125.224.54
+#--------------------------------------------------------
diff --git a/dbg/mailleur.conf b/dbg/mailleur.conf
new file mode 100644 (file)
index 0000000..106b010
--- /dev/null
@@ -0,0 +1,80 @@
+#file used to set environment configuration
+#------------------------------------------------
+#Defining application name
+APPNAME=mailleur
+#root working directoy definition for the whole application
+ROOTBASE="/"
+#defining local default domain
+DFLTDOMAIN="safe.ca"
+#defining the local REALM
+#Caution! the realm is hardcoded within data-base
+#and USED too to hash the user passwd
+#Changing this value coule be a trouble make 
+#(entering again ALL user password)
+REALM="mailleur-email"
+#------------------------------------------------
+#list of listening port to do SMTP protocole
+#format
+#protocol|ipnum|port|iteration[,protocol|ipnum|port|iteration]++
+#example
+#stmtps|ipnum|465|5    -> protocol smtps|IP number|port 465|5 iterations
+#|ipnum|||             -> smtp|ipnum|25|2
+#||,smtps||465|2       -> smtp|0.0.0.0|25|2 + smtps||465|2
+#||                    -> smtp|0.0.0.0|25|2
+#Examples
+#SMTPPORTS="|127.127.10.25|1025|,smtps|127.127.10.25|1026|1"
+#SMTPPORTS="|127.127.10.25|1025|"
+#SMTPPORTS="smtps|127.127.10.25|1065|1"
+#Defaults
+SMTPPORTS="|||5,smtps||465|3,smtp||587|2"
+#------------------------------------------------
+#SSL Security level default value
+#CIPHER_LIST="DEFAULT"
+#SSL_SECURITY=2
+#------------------------------------------------
+#Defining SERVER mode Certificate data
+CA_ROOT_SRV="/etc/pki/tls/make-ca/ca-bundle.crt"
+CA_VERIFY_SRV=0        #to check PEER/client remote certificate
+#If certificate set via do_dns_tlsa.sh (letsencrypt)
+#CA_CERT_SRV="/etc/certbot/mailleur/mailleur-fullchain.pem"
+#CA_KEY_SRV="/etc/certbot/mailleur/mailleur-key.pem"
+#If certificate are self signed (default installation)
+#CA_CERT_SRV="/etc/pki/mailleur/mailleur-cert.pem"
+#CA_KEY_SRV="/etc/pki/mailleur/mailleur-key.pem"
+#Symbolic link set at installation
+CA_CERT_SRV="/etc/mailleur/pki/mailleur-cert.pem"
+CA_KEY_SRV="/etc//mailleur/pki/mailleur-key.pem"
+#------------------------------------------------
+#Defining CLIENT mode Certificate data
+CA_ROOT_CLT="/etc/pki/tls/make-ca/ca-bundle.crt"
+CA_VERIFY_CLT=0        #to check PEER/server remote certificate
+#If certificate set via do_dns_tlsa.sh (letsencrypt)
+#CA_CERT_CLT="/etc/certbot/mailleur/mailleur-fullchain.pem"
+#CA_KEY_CLT="/etc/certbot/mailleur/mailleur-key.pem"
+#If certificate are self signed (default installation)
+#CA_CERT_CLT="/etc/pki/mailleur/mailleur-cert.pem"
+#CA_KEY_CLT="/etc/pki/mailleur/mailleur-key.pem"
+#Symbolic link set at installation
+CA_CERT_CLT="/etc/mailleur/pki/mailleur-cert.pem"
+CA_KEY_CLT="/etc/mailleur/pki/mailleur-key.pem"
+#------------------------------------------------
+#Configured for Postgresql database
+#DB_TYPE can be either POSTGRESQL,MYSQL, default POSTGRESQL
+DB_TYPE=POSTGRESQL
+DB_NAME=mailleur
+DB_HOST=localhost
+DB_PORT=5432
+DB_LANG="UTF-8"
+#------------------------------------------------
+#Dovecot storage directory
+DOV_MAILDIR="/var/spool/mailleur/mails"
+#------------------------------------------------
+#The list of public blacklist serveur
+BLACKLISTER=/etc/mailleur/blacklister.conf
+#the list of IP from which we accept to relay email
+RELAYABLE=/etc/mailleur/relayed.conf
+#------------------------------------------------
+#Minimal credit level to which remote is rejected at once
+#if credit is equal or below that level
+RJCTCRED=-50
+#------------------------------------------------
diff --git a/dovecot/conf.d/70-mailleur-mysql.conf b/dovecot/conf.d/70-mailleur-mysql.conf
new file mode 100644 (file)
index 0000000..7895daa
--- /dev/null
@@ -0,0 +1,11 @@
+# -------------------------------
+# 70-mailleur-mysql-conf
+# Configuration Dovecot to use Mariadb server
+# -------------------------------
+sql_driver = mysql
+
+mysql remote {
+        host = localhost.localdomain
+        user = mailleur
+        dbname = mailleur
+        }
diff --git a/dovecot/conf.d/70-mailleur-pgsql.conf b/dovecot/conf.d/70-mailleur-pgsql.conf
new file mode 100644 (file)
index 0000000..ca34767
--- /dev/null
@@ -0,0 +1,13 @@
+# -------------------------------
+# 70-pgsql.conf
+# Configuration Dovecot to use PostgreSQL server
+# -------------------------------
+sql_driver = pgsql
+
+pgsql remote {
+       parameters {
+               host = localhost.localdomain
+               user = mailleur
+               dbname = mailleur
+               }
+       }
diff --git a/dovecot/conf.d/80-mailleur-usesql.conf b/dovecot/conf.d/80-mailleur-usesql.conf
new file mode 100644 (file)
index 0000000..d83fd8c
--- /dev/null
@@ -0,0 +1,23 @@
+# -------------------------------
+# 80-usesql.conf
+# Configuration Dovecot to use SQL database
+# -------------------------------
+# --- Passdb : authentication via SQL ---
+passdb sql     {
+       default_password_scheme = SHA512-CRYPT
+       query = SELECT password FROM emails WHERE email='%{user}';
+       }
+
+# --- Userdb : user data(home, uid, gid) via SQL ---
+#
+#
+#
+
+userdb sql {
+       query = SELECT 'mailleur' AS uid,'mail' AS gid,                 \
+                '/var/spool/mailleur/mails/%{user | domain}/%{user | username}' \
+                AS home,'maildir:~/dovecot' AS mail                    \
+                FROM emails WHERE email='%{user}';
+       }
+
+
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644 (file)
index 0000000..9a857ab
--- /dev/null
@@ -0,0 +1,236 @@
+#--------------------------------------------------------------------
+#Executable generation area
+#--------------------------------------------------------------------
+debug  :  toremake
+          @ $(MAKE)                            \
+               $(PAR)                          \
+               OPTIME="-g -DMODEDEBUG"         \
+               objs
+          @ echo "library compiled in '$@' mode, now ready"
+
+prod   :  toremake
+          @ $(MAKE) $(PAR) OPTIME="-O3" objs
+          @ echo "library compiled in '$@' mode, now ready"
+
+clean  :
+          - rm -fr *.so.* *.o *.a *.tar.gz
+
+allclean:  clean
+          - rm -fr *.d
+
+#--------------------------------------------------------------------
+#Equivalences
+#--------------------------------------------------------------------
+OBJS=                                          \
+         modrec.o                              \
+         lvleml.o                              \
+         geseml.o gestcp.o gesspf.o gessql.o   \
+         devlog.o devsoc.o devsql.o            \
+         unidig.o unidns.o unieml.o unipar.o   \
+         uniprc.o unisig.o unisql.o unitls.o   \
+         subafn.o subcnv.o subrou.o 
+
+LIBS=                                          \
+         libmar.a libpos.a
+
+objs   : $(OBJS) $(LIBS)
+         @ ar -cr libmail.a $(OBJS)
+
+#--------------------------------------------------------------------
+#Dependances
+modrec.d                                       \
+modrec.o:                                      \
+          subrou.h                             \
+          uniprc.h                             \
+          unisig.h                             \
+          devsoc.h                             \
+          gestcp.h                             \
+          modrec.h modrec.c
+
+lvleml.d                                       \
+lvleml.o:                                      \
+          subcnv.h subrou.h                    \
+          unidig.h unidns.h unieml.h uniprc.o  \
+          gestcp.h gessql.h                    \
+          lvleml.h lvleml.c
+
+gesspf.d                                       \
+gesspf.o:                                      \
+          subrou.h                             \
+          gesspf.h gesspf.h
+
+geseml.d                                       \
+geseml.o:                                      \
+          subrou.h                             \
+          unidns.h unieml.h unipar.h unitls.h  \
+          devsql.h                             \
+          geseml.h geseml.h
+
+gestcp.d                                       \
+gestcp.o:                                      \
+          subrou.h                             \
+          unieml.h                             \
+          uniprc.h                             \
+          unisig.h                             \
+          gestcp.h gestcp.c
+
+gessql.d                                       \
+gessql.o:                                      \
+          unisql.h                             \
+          devsql.h                             \
+          gessql.h gessql.c
+
+devlog.o:                                      \
+          subrou.h                             \
+          uniprc.h                             \
+          devlog.h devlog.c
+
+devsoc.o:                                      \
+          subafn.h subrou.h                    \
+          unidns.h unieml.h uniprc.h           \
+          devsoc.h devsoc.c
+
+devsql.o:                                      \
+          unipos.h                             \
+          unimar.h                             \
+          devsql.h devsql.c
+
+unidig.o:                                      \
+          subrou.h subcnv.h                    \
+          unidig.h unidig.c
+
+unidns.o:                                      \
+          subafn.h subrou.h                    \
+          unidns.h unidns.c
+
+unieml.o:                                      \
+          subrou.h                             \
+          unieml.h unieml.c
+
+unimar.o:                                      \
+          subrou.h                             \
+          unimar.h unimar.c
+
+unipar.o:                                      \
+          subrou.h                             \
+          unipar.h unipar.c
+
+unipos.o:                                      \
+          subrou.h                             \
+          unipos.h unipos.c
+
+uniprc.o:                                      \
+          subrou.h                             \
+          uniprc.h uniprc.c
+
+unisig.o:                                      \
+          subrou.h                             \
+          unisig.h unisig.c
+
+unitls.o:                                      \
+          subrou.h                             \
+          unitls.h unitls.c
+
+subrou.o:                                      \
+          subrou.h subrou.c
+
+subafn.o:                                      \
+          subafn.h subafn.c
+
+subcnv.o:                                      \
+          subcnv.h subcnv.c
+
+lvleml.h:                                      \
+          devsoc.h devsql.h                    \
+          geseml.h gestcp.h
+
+geseml.h:                                      \
+          unisql.h
+
+gestcp.h:                                      \
+          subrou.h                             \
+          unidns.h                             \
+          devlog.h devsoc.h
+
+gessql.h:                                      \
+          unieml.h                             \
+          devsql.h
+
+gesspf.h:                                      \
+          subafn.h
+
+devsoc.h:                                      \
+          unitls.h
+
+devsql.h:                                      \
+          unisql.h
+
+uniprc.h:                                      \
+          subrou.h
+
+unieml.h                                       \
+unitls.h                                       \
+       :                                       \
+          subafn.h
+
+libpos.a:  unimar.o                            \
+          subrou.h                             \
+           unipos.h unipos.c
+          @ $(CC)                              \
+               $(CFLAGS)                       \
+               $(CPPFLAGS)                     \
+               -DDATABASE=1                    \
+               -Dwith_postgres                 \
+               -c                              \
+               -g                              \
+               -o libpos.o unipos.c
+         @ ar -cr libpos.a libpos.o unimar.o
+
+libmar.a:  unipos.o                            \
+          subrou.h                             \
+           unimar.h unimar.c
+          @ $(CC)                              \
+               $(CFLAGS)                       \
+               $(CPPFLAGS)                     \
+               -DDATABASE=2                    \
+               -Dwith_mysql                    \
+               -c                              \
+               -g                              \
+               -o libmar.o unimar.c
+         @ ar -cr libmar.a libmar.o unipos.o
+
+#--------------------------------------------------------------------
+toremake:  Makefile
+          touch toremake
+          - rm -f $(OBJS) $(LIBMAIL)
+
+#--------------------------------------------------------------------
+#to manage dependencies
+%.d:     %.c
+         @ set -e; rm -f $@;                                   \
+           $(CC) -M $(CPPFLAGS) $< > $@.$$$$;                  \
+           sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+           rm -f $@.$$$$
+
+sources        =                                                       \
+           modrec.c                                            \
+           lvleml.c                                            \
+           geseml.c gesspf.c gessql.c gestcp.c                 \
+           devlog.c devsoc.c devsql.c                          \
+           unidig.c unidns.c unieml.c unimar.c                 \
+           unipar.c unipos.c uniprc.c unisig.c                 \
+           unisql.c unitls.c                                   \
+           subafn.c subcnv.c subrou.c                          \
+
+include $(sources:.c=.d)
+#--------------------------------------------------------------------
+.PHONY:        clean allclean
+#--------------------------------------------------------------------
+CC     = gcc
+LD     = gcc
+CPPFLAGS= -DPUBLIC=''
+CFLAGS = -Wall -D_GNU_SOURCE                                   \
+         $(OPTIME)
+LIBMAIL        = libmail.a libmar.a libpos.a
+PAR    = -j`/usr/bin/getconf _NPROCESSORS_ONLN`
+#--------------------------------------------------------------------
diff --git a/lib/devlog.c b/lib/devlog.c
new file mode 100644 (file)
index 0000000..c58aa14
--- /dev/null
@@ -0,0 +1,377 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Log management implementation module            */
+/*                                                     */
+/********************************************************/
+#include        <linux/limits.h>
+#include        <sys/file.h>
+#include        <errno.h>
+#include        <malloc.h>
+#include        <stdbool.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <unistd.h>
+
+#include        "subrou.h"
+#include        "uniprc.h"
+#include        "devlog.h"
+
+#define JRLDIR  "/var/spool/"APPNAME
+
+typedef struct  {
+        char *filename;         //the log filename
+        FILE *file;             //The log file pointer
+        TIMESPEC start;         //log start time
+        }LOGTYP;
+
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to free reference to a log device     */
+/*                                                      */
+/********************************************************/
+static LOGTYP *freelog(LOGTYP *log)
+
+{
+if (log!=(LOGTYP *)0) {
+  log->filename=rou_freestr(log->filename);
+  (void) free(log);
+  log=(LOGTYP *)0;
+  }
+return log;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to merge current log within current   */
+/*      daily logs.                                     */
+/*                                                      */
+/********************************************************/
+static _Bool mergelog(const char *logname,long *debut,long *fin)
+
+{
+#define OPEP    "devlog.c:mergelog"
+#define UFTIME  "%Y%m%d"
+#define EVENT   "logevent"
+
+_Bool status;
+char *event;
+FILE *fevent;
+FILE *flog;
+time_t curtime;
+char asctemps[100];
+char daily[200];
+int phase;
+int proceed;
+
+status=true;
+fevent=(FILE *)0;
+flog=(FILE *)0;
+curtime=time((time_t)0);
+(void) strftime(asctemps,sizeof(asctemps),UFTIME,localtime(&curtime));
+event=(char *)0;
+(void) strcpy(daily,"");
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d' pid='%d'",OPEP,phase,getpid());
+  switch (phase) {
+    case 0      :       //do we have a log name?
+      if (logname==(char *)0) {
+        (void) rou_alert(0,"%s logname is missing (Bug!?)",OPEP);
+        proceed=false;  //trouble trouble
+        }
+      break;
+    case 1      :       //duplicat logname
+      (void) strncpy(daily,logname,sizeof(daily)-strlen(daily));
+      if (strlen(daily)>0) {
+        char *ptr;
+
+        if ((ptr=strrchr(daily,'/'))!=(char *)0)
+          *ptr='\000';
+        }
+      (void) rou_asprintf(&event,"%s/event-%s.jrl",daily,asctemps);
+      break;
+    case 2      :       //opening log file
+      if ((flog=fopen(logname,"r"))==(FILE *)0) {
+        (void) rou_alert(0,"%s Unable to open log file <%s> (error=<%s>)",
+                               OPEP,logname,strerror(errno));
+        phase=999;      //no need to go further
+        }
+      break;
+    case 3      :       //opening log file
+      if ((fevent=fopen(event,"a+"))==(FILE *)0) {
+        (void) rou_alert(0,"%s Unable to open event file <%s> (error=<%s>)",
+                               OPEP,event,strerror(errno));
+        (void) fclose(flog);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 4      :       //locking access to logname
+      if (flock(fileno(fevent),LOCK_EX)<0) {
+        (void) rou_alert(0,"%s Unable to lock acces to log file <%s> "
+                           "(error=<%s>) (Bug?)",
+                           OPEP,event,strerror(errno));
+        (void) fclose(fevent);
+        (void) fclose(flog);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 5      :       //Setting file position
+      (void) fseek(fevent,(long)0,SEEK_END);
+      *debut=ftell(fevent);     //First char position
+      break;
+    case 6      :  {    //merging file
+      char ch;
+
+      while ((ch=fgetc(flog))!=EOF) 
+        fputc(ch,fevent);
+      break;
+    case 7      :       //unlocking file
+      (void) flock(fileno(fevent),LOCK_UN);
+      *fin=ftell(fevent)-1;     //Last char Position
+      (void) fclose(fevent);
+      (void) fclose(flog);
+      }
+      break;
+    case 8      :       //removing log file
+      if (unlink(logname)<0) {
+        (void) rou_alert(0,"%s Unable to delete log file <%s> (error=<%s>)",
+                               OPEP,logname,strerror(errno));
+        }
+      break;
+    default     :       //SAFE guard
+      event=rou_freestr(event);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+#undef  UFTIME
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to open a session log to collect all  */
+/*      exchange with remote SMTP client.               */
+/*      return a NULL file pointeur if trouble.         */
+/*                                                      */
+/********************************************************/
+PUBLIC LOGPTR *log_openlog(char *logname,const char *cmt)
+
+{
+#define OPEP    "devlog.c:log_openlog"
+
+LOGTYP *log;
+char logdir[PATH_MAX];
+int phase;
+int proceed;
+
+log=(LOGTYP *)0;
+(void) snprintf(logdir,sizeof(logdir),"%s/%s-logs/",JRLDIR,execname);
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //do we have a log name?
+      if (logname==(char *)0) {
+        (void) rou_alert(0,"%s logname is missing (Bug!?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //generating log file name
+      char *loc;
+
+      log=(LOGTYP *)calloc(1,sizeof(LOGTYP));
+      loc=rou_apppath(logdir);
+      (void) rou_asprintf(&(log->filename),"%s%s-%05d.jrl",loc,logname,getpid());
+      if (rou_do_mkpdir(loc)==false) {
+        (void) rou_alert(0,"%s Unable to create log directory <%s> (system?)",
+                            OPEP,loc);
+        log=freelog(log);
+        phase=999;
+        }
+      loc=rou_freestr(loc);
+      break;
+    case 2      :       //opening the log file
+      if ((log->file=fopen(log->filename,"w"))==(FILE *)0) {
+        (void) rou_alert(0,"%s Unable to open file <%s> (error=<%s>)",
+                            OPEP,log->filename,strerror(errno));
+        log=freelog(log);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 3      :       //writing a minimal data
+      (void) clock_gettime(CLOCK_REALTIME,&(log->start));
+      (void) fprintf(log->file,"\n#-----------------------------\n");
+      (void) fprintf(log->file,"#%s",ctime(&(log->start.tv_sec)));
+      if (cmt!=(const char *)0) 
+        (void) fprintf(log->file,"#%s\n",cmt);
+      (void) fflush(log->file);
+      log->start.tv_sec=0;
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return (LOGPTR *)log;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to close a session log                */
+/*      return the close status.                        */
+/*                                                      */
+/********************************************************/
+PUBLIC LOGPTR *log_closelog(LOGPTR *logptr,long *debut,long *fin)
+
+{
+#define OPEP    "devlog.c:log_closelog"
+
+LOGTYP *log;
+int phase;
+int proceed;
+
+log=(LOGTYP *)logptr;
+*debut=0;
+*fin=0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //do we have a log reference?
+      if (log==(LOGTYP *)0) 
+        phase=999;
+      break;
+    case 1      :       //closing log file
+      if (fclose(log->file)!=0) {
+        (void) rou_alert(0,"%s Unable to close log file <%s> (error=<%s>)",
+                            OPEP,log->filename,strerror(errno));
+        phase=999;
+        }
+      break;
+    case 2      :       //merging current log
+      if (mergelog(log->filename,debut,fin)==false) 
+        phase=999;
+      break;
+    case 3      :       //removing logfile
+      break;
+    default     :       //SAFE guard
+      logptr=freelog(log);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return logptr;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to insert a formated string within    */
+/*      log file.                                       */
+/*                                                      */
+/********************************************************/
+PUBLIC int log_fprintlog(LOGPTR *logptr,_Bool showtime,const char *format,...)
+
+{
+#define OPEP    "devlog.c:log_fprintlog"
+int taille;
+LOGTYP *log;
+char chrono[30];
+char *line;
+va_list args;
+int phase;
+int proceed;
+
+taille=0;
+log=(LOGTYP *)logptr;
+(void) strcpy(chrono,"");
+line=(char *)0;
+va_start(args,format);
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //do we have a log name?
+      if (log==(LOGTYP *)0) {
+        (void) rou_alert(0,"%s log pointer is NULL (Bug!?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //set time marker within log
+      if (log->start.tv_sec==0) {
+        time_t timer;
+        struct tm* tm_info;
+        char buffer[10];
+
+        timer=time((time_t *)0);
+        tm_info=localtime(&timer);
+        (void) strftime(buffer,sizeof(buffer),"%T",tm_info);
+        (void) clock_gettime(CLOCK_REALTIME,&(log->start));
+        (void) snprintf(chrono,sizeof(chrono),"%s.%03ld",
+                        buffer,log->start.tv_nsec/1000000);
+        }
+      else {
+        (void) snprintf(chrono,sizeof(chrono),"%12s"," ");
+        if (showtime==true) {
+          register unsigned int delta;
+          u_int millisec;
+          u_int sec;
+          u_int min;
+
+          delta=rou_getdifftime(&(log->start));
+          millisec=delta%1000;
+          sec=(delta/1000)%60;
+          min=(delta/1000)/60;
+          if (min==0)
+            (void) snprintf(chrono,sizeof(chrono),"%6s%02d.%03d","+",
+                                                   sec,millisec);
+          else
+            (void) snprintf(chrono,sizeof(chrono),"%3s%02d:%02d.%03d","+",
+                                                   min,sec,millisec);
+          }
+        }
+      break;
+    case 2      :       //formating the line
+      if ((taille=rou_vasprintf(&line,format,args))<0) {
+        (void) rou_alert(0,"%s Unable to format <%s> (Bug!?)",OPEP,format);
+        phase=999;
+        }
+      break;
+    case 3      :       //string the formated line within logs
+      (void) fprintf(log->file,"%s %s\n",chrono,line);
+      (void) fflush(log->file);
+      break;
+    default     :       //SAFE guard
+      line=rou_freestr(line);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+va_end(args);
+return taille;
+#undef  OPEP
+}
diff --git a/lib/devlog.d b/lib/devlog.d
new file mode 100644 (file)
index 0000000..66ed141
--- /dev/null
@@ -0,0 +1,53 @@
+devlog.o devlog.d : devlog.c /usr/include/stdc-predef.h /usr/include/linux/limits.h \
+ /usr/include/sys/file.h /usr/include/features.h \
+ /usr/include/features-time64.h /usr/include/bits/wordsize.h \
+ /usr/include/bits/timesize.h /usr/include/sys/cdefs.h \
+ /usr/include/bits/long-double.h /usr/include/gnu/stubs.h \
+ /usr/include/gnu/stubs-64.h /usr/include/fcntl.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/fcntl.h \
+ /usr/include/bits/fcntl-linux.h \
+ /usr/include/bits/types/struct_timespec.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/stat.h /usr/include/bits/struct_stat.h \
+ /usr/include/errno.h /usr/include/bits/errno.h \
+ /usr/include/linux/errno.h /usr/include/asm/errno.h \
+ /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
+ /usr/include/malloc.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/stdlib.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h /usr/include/time.h \
+ /usr/include/bits/time.h /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h uniprc.h devlog.h
diff --git a/lib/devlog.h b/lib/devlog.h
new file mode 100644 (file)
index 0000000..0303e5b
--- /dev/null
@@ -0,0 +1,28 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/************************************************/
+/*                                             */
+/*     device level, logs management           */
+/*      declaration.                            */
+/*                                             */
+/************************************************/
+#ifndef        DEVLOG
+#define DEVLOG
+
+#include        <stdio.h>
+
+typedef void    LOGPTR;
+
+//procedure to open a session log
+extern LOGPTR *log_openlog(char *logname,const char *cmt);
+
+//procedure to close a previously open session log
+extern LOGPTR *log_closelog(LOGPTR *logptr,long *debut,long *fin);
+
+//procedure to write a sequence within the current log
+extern int log_fprintlog(LOGPTR *logptr,_Bool showtime,const char *format,...);
+
+//procedure to merge session log contents to 
+//current daily log
+extern int log_mergelog(char *logname);
+
+#endif
diff --git a/lib/devsoc.c b/lib/devsoc.c
new file mode 100644 (file)
index 0000000..d663602
--- /dev/null
@@ -0,0 +1,1669 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Low level subroutine declaration                */
+/*     to handle TCP socket.                           */
+/*                                                     */
+/********************************************************/
+#include        <arpa/inet.h>
+#include        <sys/types.h>
+#include        <errno.h>
+#include        <fcntl.h>
+#include        <netdb.h>
+#include        <poll.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <signal.h>
+#include        <string.h>
+#include        <unistd.h>
+
+#include       "subrou.h"
+#include       "subafn.h"
+#include       "unidns.h"
+#include       "unieml.h"
+#include       "uniprc.h"
+#include       "unitls.h"
+#include       "devsoc.h"
+
+#define MXCARIN 1200    //maximun number of char within carpile 
+                        //full line sould be more than 998 char
+
+typedef struct  {
+        PROTYP proto;   //Connexion protocol type
+        int handle;     //connexion handle
+        _Bool connected;//soc is connected to remote
+        _Bool modtls;   //soc is in TLS mode
+        TLSTYP *tls;    //full TPS/SSL channel
+        char *cipherid; //Information on TLS crypting
+        int maxcarin;   //absolute number within carin
+        char *EOL;      //End of line marker
+        int carin;      //number of char within incpt;
+        char *carpile;  //area to store incoming char
+        char *ip;       //Binding IP    //IPV4 or IPV6
+        char *port;     //Binding Port
+        char *hostname; //binding hostname
+        time_t lasttry; //successful binding last time
+        int iteration;  //number of soc slot used on the IP
+        }SOCTYP;
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to free memory used by a              */
+/*     binding info.                                   */
+/*                                                     */
+/*                                                      */
+/********************************************************/
+static SOCPTR *freesocket(SOCPTR *socptr)
+
+{
+if (socptr!=(SOCPTR *)0) {
+  register SOCTYP *soc;
+
+  soc=(SOCTYP *)socptr;
+  soc->cipherid=rou_freestr(soc->cipherid);
+  soc->hostname=rou_freestr(soc->hostname);
+  soc->ip=rou_freestr(soc->ip);
+  soc->port=rou_freestr(soc->port);
+  soc->carpile=rou_freestr(soc->carpile);
+  soc->EOL=rou_freestr(soc->EOL);
+  (void) free(soc);
+  socptr=(SOCPTR *)0; 
+  }
+return socptr;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to create a new socket                */
+/*                                                     */
+/*                                                      */
+/********************************************************/
+static SOCTYP *newsocket()
+
+{
+SOCTYP *soc;
+
+soc=(SOCTYP *)calloc(1,sizeof(SOCTYP));
+soc->handle=-1;
+soc->maxcarin=MXCARIN;
+soc->carin=0;
+soc->EOL=strdup(CRLF);
+soc->carpile=(char *)calloc(soc->maxcarin,sizeof(char));
+return soc;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to duplicate a socket                 */
+/*                                                      */
+/********************************************************/
+static SOCTYP *dupsocket(SOCTYP *soc)
+
+{
+SOCTYP *newsoc;
+
+newsoc=(SOCTYP *)newsocket();
+newsoc->proto=soc->proto;
+newsoc->handle=soc->handle;
+newsoc->connected=soc->connected;
+newsoc->ip=strdup(soc->ip);
+newsoc->port=strdup(soc->port);
+newsoc->iteration=soc->iteration;
+return newsoc;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to wait incoming connexion on a       */
+/*      plain (not crypted) socket                      */
+/*                                                      */
+/********************************************************/
+static int getnewhandle(SOCTYP *soc)
+
+{
+#define OPEP    "devsoc.c:getnewhandle"
+
+int newhandle;
+
+if ((newhandle=accept(soc->handle,(SOCKADDR *)0,(socklen_t *)0))<0) {
+  if (errno==EAGAIN)
+    errno=EWOULDBLOCK;
+  switch (errno) {
+    case EWOULDBLOCK    :
+      (void) rou_alert(0,"%s Socket on IP/PORT <%s/%s> is in no_block mode "
+                         "(Bug?!, errno=<%s>)",
+                         OPEP,soc->ip,soc->port,strerror(errno));
+      break;
+    case EINVAL         :
+      (void) rou_alert(0,"%s Socket on IP/PORT <%s/%s> not available "
+                         "(Bug?!, errno=<%s>)",
+                         OPEP,soc->ip,soc->port,strerror(errno));
+      break;
+    case EINTR          :
+      (void) rou_alert(5,"%s Socket on IP/PORT <%s/%s> got a signal "
+                         "(errno=<%s>)",
+                         OPEP,soc->ip,soc->port,strerror(errno));
+      break;
+    default             :
+      (void) rou_alert(0,"%s Unexpected error on IP/PORT <%s/%s> (errno=<%s>)",
+                         OPEP,soc->ip,soc->port,strerror(errno));
+      break;
+    }
+  }
+return newhandle;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check if a socket is connected     */
+/*      This procedure is called when pool exit with    */
+/*      en event.                                       */
+/*                                                      */
+/********************************************************/
+static _Bool isconnected(SOCTYP *soc)
+
+{
+_Bool connected;
+char buffer[10];
+
+connected=true;
+if (recv(soc->handle,buffer,sizeof(buffer),MSG_PEEK|MSG_DONTWAIT)==0)
+  connected=false;
+return connected;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to clear and return socket status     */
+/*      return the errno.                               */
+/*                                                      */
+/********************************************************/
+static int checksockstat(int handle)
+
+{
+int status;
+socklen_t len;
+
+status=0;
+len=sizeof(status);
+if (getsockopt(handle,SOL_SOCKET,SO_ERROR,&status,&len)<0) {
+  status=errno;
+  }
+return status;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to open plain incoming channel        */
+/*                                                      */
+/********************************************************/
+static _Bool openplain(SOCTYP *soc)
+
+{
+#define OPEP    "devsoc:plainsoc"
+
+_Bool good;
+int newhandle;
+int flags;
+int phase;
+_Bool proceed;
+
+good=false;
+newhandle=-1;
+flags=0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //First prepare a new socket
+      if ((newhandle=getnewhandle(soc))<0) 
+        phase=999;      //no newhandle troub trouble
+      break;
+    case 1      :       //getting newhandle flag
+      if ((flags=fcntl(newhandle,F_GETFL,0))<0) {
+        (void) rou_core_dump("%s, Unable to get socket descripteur on "
+                             "IP/PORT <%s/%s> (Bug? error=<%s>)",
+                              OPEP,soc->ip,soc->port,strerror(errno));
+        phase=999;      //never reached
+        }
+      break;
+    case 2      :       //setting newhandle working mode
+      if ((flags=fcntl(newhandle,F_SETFL,flags|O_NONBLOCK|O_ASYNC))<0) {
+        (void) rou_core_dump("%s, Unable to set socket descripteur on "
+                             "IP/PORT <%s/%s> (Bug? error=<%s>)",
+                              OPEP,soc->ip,soc->port,strerror(errno));
+        phase=999;      //never reached
+        }
+      break;
+    case 3      :       //Socket ready
+      soc->handle=newhandle;
+      soc->connected=true;
+      good=true;
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return good;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to close plain incoming channel       */
+/*                                                      */
+/********************************************************/
+static void closeplain(SOCTYP *soc)
+
+{
+#define OPEP    "devsoc.c:closeplain"
+
+int phase;
+_Bool proceed;
+
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //is the connect still active
+      if (soc->connected==false)        //no!, no need to shutdown
+        phase=999;                      //no need to go further
+      break;
+    case 1      :       //shutting down the link
+      if (shutdown(soc->handle,SHUT_RDWR)<0) {
+        switch (errno) {
+          case ENOTCONN :       //already disconnect by other side!
+            (void) rou_alert(0,"%s [%s:%s] Already disconnected (errno=<%s>)",
+                                OPEP,soc->ip,soc->port,strerror(errno));
+            break;
+          default       :
+            (void) rou_alert(0,"%s unable to shutdown [%s:%s] (errno=<%s>)",
+                                OPEP,soc->ip,soc->port,strerror(errno));
+            break;
+          }
+        }
+      break;
+    case 2      :       //closing connexion
+      if (close(soc->handle)<0) {
+        (void) rou_alert(0,"%s unable to close channel [%s:%s] properly "
+                           "(errno=<%s>)",
+                            OPEP,soc->ip,soc->port,strerror(errno));
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to extract next line from the socket  */
+/*      incoming pile.                                  */
+/*      Return the number of character, zero if the     */
+/*      line is empty, -1 if no char are available.     */
+/*                                                      */
+/********************************************************/
+static int doextract(SOCTYP *soc,char **lineptr)
+
+{
+#define OPEP    "devsoc.c:doextract,"
+
+int got;
+char *eol;
+int phase;
+_Bool proceed;
+
+*lineptr=(char *)0;
+got=-1;
+eol=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d' carin='%d'",OPEP,phase,soc->carin);
+  switch (phase) {
+    case 0      :       //Do we have dat in carpile
+      if (soc->carin==0)
+        phase=999;      //No char,no need to check for line
+      break;
+    case 1      :       //do we have a buffer full;
+      //(void) rou_alert(0,"%s capile=<%s>",OPEP,soc->carpile);
+      if (soc->carin>=(soc->maxcarin)-1) {
+        //Overload! trying to overcome by extending carpile an adding an EOL
+        soc->maxcarin=soc->carin+5;
+        soc->carpile=(char *)realloc(soc->carpile,(soc->maxcarin)*sizeof(char));
+        (void) strcat(soc->carpile,soc->EOL);
+        }
+      break;
+    case 2      :       //Do we have a CRLF
+      eol=strstr(soc->carpile,soc->EOL);
+      if (eol==(char *)0) 
+        phase=999;      //No End Of Line yet
+      break;
+    case 3      :       //duplicating carpile
+      *lineptr=calloc(soc->carin+1,sizeof(char));
+      *eol='\000';
+      (void) strcpy(*lineptr,soc->carpile);
+      got=strlen(*lineptr);
+      break;
+    case 4      :       //managing carpile
+      soc->carin-=(got+strlen(soc->EOL));
+      if (soc->carin<0)
+        soc->carin=0;
+      if (soc->carin>0) {
+        int delta;
+
+        delta=got+strlen(soc->EOL);
+        (void) memmove(soc->carpile,soc->carpile+delta,soc->carin+1);
+        }
+      //soc->carpile[soc->carin]='\000';
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return got;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to wait and get a new handle          */
+/*                                                      */
+/********************************************************/
+static SOCTYP *waitincoming(SOCTYP *soc)
+
+{
+#define OPEP    "devsoc.c:waitincoming"
+
+SOCTYP *newsoc;
+_Bool ready;
+int phase;
+_Bool proceed;
+
+newsoc=(SOCTYP *)0;
+ready=false;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //First prepare a new socket
+      newsoc=dupsocket(soc);
+      if (openplain(newsoc)==false) 
+                        //received a termination signal
+        phase=999;      //return a null socket
+      break;
+    case 1      :       //wait for incoming connexion
+      switch (newsoc->proto) {
+        case pro_smtp           :       //plain socket
+        case pro_starttls       :       //plain socket + STARTTLS
+                                        //nothing to do
+          break;
+        case pro_smtps          :       //set secure socket
+          newsoc->modtls=true;
+          newsoc->tls=tls_opentls(newsoc->handle,true);
+          if (newsoc->tls==(TLSTYP *)0) {
+            (void) rou_alert(0,"%s Unable to get a TLS channel",OPEP);
+            newsoc->modtls=false;
+            phase=999;      //trouble trouble
+            }
+          break;
+        default                 :
+          (void) rou_alert(0,"%s Protocol '%d' unset (Bug?)",
+                              OPEP,(int)(newsoc->proto));
+          break;
+        }
+      break;
+    case 2      :       //Everything is fine
+      (void) usleep(10000);     //make sure link is properly set
+      ready=true;
+      break;
+    default     :       //SAFE guard
+      if (ready==false)
+        newsoc=soc_release(newsoc); //trouble touble
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return newsoc;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to bind a socket to a specific        */
+/*     address and return the linked handle            */
+/*                                                      */
+/********************************************************/
+static int bindhandle(struct addrinfo *ai,SOCTYP *soc)
+
+{
+#define        OPEP    "devsoc.c:bindhandle,"
+#define        BFSZ    30      //working buffer size
+
+int handle;
+struct linger slg;
+int reuse;
+int phase;
+_Bool proceed;
+
+handle=-1;
+slg.l_onoff=true;
+slg.l_linger=2;                 //2 sec max timeout on shutdown
+reuse=1;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0     :       //lets get a socket
+      if ((handle=socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol))<0) {
+        (void) rou_alert(0,"%s Unable to open socket for <%s> (error='%s')",
+                          OPEP,ai->ai_canonname,strerror(errno));
+       phase=999;      //trouble trouble
+       }
+      break;
+    case 1     :       //setting socket option (address)
+      if (setsockopt(handle,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0) {
+        (void) rou_alert(0,"%s Unable to get socket option "
+                           "SO_REUSEADDR for <%s> (error='%s')",
+                          OPEP,ai->ai_canonname,strerror(errno));
+        phase=999;     //trouble cleanup phase
+       }
+      break;
+    case 2     :       //setting socket option (port)
+      if (setsockopt(handle,SOL_SOCKET,SO_REUSEPORT,&reuse,sizeof(reuse))<0) {
+        (void) rou_alert(0,"%s Unable to get socket option "
+                           "SO_REUSEPORT for <%s> (error='%s')",
+                          OPEP,ai->ai_canonname,strerror(errno));
+        phase=999;     //trouble cleanup phase
+        }
+      break;
+    case 3     :       //SO_LINGER allow  Connection reset by peer
+      if (setsockopt(handle,SOL_SOCKET,SO_LINGER,&slg,sizeof(slg))<0) {
+        (void) rou_alert(0,"%s Unable to set socket option "
+                           "SO_LINGER for <%s> (error='%s')",
+                         OPEP,strerror(errno));
+        phase=999;     //trouble cleanup phase
+       }
+      break;
+    case 4     :       //getting socket option
+      if (bind(handle,ai->ai_addr,ai->ai_addrlen)<0) {
+       (void) rou_alert(0,"%s Unable to bind on IP/port <%s/%s> (error='%s')",
+                          OPEP,ai->ai_canonname,soc->port,strerror(errno));
+        phase=999;     //trouble cleanup phase
+       }
+      break;
+    case 5     :       //everythin is fine no need to go further
+      proceed=false;
+      break;
+    default    :       //SAFE Guard (in case of trouble)
+      if (handle>=0)
+        close(handle);
+      handle=-1;        //trouble trouble
+      proceed=false;    
+      break;
+    }
+  phase++;
+  }
+return handle;
+#undef OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to parse an email protocol            */
+/*                                                     */
+/********************************************************/
+PUBLIC PROTYP soc_getprotocol(const char *strproto)
+
+{
+static struct  {
+                PROTYP proto;
+                const char *voca;
+                }prolist[]={
+                          {pro_smtp,""},
+                          {pro_smtp,"smtp"},
+                          {pro_smtps,"smtps"},
+                          {pro_unknwn,(char *)0}
+                          };
+PROTYP proto;
+
+
+proto=pro_unknwn;
+for (int i=0;prolist[i].voca!=(char *)0;i++) {
+  if (strcasecmp(strproto,prolist[i].voca)==0) {
+    proto=prolist[i].proto;
+    break;
+    }
+  }
+return proto;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to free memory used by a bind         */
+/*     definition.                                     */
+/*                                                     */
+/********************************************************/
+PUBLIC SOCPTR **soc_freebindinf(SOCPTR **socptr)
+
+{
+if (socptr!=(SOCPTR **)0) {
+  register SOCTYP **socs;
+  int i;
+
+  socs=(SOCTYP **)socptr;
+  for (i=0;socs[i]!=(SOCTYP *)0;i++) {
+    socs[i]=freesocket(socs[i]);
+    }
+  (void) free(socs);
+  socptr=(SOCPTR **)0;
+  }
+return socptr;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to set memory used by a bin           */
+/*     definition.                                     */
+/*                                                     */
+/********************************************************/
+PUBLIC SOCPTR **soc_mkbindinf(SOCPTR **socptr,PROTYP proto,
+                              const char *ip,const char *port,int iteration)
+
+{
+SOCTYP *soc;
+
+soc=newsocket();
+soc->proto=proto;
+soc->ip=strdup(ip);
+soc->port=strdup(port);
+soc->iteration=iteration;
+socptr=(SOCPTR **)rou_addlist((void **)socptr,(void *)soc);
+return socptr;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to open a channel with a remote smtp  */
+/*      server. Return a socptr if successful.          */
+/*                                                     */
+/********************************************************/
+PUBLIC SOCPTR *soc_openfeedsock(PROTYP proto,const char *srcip,const char *ip,const char *port)
+
+{
+#define OPEP    "devsoc.c:soc_openfeedsoc,"
+#define WAITCNT 10
+
+SOCTYP *soc;
+int swait;
+int status;
+int handle;
+int flags;
+fd_set rset;
+fd_set wset;
+struct timeval timeout;
+struct addrinfo hints;
+struct addrinfo *ai;
+int phase;
+_Bool proceed;
+
+soc=(SOCTYP *)0;
+swait=WAITCNT;
+status=0;
+handle=0;
+flags=0;
+FD_ZERO(&rset);
+FD_ZERO(&wset);
+timeout.tv_usec=0;;
+timeout.tv_sec=swait;
+(void) memset(&hints,'\000',sizeof(hints));
+hints.ai_family=PF_UNSPEC;
+hints.ai_flags=HINTFLG;
+hints.ai_socktype=SOCK_STREAM;
+//ai=(struct addrinfo *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Do we have parameters
+      if ((ip==(const char *)0)||(port==(const char *)0)) {
+        (void) rou_alert(0,"%s, ip (%s) or port (%s) missing (config?)",
+                            OPEP,ip,port);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 1      :       //is address a good one
+      if ((status=getaddrinfo(ip,port,&hints,&ai))!=0) {
+        (void) rou_alert(0,"%s, Unable to find a address about "
+                           "IP:port '%s:%s' (error='%s')",
+                            OPEP,ip,port,gai_strerror(status));
+        phase=999;      //no need to go further
+        }
+      break;
+    case 2      :       //lets create socket
+      if ((handle=socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol))<0) {
+        (void) rou_alert(0,"%s Unable to open socket for <%s> (error='%s')",
+                          OPEP,ai->ai_canonname,strerror(errno));
+        phase=999;      //no need to go further
+        }
+      break;
+    case 3      :       //Need to bind from a specific source address
+      if ((srcip!=(const char *)0)&&(strlen(srcip)>0)) {
+        struct sockaddr_in server_addr;
+
+        (void) memset(&server_addr,'\000',sizeof(server_addr));
+        server_addr.sin_family=AF_INET;
+        inet_pton(AF_INET,srcip, &(server_addr.sin_addr));
+        if (bind(handle,(struct sockaddr*)&server_addr,sizeof(server_addr))<0) {
+          (void) rou_alert(0,"%s Unable to bind on <%s> error=<%s>",
+                              OPEP,srcip,strerror(errno));
+          (void) close(handle);
+          phase=999;
+          }
+        }
+      break;
+    case 4      :       //getting the socket status
+      if ((flags=fcntl(handle,F_GETFL,0))<0) {
+        (void) rou_core_dump("%s, Unable to get socket descripteur on "
+                             "IP/PORT <%s/%s> (Bug? error=<%s>)",
+                              OPEP,soc->ip,soc->port,strerror(errno));
+        phase=999;      //never reached
+        }
+      break;
+    case 5      :       //setting newhandle working mode
+      if ((flags=fcntl(handle,F_SETFL,flags|O_NONBLOCK|O_ASYNC))<0) {
+        (void) rou_core_dump("%s, Unable to set socket descripteur on "
+                             "IP/PORT <%s/%s> (Bug? error=<%s>)",
+                              OPEP,soc->ip,soc->port,strerror(errno));
+        phase=999;      //never reached
+        }
+      break;
+    case 6      :       //connecting to remote
+      FD_SET(handle,&rset);     /*FD ready for select   */
+      wset=rset;
+      if (connect(handle,ai->ai_addr,ai->ai_addrlen)<0) {
+        switch (errno) {
+          case EINPROGRESS      :       //its acceptable
+            break;
+          default               :
+            (void) rou_alert(1,"%s unable to make connection with '%s.%s' "
+                               "(error=<%s>)",
+                              OPEP,ip,port,strerror(errno));
+            (void) close(handle);
+            phase=999;
+            break;
+          }
+        }
+      break;
+    case 7      :       //Wait link established
+      switch (select(handle+1,&rset,&wset,(fd_set *)0,&timeout)) {
+        case -1 :
+          (void) rou_alert(1,"%s Unable to establish connection with '%s.%s' "
+                               "(error=<%s>)",
+                              OPEP,ip,port,strerror(errno));
+          (void) close(handle);
+          phase=999;
+          break;
+        case  0 :
+          (void) rou_alert(1,"%s Unable to establish connection with <%s> "
+                             "within %d sec",OPEP,ip,swait);
+          (void) close(handle);
+          phase=999;
+          break;
+        default :       //Link established
+          break;
+        }
+      break;
+    case 8      :       //checking socket status
+      if ((status=checksockstat(handle))!=0) {
+        (void) rou_alert(0,"%s  Unable to establish socklink with <%s:%s> (error=<%s>)",
+                           OPEP,ip,port,strerror(status));
+        (void) close(handle);
+        phase=999;
+        }
+      break;
+    case 9      :       //socket is now ready
+      soc=newsocket();
+      soc->proto=proto;
+      soc->connected=true;
+      soc->handle=handle;
+      soc->ip=strdup(ip);
+      soc->port=strdup(port);
+      soc->iteration=1;
+      break;
+    default     :       //SAFE Guard
+      if (ai!=(struct addrinfo *)0)
+        (void) freeaddrinfo(ai);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return (SOCPTR *)soc;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to close an exchange socket sonnected */
+/*     server. Return a so remote smtp server.          */
+/*                                                     */
+/********************************************************/
+PUBLIC SOCPTR *soc_closefeedsock(SOCPTR *socptr)
+
+{
+#define OPEP    "devsoc.c:soc_closefeedsoc"
+
+SOCTYP *soc;
+int phase;
+_Bool proceed;
+
+soc=(SOCTYP *)socptr;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Do we have an opened socket
+      if (soc==(SOCTYP *)0) {
+        (void) rou_alert(0,"%s, Aborting, socket is null (bug?)",OPEP);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 1      :       //clsing cryptying if needed
+      if (soc->tls!=(TLSTYP *)0) 
+        soc->tls=tls_closetls(soc->tls);
+      break;
+    case 2      :       //is the sockect detected closed
+      if (soc->handle<0)
+        phase=999;      //Socket allready closed
+      break;
+    case 3      :       //let close the socket
+      if (close(soc->handle)<0) 
+        (void) rou_alert(0,"%s, error on closing socket '%s.%s' (error=<%s>)",
+                            OPEP,soc->ip,soc->port,strerror(errno));
+      break;
+    default     :       //SAFE Guard
+      socptr=freesocket(socptr);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return socptr;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return the number of iterations    */
+/*      within socket definition.                       */
+/*                                                     */
+/********************************************************/
+PUBLIC int  soc_getiterations(SOCPTR *socptr)
+
+{
+int iterations;
+SOCTYP *soc;
+
+soc=(SOCTYP *)socptr;
+iterations=0;
+if (soc!=(SOCTYP *)0)
+  iterations=soc->iteration;
+return iterations;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to bind to one socket and return the  */
+/*      socket handle                                   */
+/*                                                     */
+/********************************************************/
+PUBLIC _Bool  soc_openbinding(SOCPTR *socptr)
+
+{
+#define OPEP    "devsoc.c:soc_openbinding"
+
+_Bool done;
+SOCTYP *soc;
+uid_t gid;      //ccurrent group id
+uid_t uid;      //ccurrent user id
+struct addrinfo hints;
+struct addrinfo *tobind;
+int phase;
+_Bool proceed;
+
+done=false;
+soc=(SOCTYP *)socptr;
+(void) memset(&hints,'\000',sizeof(hints));
+hints.ai_family=PF_UNSPEC;
+hints.ai_flags=HINTFLG;
+hints.ai_socktype=SOCK_STREAM;
+tobind=(struct addrinfo *)0;
+gid=getegid();  //let be back to root if needed
+uid=geteuid();  //to open device on < 1024 port
+if (setegid(getgid())<0)
+  (void) rou_core_dump("%s Unable to set the Egid to '%d' (error=<%s>",
+                        OPEP,getgid(),strerror(errno));
+if (seteuid(getuid())<0)
+  (void) rou_core_dump("%s Unable to set the Euid to '%d' (error=<%s>",
+                        OPEP,getuid(),strerror(errno));
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //sanity check
+      if (soc==(SOCTYP *)0) {
+        (void) rou_alert(0,"%s, socket binding reference is NULL (Bug!?)",OPEP);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 1      :       //check if time to retry binding
+      if ((soc->lasttry+5)>time((time_t)0)) {
+        phase=999;      //no need to go further
+        }
+      break;
+    case 2      :       //to have the right address infp
+      int status;
+
+      soc->lasttry=time((time_t *)0);
+      if ((status=getaddrinfo(soc->ip,soc->port,&hints,&tobind))!=0) {
+        (void) rou_alert(0,"%s, Unable to find a address about "
+                           "IP:port '%s:%s' (error='%s')",
+                            OPEP,soc->ip,soc->port,gai_strerror(status));
+        phase=999;      //no need to go further
+        }
+      break;
+    case 3      :       //binding socket
+      if ((soc->handle=bindhandle(tobind,soc))<0)
+        phase=999;      //no need to go further
+      (void) freeaddrinfo(tobind);
+      break;
+    case 4      :       //listening on socket
+      (void) prc_settitle("%s:%s, monitoring %02d contacts on %s:%s",
+                           APPNAME,appname,soc->iteration,soc->ip,soc->port);
+      if (listen(soc->handle,soc->iteration+4)<0) {
+        (void) rou_alert(0,"%s, Unable to listen at address "
+                           "IP:port '%s:%s' (error='%s')",
+                            soc->ip,soc->port,strerror(errno));
+        (void) close(soc->handle);
+        soc->handle=-1;
+        phase=999;      //no need to go further
+        }
+      break;
+    case 5      :       //listening on socket
+      done=true;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+(void) setegid(gid);    //recover the standard application
+(void) seteuid(uid);    //uid:gid
+return done;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to unbind from one socket.            */
+/*                                                     */
+/********************************************************/
+PUBLIC void soc_closebinding(SOCPTR *socptr)
+
+{
+#define OPEP    "devsoc.c:soc_closebinding"
+
+SOCTYP *soc;
+int phase;
+_Bool proceed;
+
+soc=(SOCTYP *)socptr;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //sanity check
+      if (soc==(SOCTYP *)0) {
+        (void) rou_alert(0,"%s binding pointer is NULL (Bug!?)",OPEP);
+        phase=999;      //not going further
+        }
+      break;
+    case 1      :       //check if socket already close
+      if (soc->handle<0) {
+        phase=999;      //yes, no need to go further
+        }
+      break;
+    case 2      :       //closing the the socket
+      if (close(soc->handle)<0) 
+        (void) rou_alert(0,"%s Unable to close socket (Bug!?, error=<%s>)",
+                            OPEP,strerror(errno));
+      soc->handle=-1;
+      break;
+    default     :       //SAFE Guard
+      soc->lasttry=(time_t)0;
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to wait for incoming character on     */
+/*      socket, waiting up to attend second.            */
+/*                                                     */
+/********************************************************/
+PUBLIC int soc_waitforchar(SOCPTR *socptr,u_int millisec)
+
+{
+#define OPEP    "devsoc.c:soc_waitforchar"
+register int status;
+sigset_t origmask;
+SOCTYP *soc;
+
+status=-2;
+soc=(SOCTYP *)socptr;
+if ((soc!=(SOCTYP *)0)&&(isconnected(soc)==true)) {      
+  switch (soc->modtls) {
+    case true   :
+      status=tls_waitforchar(soc->tls,millisec);
+      break;
+    case false  : {
+      struct pollfd polling[1];
+
+      polling[0].events=POLLIN|POLLPRI|POLLHUP;
+      polling[0].revents=(short)0;
+      polling[0].fd=soc->handle;
+      (void) sigprocmask(SIG_SETMASK,(sigset_t *)0,&origmask);
+      status=poll(polling,1,millisec);
+      (void) sigprocmask(SIG_SETMASK,&origmask,(sigset_t *)0);
+      }
+      break;
+    }
+  switch (status) {
+    case -1     :       //polling error
+      (void) rou_alert(0,"%s Polling error (error=<%s>)",OPEP,strerror(errno));
+      break;
+    case  0     :       //polling timeout
+      (void) rou_alert(0,"%s Polling timeout (error=<%s>)",OPEP,strerror(errno));
+      break;
+    case 1      :       //polling early return (data or event)
+                        //checking if link still available
+      if (tls_write(soc->tls,(char *)0,0)<0) {
+        (void) rou_alert(0,"%s, TLS link disconnect detected",OPEP);
+        status=0;
+        }
+      break;
+    default     :
+      (void) rou_alert(0,"%s Polling default (error=<%s>)",OPEP,strerror(errno));
+      break;
+    }
+  }
+return status;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return the next available line     */
+/*      within the socket carpile.                      */
+/*      return the number of char within line           */
+/*      Return  0 if line is 'empty'                    */
+/*      Return -1 if no characater available            */
+/*                                                     */
+/********************************************************/
+PUBLIC int soc_getnextline(SOCPTR *socptr,char **lineptr)
+
+{
+#define OPEP    "devsoc.c:soc_getnextline"
+
+int got;
+register SOCTYP *soc;
+int phase;
+_Bool proceed;
+
+*lineptr=(char *)0;
+got=-1;
+soc=(SOCTYP *)socptr;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Do we have dat in carpile
+      if (soc==(SOCTYP *)0) {
+        (void) rou_alert(0,"%s, socket binding reference is NULL (Bug!?)",OPEP);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 1      :       //Do we have char available in carpile
+      got=doextract(soc,lineptr);
+      if (got>=0)       //we have line
+        phase=999;      //no need to go further
+      break;
+    case 2      :       //lets wait for char
+      if (soc_receive(socptr)>0)        //we have char
+        phase=0;        //Let try to read line
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return got;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to send data to a tcp socket.         */
+/*      return the number of character transmitted, is  */
+/*      unable to send char return -1;                  */
+/*                                                      */
+/********************************************************/
+PUBLIC  int soc_writebuffer(SOCPTR *socptr,char *buffer,int tosend)
+
+{
+#define OPEP    "devsoc.c:soc_writebuffer"
+
+int sent;
+SOCTYP *soc;
+
+sent=-1;
+soc=(SOCTYP *)socptr;
+if (soc!=(SOCTYP *)0) {
+  switch (soc->modtls) {
+    case true   :
+      sent=tls_write(soc->tls,buffer,tosend);
+      break;
+    case false  :
+      sent=send(soc->handle,buffer,tosend,0);
+      break;
+    }
+  if (sent<0) {
+    switch (errno) {
+      case EPIPE        :
+        (void) rou_alert(0,"%s broken pipe",OPEP);
+        break;
+      default           :
+        (void) rou_alert(0,"%s Unable to send data, error=%d <%s> (Bug)",
+                            OPEP,errno,strerror(errno));
+        break;
+      }
+    }
+  }
+return sent;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to read incoming character from the   */
+/*      socket.                                         */
+/*      Return -1 if trouble.                           */
+/*              the number of char read otherwise       */
+/*                                                     */
+/********************************************************/
+PUBLIC int soc_receive(SOCPTR *socptr)
+
+{
+#define OPEP    "devsoc.c:soc_receive"
+
+int got;
+SOCTYP *soc;
+int limit;
+char *buffer;
+int phase;
+_Bool proceed;
+
+got=0;
+soc=(SOCTYP *)socptr;
+limit=0;
+buffer=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d' carpile=<%s>",OPEP,phase,soc->carpile);
+  switch (phase) {
+    case 0      :       //is socket available
+      if (soc==(SOCTYP *)0) {
+        (void) rou_alert(0,"%s socket pointer is null (Bug?)",OPEP);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 1      :       //Is the socket conneted
+      if (soc->connected==false) {
+        (void) rou_alert(0,"%s socket Already disconnect",OPEP);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 2      :       //reading data
+      buffer=soc->carpile+soc->carin;
+      limit=(soc->maxcarin-soc->carin);
+      //(void) rou_alert(0,"%s JMPDBG limit='%d' maxcarin='%d' carin='%d'",
+      //                   OPEP,limit,soc->maxcarin,soc->carin);
+      (void) memset(buffer,'\000',limit);
+      limit--;
+      errno=0;
+      switch (soc->modtls) {
+        case true   :
+          got=tls_read(soc->tls,buffer,limit);
+          break;
+        case false  :
+          got=recv(soc->handle,buffer,limit,MSG_DONTWAIT);
+          break;
+        }
+      break;
+    case 3      :       //check about recieved data
+      switch (got) {
+        case -1     :             //do not block
+          if (errno==EWOULDBLOCK)
+            errno=EAGAIN;
+          switch (errno) {
+            case ITSOK          :       //SSL report (wrong) error?
+            case EAGAIN         :       //no char available yet
+              got=0;                    //lets report "no char"
+              break;
+            case ECONNRESET     :       //Connection reset by peer
+              (void) rou_alert(0,"%s connection reset by peer",OPEP);
+              break;
+            default             :
+              (void) rou_alert(0,"%s Unexpected error=%d <%s> (Bug)",
+                                  OPEP,errno,strerror(errno));
+              break;
+            }
+          break;
+        case  0     :           //No char available?
+                                //wait for timeout detection
+          break;
+        default     :           //we got some char from remote
+          for (int i=0;i<got;i++,buffer++) {
+            if (*buffer=='\000')
+              *buffer='?';
+            }
+          soc->carin+=got;      //managing carpile
+          soc->carpile[soc->carin]='\000';
+          break;
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return got;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to wait an accept a connexion comming */
+/*      from a remote client.                           */
+/*      return a new socket.                            */
+/*                                                     */
+/********************************************************/
+PUBLIC SOCPTR *soc_accept(SOCPTR *socptr,int pos)
+
+{
+#define OPEP    "devsoc.c:soc_accept"
+
+SOCTYP *newsoc;
+SOCTYP *soc;
+int phase;
+_Bool proceed;
+
+newsoc=(SOCTYP *)0;
+soc=(SOCTYP *)socptr;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  (void) rou_dbglive(9,OPEP,"phase='%d'",phase);
+  switch (phase) {
+    case 0      :       //checking if soc is available
+      if (soc==(SOCTYP *)0) {
+        (void) rou_core_dump("%s, socket descripteur missing (Bug?)",OPEP);
+        phase=999;      //never reached
+        }
+      break;
+    case 1      :       //display ready on process status;
+      (void) prc_settitle("%s:%s, waiting (%02d/%02d) on [%s:%s]",
+                           APPNAME,appname,pos,soc->iteration,soc->ip,soc->port);
+      break;
+    case 2      :       //waiting for new connection
+      if ((newsoc=waitincoming(soc))==(SOCPTR *)0) {
+        (void) rou_alert(4,"%s, Contact from Remote not successful",OPEP);
+        phase=999;      //no need to go further
+        }
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return (SOCPTR *)newsoc;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return the address, name or port   */
+/*      for local or remote socket.                     */
+/*                                                     */
+/********************************************************/
+PUBLIC char *soc_getaddrinfo(SOCPTR *socptr,_Bool local,_Bool getname)
+
+{
+#define OPEP    "devsoc.c:soc_getaddrinfo"
+
+char *data;
+SOCTYP *soc;
+int mode;
+
+data=(char *)0;
+soc=(SOCTYP *)socptr;
+mode=NI_NUMERICSERV;
+switch (getname) {
+  case true     :
+    mode|=NI_NAMEREQD;
+    break;
+  case false    :
+    mode|=NI_NUMERICHOST;
+    break;
+  }
+if (soc!=(SOCTYP *)0) {
+  int status;
+  struct sockaddr connip;
+  socklen_t taille;
+  char host[NI_MAXHOST];
+  char serv[NI_MAXSERV];
+
+  (void) strcpy(host,"Unknown IP");
+  (void) strcpy(serv,"Unknown service");
+  taille=(socklen_t)(sizeof(connip));
+  if (local==true) 
+    status=getsockname(soc->handle,&connip,&taille);
+  else
+    status=getpeername(soc->handle,&connip,&taille);
+  switch (status) {
+    case -1     :               //trouble to read socket data
+      switch(errno) {
+        case ENOTCONN   :       //other side just vanished
+          (void) rou_alert(0,"%s, Remote side vanished (local=%d, error=<%s>)",
+                              OPEP,local,strerror(errno));
+          break;
+        default         :
+          (void) rou_alert(0,"%s, Unable to socket data (local=%d, error=<%s>)",
+                              OPEP,local,strerror(errno));
+          break;
+        }
+      break;
+    case  0     :               //NO trouble to read socket data
+      status=getnameinfo(&connip,taille,host,sizeof(host),serv,sizeof(serv),mode);
+      switch (status) {
+        case 0                  :       //everything fine
+          break;
+        case EAI_AGAIN          :
+        case EAI_NONAME         :
+          if (getname==true)
+            (void) strcpy(host,NORVERS);        //NO reverse name
+        default                 :
+          (void) rou_alert(0,"%s, Unable to get name (local=%d, error=<%s>)",
+                              OPEP,local,strerror(errno));
+          break;
+        }
+      break;    
+    default     :               //Unexpected touble to read socket
+      (void) rou_alert(0,"%s, status=%d Unexpected (local=%d, error=<%s>) (Bug?)",
+                          OPEP,status,local,strerror(errno));
+      break;    
+    }
+  switch (local) {
+    case true   :
+      switch (getname) {
+        case true       :
+          data=strdup(host);
+          break;
+        case false      :
+          char local[NI_MAXHOST+NI_MAXSERV+1];
+
+          (void) snprintf(local,sizeof(local),"%s|%s",host,serv);
+          data=strdup(local);
+          break;
+        }
+      break;
+    case false  :
+      data=strdup(host);
+      break;
+    }
+  }
+return data;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to close and release exchange socket  */
+/*                                                     */
+/********************************************************/
+PUBLIC SOCPTR *soc_release(SOCPTR *socptr)
+
+{
+#define OPEP    "devsoc.c:soc_release"
+
+SOCTYP *soc;
+int phase;
+_Bool proceed;
+
+soc=(SOCTYP *)socptr;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //checking if soc is available
+      if (soc==(SOCTYP *)0) {
+        (void) rou_core_dump("%s, socket descripteur missing (Bug?)",OPEP);
+        phase=999;      //never reached
+        }
+      break;
+    case 1      :       //shutting down the TCP link
+      switch (soc->proto) {
+        case pro_smtp           :       //plain socket
+          (void) closeplain(soc);
+          break;
+        case pro_starttls       :       //plain socket + STARTTLS
+          if (soc->modtls==true) {
+            soc->tls=tls_closetls(soc->tls);
+            soc->modtls=false;
+            }
+          break;
+        case pro_smtps          :       //set secure socket
+          if (soc->modtls==true) {
+            soc->tls=tls_closetls(soc->tls);
+            soc->modtls=false;
+            }
+          break;
+        default                 :
+          (void) rou_alert(0,"%s Protocol '%d' unset (Bug?)",
+                              OPEP,(int)(soc->proto));
+          break;
+        }
+      break;
+    case 2      :       //closing connexion
+      break;
+    case 3      :       //fee memory used by socket
+      soc=freesocket(soc);
+      socptr=(SOCPTR *)soc;
+      break;
+    default     :       //SAFE guard    
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return socptr;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to purge incoming TLS channel         */
+/*      After crypted link is established but before    */
+/*      to use it.                                      */
+/*                                                     */
+/*      See VE-2011-0411, "plaintext command injection" */
+/*                                                      */
+/********************************************************/
+PUBLIC void soc_purge(SOCPTR *socptr,const char *peerip)
+
+{
+#define OPEP    "devsoc.c:soc_purge,"
+
+int max;
+int count;
+
+max=1000;       //purging for 1 seconde max;
+count=0;
+if (socptr!=(SOCPTR *)0) {
+  SOCTYP *soc;
+
+  soc=(SOCTYP *)socptr;
+  for (;count<max;count++) {
+    soc->carin=0;
+    soc->carpile[0]='\000';
+    (void) usleep(1000);  //1 millisec
+    (void) soc_receive(soc);
+    if (soc->carin==0)    //got no character
+      break;
+    }
+  }
+if (count>=max) //one second max!
+  (void) rou_alert(0,"%s purge is too long with peer [%s]",OPEP,peerip);
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to switch a plain socket channel to   */
+/*      crypted channel, return true is successful.     */
+/*                                                     */
+/********************************************************/
+PUBLIC _Bool soc_starttls(SOCPTR *socptr,_Bool server)
+
+{
+#define OPEP    "devsoc.c:soc_starttls,"
+
+_Bool ok;
+SOCTYP *soc;
+
+ok=false;
+soc=(SOCTYP *)socptr;
+if ((soc!=(SOCTYP *)0)&&(soc->modtls==false)) {
+  switch (server)       {
+    case true   :       //Nothing to do?
+      break;
+    case false  :       //Nothing to do?
+      break;
+    }
+  soc->tls=tls_opentls(soc->handle,server);
+  if (soc->tls!=(TLSTYP *)0) {
+    soc->proto=pro_smtps;
+    soc->modtls=true;
+    ok=true;
+    (void) tls_verify(soc->tls);
+    soc->cipherid=tls_getcipherid(soc->tls);
+    }
+  }
+return ok;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return the security level used by  */
+/*      a crypted channel.                              */
+/*                                                     */
+/********************************************************/
+PUBLIC int soc_get_sec_level(SOCPTR *socptr)
+
+{
+int level;
+
+level=-1;
+if (socptr!=(SOCPTR *)0) {
+  register SOCTYP *soc;
+
+  soc=(SOCTYP *)socptr;
+  level=tls_get_sec_level(soc->tls);
+  }
+return level;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return true if sthe socket is      */
+/*      in crypted mode.                                */
+/*                                                     */
+/********************************************************/
+PUBLIC _Bool soc_iscrypted(SOCPTR *socptr)
+
+{
+_Bool iscrypted;
+
+iscrypted=false;
+if (socptr!=(SOCPTR *)0) 
+  iscrypted=((SOCTYP *)socptr)->modtls;
+return iscrypted;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return all cipher information      */
+/*                                                     */
+/********************************************************/
+PUBLIC char *soc_getcipherid(SOCPTR *socptr)
+
+{
+char *cipherid;
+SOCTYP *soc;
+
+cipherid=(char *)0;
+soc=(SOCTYP *)socptr;
+if (soc!=(SOCTYP *)0)
+  cipherid=tls_getcipherid(soc->tls);
+return cipherid;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to get the cipher name used in cypted */
+/*      mode.                                           */
+/*                                                     */
+/********************************************************/
+PUBLIC const char *soc_get_cipher_name(SOCPTR *socptr)
+
+{
+const char *cipher;
+SOCTYP *soc;
+
+cipher="Unknown";
+soc=(SOCTYP *)socptr;
+if (soc!=(SOCTYP *)0) {
+  const char *ptr;
+
+  if ((ptr=SSL_get_cipher_name(soc->tls->ssl))!=(const char *)0)
+    cipher=ptr;
+  }
+return cipher;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return the socket mode as a string */
+/*                                                     */
+/********************************************************/
+PUBLIC const char *soc_getstrmode(SOCPTR *socptr)
+
+{
+const char *mode;
+SOCTYP *soc;
+
+mode="Unknown";
+soc=(SOCTYP *)socptr;
+if (soc!=(SOCTYP *)0) {
+  mode="cleartext";
+  if (soc->modtls==true)
+    mode="crypted";
+  }
+return mode;
+}
diff --git a/lib/devsoc.d b/lib/devsoc.d
new file mode 100644 (file)
index 0000000..bd80a98
--- /dev/null
@@ -0,0 +1,120 @@
+devsoc.o devsoc.d : devsoc.c /usr/include/stdc-predef.h /usr/include/arpa/inet.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/netinet/in.h /usr/include/bits/stdint-uintn.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/socket.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/endian.h /usr/include/bits/endianness.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/socket_type.h \
+ /usr/include/bits/sockaddr.h /usr/include/asm/socket.h \
+ /usr/include/asm-generic/socket.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/errno.h /usr/include/bits/errno.h \
+ /usr/include/linux/errno.h /usr/include/asm/errno.h \
+ /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
+ /usr/include/fcntl.h /usr/include/bits/fcntl.h \
+ /usr/include/bits/fcntl-linux.h /usr/include/bits/stat.h \
+ /usr/include/bits/struct_stat.h /usr/include/netdb.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h /usr/include/poll.h \
+ /usr/include/sys/poll.h /usr/include/bits/poll.h /usr/include/stdio.h \
+ /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/stdlib.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/alloca.h /usr/include/bits/stdlib-float.h \
+ /usr/include/signal.h /usr/include/bits/signum-generic.h \
+ /usr/include/bits/signum-arch.h /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/types/stack_t.h \
+ /usr/include/sys/ucontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigstksz.h /usr/include/bits/ss_flags.h \
+ /usr/include/bits/types/struct_sigstack.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h subafn.h unidns.h unieml.h \
+ uniprc.h unitls.h /usr/include/openssl/ssl.h \
+ /usr/include/openssl/macros.h /usr/include/openssl/opensslconf.h \
+ /usr/include/openssl/configuration.h /usr/include/openssl/opensslv.h \
+ /usr/include/openssl/e_os2.h /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-least.h /usr/include/openssl/e_ostime.h \
+ /usr/include/sys/time.h /usr/include/openssl/comp.h \
+ /usr/include/openssl/crypto.h /usr/include/openssl/safestack.h \
+ /usr/include/openssl/stack.h /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h /usr/include/openssl/core.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/comperr.h /usr/include/openssl/bio.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/x509.h \
+ /usr/include/openssl/buffer.h /usr/include/openssl/buffererr.h \
+ /usr/include/openssl/evp.h /usr/include/openssl/core_dispatch.h \
+ /usr/include/openssl/indicator.h /usr/include/openssl/params.h \
+ /usr/include/openssl/bn.h /usr/include/openssl/bnerr.h \
+ /usr/include/openssl/evperr.h /usr/include/openssl/objects.h \
+ /usr/include/openssl/obj_mac.h /usr/include/openssl/asn1.h \
+ /usr/include/openssl/asn1err.h /usr/include/openssl/objectserr.h \
+ /usr/include/openssl/ec.h /usr/include/openssl/ecerr.h \
+ /usr/include/openssl/rsa.h /usr/include/openssl/rsaerr.h \
+ /usr/include/openssl/dsa.h /usr/include/openssl/dh.h \
+ /usr/include/openssl/dherr.h /usr/include/openssl/dsaerr.h \
+ /usr/include/openssl/sha.h /usr/include/openssl/x509err.h \
+ /usr/include/openssl/x509_vfy.h /usr/include/openssl/lhash.h \
+ /usr/include/openssl/pkcs7.h /usr/include/openssl/pkcs7err.h \
+ /usr/include/openssl/http.h /usr/include/openssl/conf.h \
+ /usr/include/openssl/conferr.h /usr/include/openssl/conftypes.h \
+ /usr/include/openssl/pem.h /usr/include/openssl/pemerr.h \
+ /usr/include/openssl/hmac.h /usr/include/openssl/async.h \
+ /usr/include/openssl/asyncerr.h /usr/include/openssl/ct.h \
+ /usr/include/openssl/cterr.h /usr/include/openssl/sslerr.h \
+ /usr/include/openssl/sslerr_legacy.h /usr/include/openssl/prov_ssl.h \
+ /usr/include/openssl/ssl2.h /usr/include/openssl/ssl3.h \
+ /usr/include/openssl/tls1.h /usr/include/openssl/dtls1.h \
+ /usr/include/openssl/srtp.h /usr/include/openssl/quic.h devsoc.h
diff --git a/lib/devsoc.h b/lib/devsoc.h
new file mode 100644 (file)
index 0000000..6f5e587
--- /dev/null
@@ -0,0 +1,93 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Low level subroutine declaration                */
+/*     to handle tcp Socket.                           */
+/*                                                     */
+/********************************************************/
+#ifndef        DEVSOC
+#define DEVSOC
+
+#include        <sys/socket.h>
+#include        <stdbool.h>
+#include        <time.h>
+
+#include        "unitls.h"
+
+//convenient adress structure
+typedef struct sockaddr SOCKADDR;
+
+//reference to a socket definition
+typedef void SOCPTR;
+
+//procedure to parse an email protocol 
+extern PROTYP soc_getprotocol(const char *strproto);
+
+//procedure to free all memory used by a TCP socket
+//definition (once closed) 
+extern SOCPTR **soc_freebindinf(SOCPTR **socptr);
+
+//procedure to assign memory to be used by a TCP socket
+//definition
+extern SOCPTR **soc_mkbindinf(SOCPTR **socptr,PROTYP proto,
+                              const char *ip,const char *port,int iteration);
+
+//procedure to open one exchange socket
+//to connect a remote smtp server
+extern SOCPTR *soc_openfeedsock(PROTYP proto,const char *src,const char *ip,const char *port);
+
+//procedure to close an exchange socket connected to a remote smtp server
+extern SOCPTR *soc_closefeedsock(SOCPTR *socptr);
+
+//procedure to return the number of channel to open on the soc
+extern int soc_getiterations(SOCPTR *socptr);
+
+//procedure to open ONE socket and return handle to socket
+extern _Bool soc_openbinding(SOCPTR *socptr);
+
+//procedure to close ONE socket
+extern void soc_closebinding(SOCPTR *socptr);
+
+//procedure to wait for character from contact
+extern int soc_waitforchar(SOCPTR *socptr,u_int millisec);
+
+//procedure to return a char array with the available line
+extern int soc_getnextline(SOCPTR *socptr,char **lineptr);
+
+//procedure to send a buffer contens on channel
+extern int soc_writebuffer(SOCPTR *socptr,char *buffer,int tosend);
+
+//procedure to receive up to limit char from socket
+extern int soc_receive(SOCPTR *socptr);
+
+//procedure to wait and accept remote connexion
+extern SOCPTR *soc_accept(SOCPTR *socptr,int pos);
+
+//procedure to get information about addrbane and port
+extern char *soc_getaddrinfo(SOCPTR *socptr,_Bool local,_Bool getname);
+
+//procedure to release/clsoe socket
+extern SOCPTR *soc_release(SOCPTR *socptr);
+
+//procedure to purge contact chenel (used in case of starttls)
+extern void soc_purge(SOCPTR *socptr,const char *peerip);
+
+//procedure to initiate crypted mode on plain channel
+extern _Bool soc_starttls(SOCPTR *socptr,_Bool server);
+
+//procedure to return the security level within a TLS link
+extern int soc_get_sec_level(SOCPTR *socptr);
+
+//return flag true if socket is in crypted mode
+extern _Bool soc_iscrypted(SOCPTR *socptr);
+
+//return the cipher mame used on the crypted link
+extern const char *soc_get_cipher_name(SOCPTR *socptr);
+
+//return all cipher information
+extern char *soc_getcipherid(SOCPTR *socptr);
+
+//return line socket mode (cleartext, crypted)
+extern const char *soc_getstrmode(SOCPTR *socptr);
+
+#endif
diff --git a/lib/devsql.c b/lib/devsql.c
new file mode 100644 (file)
index 0000000..b1d823c
--- /dev/null
@@ -0,0 +1,724 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Low level subroutine implementation             */
+/*     to handle SQL request                           */
+/*                                                     */
+/********************************************************/
+#include        <ctype.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+
+#include       "subrou.h"
+#include       "unimar.h"
+#include       "unipos.h"
+#include       "devsql.h"
+
+typedef void    SQLRES;    //Result from database
+
+typedef enum {
+        db_postgres,      //Postgres database
+        db_maria,         //Mariadb SQL
+        db_unknown
+        }SQLDBTYP;
+
+typedef struct  {
+        SQLDBTYP sqldb;         //database type
+        union   {
+          POSPTR *psql;         //postgreSQL reference/
+          MARPTR *msql;         //Mariadb|MySQL Reference
+          }db;
+        }SQLTYP;
+
+
+//SQL database request
+#define EMAILS          "emails"        //emails tables
+#define SESSIONS        "sessions"      //session tables
+
+#ifdef GETFIELD
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to get the ASCII form of field located*/
+/*      in a specific tuple + position in sqlresult.    */
+/*                                                      */
+/********************************************************/
+static char *getfield(SQLTYP *sql,SQLRES *rs,int tuple,int position)
+
+{
+#define OPEP    "devsql.c:getfield,"
+
+char *value;
+
+value=(char *)0;
+switch(sql->sqldb) {
+  case db_postgres    :
+    value=pos_getfield((POSRES *)rs,tuple,position);
+    break;
+  case db_maria       :
+    value=mar_getfield((MARRES *)rs,tuple,position);
+    break;
+  default             :
+    (void) rou_alert(0,"%s Unexpected type='%d' (BUG!?)",
+                        OPEP,(int)sql->sqldb);
+    break;
+  }
+return value;
+#undef  OPEP
+}
+#endif
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to create an SQL pointer for proper   */
+/*      database type.                                  */
+/*                                                      */
+/********************************************************/
+static SQLTYP *gettyp(const char *dbtype)
+
+{
+const char *db[]={"POSTGRESQL","MYSQL",(const char *)0};
+
+SQLTYP *sql;
+
+sql=(SQLTYP *)0;
+for (int i=0;db[i]!=(const char *)0;i++) {
+  if (strcmp(db[i],dbtype)==0) {
+    sql=(SQLTYP *)calloc(1,sizeof(SQLTYP));
+    sql->sqldb=(SQLDBTYP)i;
+    break;      //data base type found
+    }
+  }
+return sql;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to make sure a string to be use to    */
+/*      access database is clean.                       */
+/*      (no character to be misunderstood by database)  */
+/*                                                      */
+/********************************************************/
+PUBLIC char *sql_gooddata(SQLPTR *sqlptr,char *key)
+
+{
+#define OPEP    "devsql.c:sql_gooddata,"
+
+char *gooddata;
+char *sqlkey;
+SQLTYP *sql;
+int phase;
+_Bool proceed;
+
+gooddata=(char *)0;
+sqlkey=(char *)0;
+sql=(SQLTYP *)sqlptr;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //do we have a string to encode
+      if ((key==(char *)0)||(strlen(key)==0)) {
+        gooddata=strdup("NULL");    //no, force a 'NULL' string.
+        phase=999;
+        }
+      break;
+    case 1      :       //checking the key encoding
+      if ((sqlkey=sql_checkencoding(key))==(char *)0) {
+        (void) rou_alert(0,"%s dbkey <%s> encoding is wrong (config?)",OPEP,key);
+        gooddata=strdup("NULL");    //Let assum NULL
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 2      :       //cleaning quote according daemon type
+      switch(sql->sqldb) {
+        case (db_postgres)     :
+          gooddata=pos_cleanquote(sqlkey);
+          break;
+        case (db_maria)        :
+          gooddata=mar_cleanquote(sqlkey);
+          break;
+        case (db_unknown)     :
+        default               :
+          (void) rou_alert(0,"%s Unknown SQL daemon (type='%d')",OPEP,sql->sqldb);
+          break;
+        }
+      sqlkey=rou_freestr(sqlkey);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return gooddata;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to return a dynamicaly allocated      */
+/*      char * with an SQL date computation sequence    */
+/*                                                      */
+/********************************************************/
+PUBLIC char *sql_caldate(SQLPTR *sqlptr,char *expression,int second)
+
+{
+#define OPEP    "devsql.c:sql_caldate,"
+
+char *seq;
+
+seq=(char *)0;
+if (sqlptr!=(SQLPTR *)0) {
+  SQLTYP *sql;
+
+  sql=(SQLTYP *)sqlptr;
+  switch(sql->sqldb) {
+    case db_postgres  :
+      seq=pos_caldate(expression,second);
+      break;
+    case db_maria      :
+      seq=mar_caldate(expression,second);
+      break;
+    default            :
+      (void) rou_alert(0,"%s Unexpected db type='%d' (BUG!?)",
+                          OPEP,(int)sql->sqldb);
+      seq=strdup("");
+      break;
+    }
+  }
+return seq;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to establish a link with the          */
+/*      designated SQL server.                          */
+/*                                                      */
+/********************************************************/
+PUBLIC SQLPTR *sql_opensql()
+
+{
+#define OPEP    "devsql.c:sql_opensql,"
+
+static const char *dbenv[]={"DB_TYPE","DB_HOST","DB_PORT","DB_NAME"};
+
+SQLTYP *sql;
+const char *db[SQLENV];
+int phase;
+_Bool proceed;
+
+sql=(SQLTYP *)0;
+(void) memset(db,'\000',sizeof(db));
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //get the preset sql env variable
+      for (int i=0;i<2;i++) {
+        switch (i)      {
+          case 0        :       //Postgresql
+            if (posenv[0]!=(char *)0)
+              (void) memcpy(db,posenv,sizeof(db));
+            break;
+          case 1        :       //MySQL/Mariadb
+            if (marenv[0]!=(char *)0)
+              (void) memcpy(db,marenv,sizeof(db));
+            break;
+          default       :       //Trouble trouble
+            break;
+          }
+        }
+      break;
+    case 1      :       //get database parameters
+      for (int i=0;i<(sizeof(dbenv)/sizeof(char *));i++) {
+        char *ptr;
+
+        ptr=getenv(dbenv[i]);
+        if (ptr!=(char *)0)
+          db[i]=ptr;    //Variable overwrite
+        }
+      break;
+    case 2      :       //getting dbtype
+      if ((sql=gettyp(db[0]))==(SQLTYP *)0) {
+        (void) rou_alert(0,"%s %s type unknown (config?)",OPEP,dbenv[0]);
+        phase=999;    //missing database information
+        }
+      break;
+    case 3      :       //opening specific database
+      switch (sql->sqldb) {
+        case db_postgres        :
+          sql->db.psql=pos_opensql(db[1],db[2],db[3]);
+          break;
+        case db_maria        :
+          sql->db.msql=mar_opensql(db[1],db[2],db[3]);
+          break;
+        default                 :
+          (void) rou_alert(0,"%s Unexpected type='%d' (bug?)",
+                              OPEP,(int)sql->sqldb);
+          break;
+        }
+      break;
+    case 4      :       //checking if the database is properly open
+      _Bool isok;
+
+      isok=false;
+      switch (sql->sqldb) {
+        case db_postgres:
+          isok=(sql->db.psql!=(POSPTR *)0);
+          break;
+        case db_maria   :
+          isok=(sql->db.msql!=(MARPTR *)0);
+          break;
+        default         :
+          break;
+        }
+      if (isok==false) {
+        (void) rou_alert(0,"%s data-base not properly connected (config?)",OPEP);
+        (void) free(sql);
+        sql=(SQLTYP *)0;
+        phase=999;      //Trouble trouble
+        }
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return (SQLPTR *)sql;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to closed previously establish link   */
+/*      with the designated SQL server.                 */
+/*                                                      */
+/********************************************************/
+PUBLIC SQLPTR *sql_closesql(SQLPTR *sqlptr)
+
+{
+#define OPEP    "devsql.c:sql_closesql,"
+
+SQLTYP *sql;
+int phase;
+_Bool proceed;
+
+sql=(SQLPTR *)sqlptr;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //check if database available
+      if (sql==(SQLTYP *)0) {
+        (void) rou_alert(0,"%s %s Database pointer is NULL (bug?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //opening specific database
+      switch (sql->sqldb) {
+        case db_postgres        :
+          sql->db.psql=pos_closesql(sql->db.psql);
+          break;
+        case db_maria        :
+          sql->db.msql=mar_closesql(sql->db.msql);
+          break;
+        default                 :
+          (void) rou_alert(0,"%s Unexpected type='%d' (bug?)",
+                              OPEP,(int)sql->sqldb);
+          break;
+        }
+      break;
+    case 2      :       //freeing memory used by SQL
+      (void) free(sql);
+      sql=(SQLTYP *)0;
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return (SQLPTR *)sql;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to return a unix time in acsii        */
+/*      format (data-base compatible).                  */
+/*                                                      */
+/********************************************************/
+PUBLIC char *sql_fromunixtime(SQLPTR *sqlptr,time_t timestamp)
+
+{
+#define OPEP    "devsql.c:sql_fromunixtime,"
+
+const char *unixdate;
+
+unixdate="";
+if (sqlptr!=(SQLPTR *)0) {
+  SQLTYP *sql;
+
+  sql=(SQLTYP *)sqlptr;
+  switch(sql->sqldb) {
+    case db_postgres  :
+      unixdate=pos_fromunixtime(timestamp);
+      break;
+    case db_maria      :
+      unixdate=mar_fromunixtime(timestamp);
+      break;
+    default            :
+      (void) rou_alert(0,"%s Unexpected db type='%d' (BUG!?)",
+                          OPEP,(int)sql->sqldb);
+      break;
+    }
+  }
+return strdup(unixdate);
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to return a unix time_t  from an ASCII*/
+/*      string (data-base compatible).                  */
+/*                                                      */
+/********************************************************/
+PUBLIC time_t sql_tounixtime(SQLPTR *sqlptr,const char *date)
+
+{
+#define OPEP    "devsql.c:sql_tounixtime,"
+
+time_t datetime;
+
+datetime=(time_t)0;
+if (sqlptr!=(SQLPTR *)0) {
+  SQLTYP *sql;
+
+  sql=(SQLTYP *)sqlptr;
+  switch(sql->sqldb) {
+    case db_postgres  :
+      datetime=pos_tounixtime(date);
+      break;
+    case db_maria      :
+      datetime=mar_tounixtime(date);
+      break;
+    default            :
+      (void) rou_alert(0,"%s Unexpected db type='%d' (BUG!?)",
+                          OPEP,(int)sql->sqldb);
+      break;
+    }
+  }
+return datetime;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to get an exclusive access to a       */
+/*      specific database table.                        */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool sql_lock(SQLPTR *sqlptr,char *tablename)
+
+{
+#define OPEP    "devsql.c:sql_lock,"
+
+_Bool locked;
+
+locked=false;
+if (sqlptr!=(SQLPTR *)0) {
+  SQLTYP *sql;
+
+  sql=(SQLTYP *)sqlptr;
+  switch(sql->sqldb) {
+    case db_postgres  :
+      locked=pos_lock(sql->db.psql,tablename);
+      break;
+    case db_maria      :
+      locked=mar_lock(sql->db.psql,tablename);
+      break;
+    default            :
+      (void) rou_alert(0,"%s Unexpected db type='%d' (BUG!?)",
+                          OPEP,(int)sql->sqldb);
+      break;
+    }
+  }
+return locked;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to release an exclusive access to a   */
+/*      specific database table.                        */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool sql_unlock(SQLPTR *sqlptr,_Bool commit)
+
+{
+#define OPEP    "devsql.c:sql_unlock,"
+
+_Bool unlocked;
+
+unlocked=false;
+if (sqlptr!=(SQLPTR *)0) {
+  SQLTYP *sql;
+
+  sql=(SQLTYP *)sqlptr;
+  switch(sql->sqldb) {
+    case db_postgres  :
+      unlocked=pos_unlock(sql->db.psql,commit);
+      break;
+    case db_maria      :
+      unlocked=mar_unlock(sql->db.psql,commit);
+      break;
+    default            :
+      (void) rou_alert(0,"%s Unexpected db type='%d' (BUG!?)",
+                          OPEP,(int)sql->sqldb);
+      break;
+    }
+  }
+return unlocked;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to do some action to change database  */
+/*      contents.                                       */
+/*                                                      */
+/********************************************************/
+PUBLIC int sql_request(SQLPTR *sqlptr,const char *fmt,...)
+
+{
+#define OPEP    "devsql.c:sql_request,"
+
+int number;
+SQLTYP *sql;
+va_list args;
+char *cmd;
+
+number=-1;
+sql=(SQLTYP *)sqlptr;
+va_start(args,fmt);
+if ((rou_vasprintf(&cmd,fmt,args))>0) {
+  (void) rou_alert(0,"JMPDBG SQL cmd=<%s>",cmd);
+  switch(sql->sqldb) {
+    case db_postgres    :
+      number=pos_request(sql->db.psql,cmd);
+      break;
+    case db_maria       :
+      number=mar_request(sql->db.msql,cmd);
+      break;
+    default             :
+      (void) rou_alert(0,"%s Unexpected type='%d' (BUG!?)",
+                          OPEP,(int)sql->sqldb);
+      break;
+    }
+  (void) free(cmd);
+  }
+va_end(args);
+return number;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to get the ASCII form of field located*/
+/*      in a specific tuple + name in sqlresult.        */
+/*                                                      */
+/********************************************************/
+PUBLIC const char *sql_getvalue(SQLPTR *sqlptr,SQLRES *rs,int row,const char *name)
+
+{
+#define OPEP    "devsql.c:sql_getvalue,"
+
+const char *value;
+SQLTYP *sql;
+
+value=(const char *)0;
+sql=(SQLTYP *)sqlptr;
+switch(sql->sqldb) {
+  case db_postgres    :
+    value=pos_getvalue((POSRES *)rs,row,name);
+    break;
+  case db_maria       :
+    value=mar_getvalue((MARRES *)rs,row,name);
+    break;
+  default             :
+    (void) rou_alert(0,"%s Unexpected type='%d' (BUG!?)",
+                        OPEP,(int)sql->sqldb);
+    break;
+  }
+return value;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to create a data search request and   */
+/*      submit it to the to the proper database daemon. */
+/*                                                      */
+/********************************************************/
+PUBLIC SQLRES *sql_gettupple(SQLPTR *sqlptr,const char *fmt,...)
+
+{
+#define OPEP    "devsql.c:sql_gettupple,"
+
+SQLRES *rs;
+
+rs=(SQLRES *)0;
+if (sqlptr!=(SQLPTR *)0) {
+  SQLTYP *sql;
+  va_list args;
+  char *cmd;
+
+  sql=(SQLTYP *)sqlptr;
+  va_start(args,fmt);
+  if ((rou_vasprintf(&cmd,fmt,args))>0) {
+    switch(sql->sqldb) {
+      case db_postgres    :
+        rs=(SQLRES *)pos_gettupple(sql->db.psql,cmd);
+        break;
+      case db_maria       :
+        rs=(SQLRES *)mar_gettupple(sql->db.msql,cmd);
+        break;
+      default             :
+        (void) rou_alert(0,"%s Unexpected type='%d' (BUG!?)",
+                            OPEP,(int)sql->sqldb);
+        break;
+      }
+    (void) free(cmd);
+    }
+  va_end(args);
+  }
+return rs;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to the number of tupple within a      */
+/*      result issued by a successfull command.         */
+/*                                                      */
+/********************************************************/
+PUBLIC int sql_getnbrtupple(SQLPTR *sqlptr,SQLRES *rs)
+
+{
+#define OPEP    "devsql.c:sql_getnbrtupple,"
+
+int numrow;
+
+numrow=-1;
+if (sqlptr!=(SQLPTR *)0) {
+  SQLTYP *sql;
+
+  sql=(SQLTYP *)sqlptr;
+  switch(sql->sqldb) {
+    case db_postgres  :
+      numrow=pos_nbrtupple((POSRES *)rs);
+      break;
+    case db_maria      :
+      numrow=mar_nbrtupple((MARRES *)rs);
+      break;
+    default            :
+      (void) rou_alert(0,"%s Unexpected db type='%d' (BUG!?)",
+                          OPEP,(int)sql->sqldb);
+      numrow=0;
+      break;
+    }
+  }
+return numrow;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to drop result issued by a successfull*/
+/*      command.                                        */
+/*                                                      */
+/********************************************************/
+PUBLIC SQLRES *sql_droptupple(SQLPTR *sqlptr,SQLRES *rs)
+
+{
+#define OPEP    "devsql.c:sql_dropresult,"
+
+if (rs!=(SQLRES *)0) {
+  SQLTYP *sql;
+
+  sql=(SQLTYP *)sqlptr;
+  switch(sql->sqldb) {
+    case db_postgres    :
+      rs=(SQLRES *)pos_dropresult((POSRES *)rs);
+      break;
+    case db_maria       :
+      rs=(SQLRES *)pos_dropresult((MARRES *)rs);
+      break;
+    default             :
+      (void) rou_alert(0,"%s Unexpected type='%d' (BUG!?)",
+                          OPEP,(int)sql->sqldb);
+      rs=(SQLRES *)0;
+      break;
+    }
+  }
+return rs;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to retrieve/store data from/to        */
+/*      database.                                       */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool sql_mngdata(SQLPTR *sqlptr,FLDTYP *field,SQLENUM action)
+
+{
+(void) rou_alert(0,"JMPDBG sql_mngdata need to be implemented!");
+return false;
+}
diff --git a/lib/devsql.d b/lib/devsql.d
new file mode 100644 (file)
index 0000000..bc0f189
--- /dev/null
@@ -0,0 +1,44 @@
+devsql.o devsql.d : devsql.c /usr/include/stdc-predef.h /usr/include/ctype.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/types/locale_t.h \
+ /usr/include/bits/types/__locale_t.h /usr/include/stdio.h \
+ /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/stdlib.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/strings.h subrou.h /usr/include/linux/types.h \
+ /usr/include/asm/types.h /usr/include/asm-generic/types.h \
+ /usr/include/asm-generic/int-ll64.h /usr/include/asm/bitsperlong.h \
+ /usr/include/asm-generic/bitsperlong.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h unimar.h unipos.h devsql.h \
+ unisql.h
diff --git a/lib/devsql.h b/lib/devsql.h
new file mode 100644 (file)
index 0000000..1332fda
--- /dev/null
@@ -0,0 +1,60 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Low level subroutine declaration                */
+/*     to handle SQL resquest.                         */
+/*                                                     */
+/********************************************************/
+#ifndef        DEVSQL
+#define DEVSQL
+
+#include        "unisql.h"
+
+//An SQL database pointer reference
+typedef void SQLPTR;
+
+//An SQL database result reference
+typedef void SQLRES;
+
+//procedure to make sure a string is acceptable
+//as key search by database
+extern char *sql_gooddata(SQLPTR *sqlptr,char *key);
+
+//procedure to return a dynamicaly allocated string
+//to do SQL compute a date with a delta in second
+extern char *sql_caldate(SQLPTR *sqlptr,char *expression,int second);
+
+//to connect a remote SQL server
+extern SQLPTR *sql_opensql();
+
+//procedure to close an previously opened SQL channel
+extern SQLPTR *sql_closesql(SQLPTR *sqlptr);
+
+//converting a time to a database representation
+extern char *sql_fromunixtime(SQLPTR *sqlptr,time_t timestamp);
+
+//converting a database time representation to unix time
+extern time_t sql_tounixtime(SQLPTR *sqlptr,const char *date);
+
+//procedure to LOCK access to a database table
+extern _Bool sql_lock(SQLPTR *sqlPTR,char *tablename);
+
+//procedure to UNLOCK access to a database table
+extern _Bool sql_unlock(SQLPTR *sqlptr,_Bool commit);
+
+//procedure to transmit a simple data-base action
+extern int sql_request(SQLPTR *sqlptr,const char *fmt,...);
+
+//to extract data from database
+extern const char *sql_getvalue(SQLPTR *sqlptr,SQLRES *rs,int row,const char *name);
+
+//to extract data from database
+extern SQLRES *sql_gettupple(SQLPTR *sqlptr,const char *fmt,...);
+
+//to retreive the number of tupple available within result
+extern int sql_getnbrtupple(SQLPTR *sqlptr,SQLRES *);
+
+//to free memory used by a database tupple
+extern SQLRES *sql_droptupple(SQLPTR *sqlptr,SQLRES *res);
+
+#endif
diff --git a/lib/geseml.c b/lib/geseml.c
new file mode 100644 (file)
index 0000000..8b091dd
--- /dev/null
@@ -0,0 +1,967 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Implement all routine to manage email transport */
+/*     exchange.                                       */
+/*                                                     */
+/********************************************************/
+#include        <dirent.h>
+#include        <errno.h>
+#include        <string.h>
+#include        <unistd.h>
+#include        <uuid/uuid.h>
+
+#include       "subrou.h"
+#include       "unidns.h"
+#include       "unieml.h"
+#include       "unipar.h"
+#include       "unitls.h"
+#include       "devsql.h"
+#include       "geseml.h"
+
+//dovecot local storage directory
+#define DIRDOV  "DOV_MAILDIR"
+
+typedef struct  {
+        char *domain;   //common domain
+        TRATYP **todo;  //Transfer reference
+        }REFTYP;
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to compare 2 transaction struct       */
+/*                                                      */
+/********************************************************/
+static int cmp_sessid(const void *p1,const void *p2)
+
+
+{
+return strcmp((*((TRATYP **)p1))->sessid,(*((TRATYP **)p2))->sessid);
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to display/debug TRATYP record content*/
+/*                                                      */
+/********************************************************/
+static REFTYP *findref(REFTYP **reflist,char *domain)
+
+
+{
+REFTYP *found;
+
+found=(REFTYP *)0;
+if (reflist!=(REFTYP **)0) {
+  while (*reflist!=(REFTYP *)0) {
+    if (strcmp((*reflist)->domain,domain)==0) {
+      found=*reflist;
+      break;
+      }
+    reflist++;
+    }
+  }
+return found;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to free a add domain reference from a */
+/*      list of reference.                              */
+/*                                                      */
+/********************************************************/
+static REFTYP *freeref(REFTYP *reflist)
+
+{
+if (reflist!=(REFTYP *)0) {
+  (void) free(reflist->todo);
+  (void) free(reflist);
+  reflist=(REFTYP *)0;
+  }
+return reflist;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to add a add domain reference to a    */
+/*      list of reference.                              */
+/*                                                      */
+/********************************************************/
+static REFTYP **addref(REFTYP **reflist,TRATYP *trans)
+
+{
+#define OPEP    "geseml.c:addref,"
+
+char *dom;
+REFTYP *ref;
+int phase;
+_Bool proceed;
+
+dom=(char *)0;
+ref=(REFTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+   switch (phase) {
+    case 0    :         //Firsy Check if we have data
+      if (trans==(TRATYP *)0) {
+        (void) rou_alert(0,"%s trans is NULL (Bug?)",OPEP);
+        phase=999;      //No need to gi further
+        }
+      break;
+    case 1    :         //Do we hav a domain
+      if ((dom=strchr(trans->rcptto,'@'))==(char *)0) {
+        (void) rou_alert(0,"%s unable to find domain within <%s> (Bug?)",
+                            OPEP,trans->rcptto);
+        phase=999;    //No dmain
+        }
+      break;
+    case 2    :       //do we have domain
+      dom++;
+      if (strlen(dom)==0) {
+        (void) rou_alert(0,"%s Domain length from < <%s> is zero  (Bug?)",
+                            OPEP,trans->rcptto);
+        phase=999;    //No dmain
+        }
+      break;
+    case 3    :       //do we know domain
+      if ((ref=findref(reflist,dom))!=(REFTYP *)0)
+        phase++;      //we have reference already
+      break;
+    case 4    :       //Adding reference
+      ref=(REFTYP *)calloc(1,sizeof(REFTYP));
+      ref->domain=dom;
+      reflist=(REFTYP **)rou_addlist((void **)reflist,(void *)ref);
+      break;
+    case 5    :       //Adding transaction within reference
+      ref->todo=(TRATYP **)rou_addlist((void **)ref->todo,(void *)trans);
+      break;
+    default   :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return reflist;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to display/debug TRATYP record content*/
+/*                                                      */
+/********************************************************/
+static void print_tra(FILE *qfile,char *line,char *temps)
+
+{
+if (qfile==(FILE *)0)  
+  (void) rou_alert(0,"TRA=<%s>",line);
+else {
+  if (temps!=(char *)0)
+    (void) fprintf(qfile,"%s\n",temps);
+  (void) fprintf(qfile,"%s\n",line);
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to display/debug ONE TRATYP record    */
+/*      content                                         */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_dump_one_tra(FILE *qfile,TRATYP *tra)
+
+{
+#define OPEP    "geseml.c:eml_dump_one_tra,"
+_Bool status;
+
+status=false;
+if (tra!=(TRATYP *)0) {
+  char temps[50];
+  char line[300];
+
+  status=true;
+  (void) strftime(temps,sizeof(temps),"#%Y-%m-%d %H:%M:%S",
+                                      localtime(&(tra->date)));
+  (void) snprintf(line,sizeof(line),"%c\t%lu\t%05u %*s %*s %*s %*s \"%*s\" %s",
+                                    tra->code,
+                                    tra->date,
+                                    tra->delay,
+                                    -32,tra->sessid,
+                                    -30,tra->reverse,
+                                    -30,tra->rcptto,
+                                    -30,tra->sfrom,
+                                    -30,tra->hfrom,
+                                    tra->hsubject
+                                    );
+  (void) print_tra(qfile,line,temps);
+  if (tra->resp!=(char **)0) {
+    char **ptr;
+
+    ptr=tra->resp;
+    while (*ptr!=(char *)0) {
+      (void) snprintf(line,sizeof(line),"\t%s",*ptr);
+      (void) print_tra(qfile,line,(char *)0);
+      ptr++;
+      }
+    }
+  }
+return status;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to display/debug TRATYP record content*/
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_dump_list_tra(FILE *qfile,TRATYP **tra)
+
+{
+#define OPEP    "geseml.c:eml_dump_list_tra,"
+
+_Bool action;
+
+action=false;
+if (tra!=(TRATYP **)0) {
+  time_t isnow;
+
+  isnow=time((time_t *)0);
+  if (qfile!=(FILE *)0) {
+    (void) fprintf(qfile,"#'C', completed email task\n");
+    (void) fprintf(qfile,"#'L', Local email\n");
+    (void) fprintf(qfile,"#'R', Remote email\n");
+    (void) fprintf(qfile,"#code\tdate\t\tdelay "
+                         "%*s %*s %*s %*s %*s subject\n",
+                          -32,"session-id",
+                          -30,"reverss-address",
+                          -30,"recipient",
+                          -30,"sfrom",
+                          -30,"efrom");
+    }
+  while (*tra!=(TRATYP *)0) {
+    (void) eml_dump_one_tra(qfile,*tra);
+    if (((*tra)->date+(*tra)->delay)<=isnow) 
+      action=true;
+    tra++;
+    }
+  }
+return action;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to duplicate an resp list within a tra*/
+/*      record.                                         */
+/*                                                      */
+/********************************************************/
+PUBLIC void eml_duptra_resp(TRATYP *tra,char **resp)
+
+{
+if (tra!=(TRATYP *)0) {
+  (void) eml_freetra_resp(tra);
+  if (resp!=(char **)0) {
+    while (*resp!=(char *)0) {
+      tra->resp=(char **)rou_addlist((void **)tra->resp,strdup(*resp));
+      resp++;
+      }
+    } 
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to duplicate TRATYP structure         */
+/*                                                      */
+/********************************************************/
+PUBLIC TRATYP *eml_duptra(TRATYP *tra)
+
+{
+TRATYP *dup;
+
+dup=(TRATYP *)0;
+if (tra!=(TRATYP *)0) {
+  dup=(TRATYP *)calloc(1,sizeof(TRATYP));
+  dup->code=tra->code;
+  dup->date=tra->date;
+  dup->delay=tra->delay;
+  dup->reverse=strdup(tra->reverse);
+  dup->sfrom=strdup(tra->sfrom);
+  dup->hfrom=strdup(tra->hfrom);
+  dup->hsubject=strdup(tra->hsubject);
+  dup->rcptto=strdup(tra->rcptto);
+  dup->sessid=strdup(tra->sessid);
+  if (tra->resp!=(char **)0) {
+    char **ptr;
+
+    ptr=tra->resp;
+    while (*ptr!=(char *)0) {
+      dup->resp=(char **)rou_addlist((void **)dup->resp,(void *)strdup(*ptr));
+      ptr++;
+      }
+    }
+  }
+return dup;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to scan the a qfile and build a       */
+/*      list of email transport directive.              */
+/*                                                      */
+/********************************************************/
+PUBLIC TRATYP **eml_scanqfile(TRATYP **list,FILE *qfile)
+
+{
+#define OPEP    "geseml.c:eml_scanqfile,"
+#define FMT     "%c %ld %d %s %s %s %s \"%[^\"]\" %99[^\n]"
+
+TRATYP *cur;    //previous trans record
+char *ptr;
+char line[300];
+
+cur=(TRATYP *)0;
+while ((ptr=fgets(line,sizeof(line),qfile))!=(char *)0) {
+  char id[300];
+  char reverse[300];
+  char sfrom[300];
+  char hfrom[300];
+  char hsubject[300];
+  char to[300];
+  char code;
+  u_long date;
+  u_int delay;
+  int phase;
+  _Bool proceed;
+
+  phase=0;
+  proceed=true;
+  while (proceed==true) {
+    switch (phase) {
+      case 0    :       //Removing comment
+        if ((ptr=strchr(line,'#'))!=(char *)0)
+          *ptr='\000';
+        (void) rou_stripcrlf(line);
+        if (strlen(line)==0)
+          phase=999;    //No data within line
+        break;
+      case 1    :       //scanning line contents
+        switch (line[0]) {
+          case '\t'     :       //extra response line
+            if ((cur!=(TRATYP *)0)&&(strlen(line)>1)) 
+              cur->resp=(char **)rou_addlist((void **)cur->resp,strdup(line+1));
+            phase=999;    //not a scanable trans line
+            break;
+          case 'C'      :       //Directives
+          case 'L'      :       
+          case 'R'      :       
+          case 'W'      :       
+            (void) memset(hfrom,'\000',sizeof(hfrom));
+            (void) memset(hsubject,'\000',sizeof(hsubject));
+            if (sscanf(line,FMT,&code,&date,&delay,
+                                id,reverse,to,sfrom,hfrom,hsubject)!=9) {
+              (void) rou_alert(0,"%s Unable to scan <line=%s> (config?)",OPEP,line);
+              phase=999;    //No data within line
+              }
+            break;
+          default       :
+            (void) rou_alert(0,"%s Unexpected code within  <line=%s> (config?)",
+                                OPEP,line);
+            phase=999;    //No data within line
+            break;
+          }
+        break;
+      case 2    :       //cleaning element
+        while ((ptr=strchr(hfrom,'\"'))!=(char *)0)
+          *ptr=' ';
+        break;
+      case 3    :       //storing line contents
+        if (strlen(id)>0) {     //always?
+          cur=(TRATYP *)calloc(1,sizeof(TRATYP));
+          cur->code=code;
+          cur->date=date;
+          cur->delay=delay;
+          cur->reverse=strdup(reverse);
+          cur->sessid=strdup(id);
+          cur->rcptto=strdup(to);
+          cur->sfrom=strdup(sfrom);
+          cur->hfrom=strdup(hfrom);
+          cur->hsubject=strdup(hsubject);
+          list=(TRATYP **)rou_addlist((void **)list,(void *)cur); 
+          }
+        break;
+      default   :       //SAFE Guard
+        proceed=false;
+        break;
+      }
+    phase++;
+    }
+  }
+return list;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to generate a list of todo file to    */
+/*      sender MTA.                                     */
+/*                                                      */
+/********************************************************/
+PUBLIC void eml_todoqfile(TRATYP **list)
+
+{
+#define OPEP    "geseml.c:eml_todoqfile,"
+
+REFTYP **domlist;
+
+domlist=(REFTYP **)0;
+if (list!=(TRATYP **)0) {
+  while (*list!=(TRATYP *)0) {
+    domlist=addref(domlist,*list);
+    list++;
+    }
+  }
+if (domlist!=(REFTYP **)0) {
+  int iter;
+  time_t current;
+  struct tm *tinfo;
+  char buffer[80];
+  REFTYP **ptr;
+
+  iter=0;
+  (void) time(&current);
+  tinfo=localtime(&current);
+  (void) strftime(buffer,sizeof(buffer),"%s",tinfo);
+  ptr=domlist;
+  while (*ptr!=(REFTYP *)0) {
+    FILE *qfile;
+    _Bool action;
+    const char *ext;
+    char qname[150];
+    int phase;
+    _Bool proceed;
+   
+    qfile=(FILE *)0;
+    action=false;
+    ext=EXTRANS;
+    (void) snprintf(qname,sizeof(qname),"%08ld-%08d-%04d",current,getpid(),iter);
+    phase=0;
+    proceed=true;
+    while (proceed==true) {
+      switch (phase) {
+        case 0  :       //Opening qfile
+          if ((qfile=eml_createqfile(qname,EXTOBE))==(FILE *)0) 
+            phase=999;  //Trouble trouble
+          break;
+        case 1  :       //dumping TRANS data to file
+          (void) eml_sort_list((*ptr)->todo);
+          if ((action=eml_dump_list_tra(qfile,(*ptr)->todo))==true) {
+            ext=EXTODO;
+            }
+          break;
+        case 2  :       //Closing file
+          (void) eml_closeqfile(qfile);
+          break;
+        case 3  :       //file ready, renaming file, 
+          (void) eml_renameqfile(qname,EXTOBE,ext);          
+          break;
+        case 4  :       //do we need to start sender?
+                        //Never start sender anymore (Saturday, July 19 2025)
+#ifdef  JMPDBG
+          if ((action==true)&&(foreground==false)) 
+            (void) eml_start_sender(qname);
+#endif
+          break;
+        default :       //SAFE Guard
+          proceed=false;
+          break;
+        }
+      phase++;
+      } 
+    *ptr=freeref(*ptr);
+    ptr++;
+    iter++;
+    }
+  (void) free(domlist);
+  }
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to clean/remove 'done' file.          */
+/*                                                      */
+/********************************************************/
+PUBLIC void eml_doneqfile(TRATYP **list)
+
+{
+#define OPEP    "geseml.c:eml_doneqfile,"
+
+TRATYP **next;
+
+next=(TRATYP **)0;
+if (list!=(TRATYP **)0) {
+  TRATYP **ptr;
+
+  ptr=list;
+  while (*ptr!=(TRATYP *)0) {
+    _Bool toadd;
+
+    toadd=true;
+    switch ((*ptr)->code) {
+      case 'C'  :       //email completed 
+        toadd=false;
+        (void) eml_freetra_resp(*ptr);  //no need to report actions
+        if (eml_countqfile((*ptr)->sessid,-1)==0) {
+          char fname[200];
+
+          (void) rou_alert(0,"%s JMPDBG need to remove <%s>",OPEP,(*ptr)->sessid);
+          (void) snprintf(fname,sizeof(fname),"%s.%s",(*ptr)->sessid,EXTCNT);
+          (void) eml_deleteqfile(fname);
+          }
+        break;
+      case 'W'  :       //need to send a warning
+        (void) eml_do_warning(*ptr);
+        break;
+      default   :
+        break;
+      }
+    if (toadd==true)
+      next=(TRATYP **)rou_addlist((void **)next,(void *)eml_duptra(*ptr));
+    ptr++;
+    }
+  }
+if (next!=(TRATYP **)0) {
+  FILE *qfile;
+  time_t current;
+  char qname[150];
+  int phase;
+  _Bool proceed;
+   
+  qfile=(FILE *)0;
+  (void) time(&current);
+  (void) snprintf(qname,sizeof(qname),"%08ld-%08d-%04d",current,getpid(),0);
+  phase=0;
+  proceed=true;
+  while (proceed==true) {
+    switch (phase) {
+      case 0  :       //Opening qfile
+        if ((qfile=eml_createqfile(qname,EXTOBE))==(FILE *)0) 
+          phase=999;  //Trouble trouble
+        break;
+      case 1  :       //dumping TRANS data to file
+        (void) eml_dump_list_tra(qfile,next);
+        break;
+      case 2  :       //Closing file
+        (void) eml_closeqfile(qfile);
+        break;
+      case 3  :       //file ready, renaming file, 
+        (void) eml_renameqfile(qname,EXTOBE,EXTRANS);
+        break;
+      default :       //SAFE Guard
+        proceed=false;
+        break;
+      }
+    phase++;
+    }
+  next=(TRATYP **)rou_freelist((void **)next,(genfree_t)eml_freetra);
+  }
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to store an email contents with the   */
+/*      recipient email area.                           */
+/*      Return true if successful, false otherwise       */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_store_email(TRATYP *tra)
+
+{
+#define OPEP    "geseml.c:eml_store_email,"
+
+_Bool done;
+char *locdom;
+char dirname[300];
+int phase;
+_Bool proceed;
+
+done=false;
+locdom=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  (void) rou_dbglive(9,OPEP,"phase='%d'",phase);
+  switch (phase) {
+    case 0      :       //Extracting recipient email
+      if ((tra!=(TRATYP *)0)&&(tra->rcptto!=(char *)0)&&(strlen(tra->rcptto)>0))
+        locdom=strrchr(tra->rcptto,'@');
+      if (locdom==(char *)0) {
+        (void) rou_alert(0,"%s Unable to get recipient domain in <%s> (Bug?)",
+                            OPEP,tra->rcptto);
+        phase=999;      //Big trouble
+        }
+      else
+        locdom++;
+      break;
+    case 1      :       //creating recipient directory if needed
+      DIR *dir;
+      char *path;
+
+      path=rou_apppath(getenv(DIRDOV));
+      (void) snprintf(dirname,sizeof(dirname),"%s/%s/%s",path,locdom,tra->rcptto);
+      *(strrchr(dirname,'@'))='\000';   //keeping username only
+      (void) strncat(dirname,"/dovecot/new",sizeof(dirname)-strlen(dirname));
+      if ((dir=opendir(dirname))==(DIR *)0) {
+        if (rou_do_mkpdir(dirname)==false) {
+          (void) rou_alert(0,"%s Unable to create <%s> directory (system?/bug?)",
+                              OPEP,dirname);
+          phase=999;    //big trouble, No need to go further
+          }
+        }
+      else
+        closedir(dir);
+      (void) free(path);
+      break;
+    case 2      :       //duplicating session file to the recipient directory
+      if (eml_dupqfile(tra->sessid,dirname)==false) {
+        (void) rou_alert(0,"%s Unable to store email <%s> to user directory <%s>",
+                            OPEP,tra->sessid,dirname);
+        phase=999;      //big trouble.
+        }
+      break;
+    case 3      :       //everything fine
+      char cmt[200];
+
+      (void) eml_freetra_resp(tra);
+      (void) snprintf(cmt,sizeof(cmt),"%d Message delivered in mail box",CMDOK);
+      tra->resp=(char **)rou_addlist((void **)tra->resp,(void *)strdup(cmt));
+      done=true;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return done;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to add an execution delay to an email */
+/*      sending request.                                */
+/*      Return true if succesfull, false otherwise      */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_add_delay(time_t isnow,TRATYP *tra)
+
+{
+#define OPEP    "geseml.c:eml_add_delay,"
+_Bool status;
+
+status=false;
+if (tra!=(TRATYP *)0) {
+  tra->date=isnow;
+  tra->delay=(tra->delay*2)+60;
+  status=true;
+  }
+return status;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to fork process and execute the       */
+/*      "sender" component.                             */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_start_sender(char *todo)
+
+{
+#define OPEP    "unieml.c:eml_start_sender,"
+
+_Bool status;
+
+status=false;
+switch (fork()) {
+  case -1       :       //trouble trouble
+    (void) rou_alert(0,"%s Unable to fork process (error=<%s> Bug?)",
+                        OPEP,strerror(errno));
+    break;
+  case 0        :       //the forked process
+    char *execpath;
+    char *argv[10];
+    char dbgstr[10];
+    char buffer[300];
+
+    (void) memset(argv,'\000',sizeof(argv));
+    (void) snprintf(dbgstr,sizeof(dbgstr),"%d",debug);
+    (void) snprintf(buffer,sizeof(buffer),"%s%s",SBINDIR,SENDER);        
+    execpath=rou_apppath(buffer);
+    (void) snprintf(buffer,sizeof(buffer),"%s.%s",todo,EXTODO);
+    argv[0]=execpath;     //The FULL SENDER name
+    argv[1]="-c";
+    argv[2]=config;
+    argv[3]="-d";
+    argv[4]=dbgstr;
+    argv[5]=buffer;
+    if (rootdir!=(char *)0) {
+      argv[5]="-r";
+      argv[6]=rootdir;
+      argv[7]=buffer;
+      }
+    (void) rou_dbglive(2,OPEP,"Starting sender=<%s> todo= <%s>",execpath,buffer);
+    if (execv(execpath,argv)<0) {
+      (void) rou_alert(0,"%s Unable to exec process=<%s> (error=<%s> Bug?)",
+                        OPEP,execpath,strerror(errno));
+      (void) rou_freestr(execpath);
+      (void) exit(-1);
+      }
+    break;
+  default       :               //the forking process itself
+    (void) usleep(10000);       //relax delay
+    status=true;
+    break;
+  }
+return status;
+#undef   OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to make sure todo list is sorted      */
+/*      according the session id.                       */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_sort_list(TRATYP **list)
+
+{
+_Bool result;
+
+result=false;
+if (list!=(TRATYP **)0) { 
+  register int num;
+
+  num=rou_nbrlist((void **)list);
+  (void) qsort((void *)list,num,sizeof(TRATYP *),cmp_sessid);
+  result=true;
+  }
+return result;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to build a warning/reply about an     */
+/*      unsuccessfull email sending. The Email is sent  */
+/*      back to the originator.                         */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_do_warning(TRATYP *tra)
+
+{
+#define OPEP    "geseml.c:eml_do_warning,"
+
+_Bool done;
+char *sessata;  //attachement session number
+FILE *qout;
+char orig[100];
+char cmt[200];
+int phase;
+_Bool proceed;
+
+done=false;
+sessata=eml_getmainsesid();
+(void) snprintf(orig,sizeof(orig),"%s@%s","noreply",getenv("DFLTDOMAIN"));
+qout=(FILE *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  (void) rou_dbglive(9,OPEP,"phase='%d'",phase);
+  switch (phase) {
+    case 0      :       //create the new attachement file
+      if ((qout=eml_createqfile(sessata,(char *)0))==(FILE *)0) {
+        (void) rou_alert(0,"%s Unable to create attachement file (system?)",
+                              OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //Preparing attachement email
+      (void) fprintf(qout,"%s%s Email daemon <%s>\n",HFROM,APPNAME,orig);
+      (void) fprintf(qout,"To: <%s>\n",tra->sfrom);
+      (void) fprintf(qout,"Date: %s\n",rou_ascsysstamp(time((time_t *)0)));
+      (void) fprintf(qout,"%s%s\n",HTITLE,*(tra->resp)+4);
+      break;
+    case 2      :       //attaching (200 line at most) from email itself
+      done=eml_attache(qout,tra->sessid,tra->resp,200);
+      qout=eml_closeqfile(qout);
+      if (done==false) {
+        (void) rou_alert(0,"%s Unable to create attachement session (system?)",
+                              OPEP);
+        phase=999;
+        }
+      break;
+    case 3      :       //creating a count file for attachement file
+      if ((qout=eml_createqfile(sessata,EXTCNT))==(FILE *)0) {
+        (void) rou_alert(0,"%s Unable to open 'count' qfile <%s> (error=<%s>)",
+                            OPEP,sessata,strerror(errno));
+        (void) eml_deleteqfile(sessata);
+        done=false;
+        phase=999;
+        }
+      break;
+    case 4      :       //set number winthin sessata count file
+      (void) fprintf(qout,"1\n");
+      (void) eml_closeqfile(qout);
+      break;
+    case 5      :       //counting original session
+      switch (eml_countqfile(tra->sessid,-1)) {
+        case -1 :
+          (void) rou_alert(0,"%s Unable to count down sessid <%s>",
+                              OPEP,tra->sessid);
+          phase=999;
+          break;
+        case 0  :       //session file is not needed anymore
+          (void) snprintf(cmt,sizeof(cmt),"%s.%s",tra->sessid,EXTCNT);
+          (void) eml_deleteqfile(cmt);
+          break;
+        default :       //Nothing to do
+          break;
+        }
+      break;
+    case 6      :       //check if email is FROM local or remote
+      char *domain;
+      int status;
+
+      domain=strchr(tra->sfrom,'@');
+      status=dns_is_domain_local(domain,tls_get_bind_afn());
+      switch (status) {
+        case dns_local  :
+          tra->code='L';
+          break;
+        case dns_remote :
+          tra->code='R';
+          break;
+        case dns_nomx   :       //Should NEVER ever occur
+          (void) rou_alert(0,"%s Unable to find MX for domain<%s> (bug?)",
+                              OPEP,status,domain);
+          phase=999;
+          break;
+        default         :
+          (void) rou_alert(0,"%s Unexpected status='%d' for domain<%s> (bug?)",
+                              OPEP,status,domain);
+          phase=999;
+          break;                
+        }
+      break;
+    case 7      :       //reversing originator/recipient
+      tra->delay=0;
+      tra->sessid=rou_freestr(tra->sessid);
+      tra->sessid=strdup(sessata);
+      tra->rcptto=rou_freestr(tra->rcptto);
+      tra->rcptto=tra->sfrom;
+      tra->sfrom=strdup(orig);
+      done=true;
+      break;
+    case 8      :       //building the new sessid file
+      tra->resp=(char **)rou_freelist((void **)tra->resp,(genfree_t)rou_freestr);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+sessata=rou_freestr(sessata);
+return done;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check email header and extract     */
+/*      session data.                                   */
+/*      Return true if data was extracted.              */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_scan_headerline(SESTYP *session,const char *line)
+
+{
+#define OPEP    "geseml.c:eml_scan_headerline,"
+_Bool extracted;
+
+const char *look[]={HFROM,HTITLE,(char *)0};
+
+extracted=false;
+if ((line!=(char *)0)&&(strlen(line)>0)) {
+  (void) rou_dbglive(0,OPEP,"scaning header <%s>",line);
+  for (int i=0;(extracted==false)&&(look[i]!=(char *)0);i++) {
+    int max;
+
+    max=strlen(look[i]);
+    if (strncasecmp(line,look[i],max)==0) {
+      extracted=true;
+      switch (i) {
+        case 0        :       //Inserting the header from in session
+          session->hfrom=rou_freestr(session->hfrom);
+          session->hfrom=strdup(line+max);
+          break;
+        case 1        :       //Inserting the header from in session
+          session->hsubject=rou_freestr(session->hsubject);
+          session->hsubject=strdup(line+max);
+          break;
+        default       :
+          (void) rou_alert(0,"%s Code missing for Head entrey <%s> (Bug?!)",
+                              OPEP,look[i]);
+          break;
+        }
+      }
+    }
+  }
+return extracted;
+
+#undef  OPEP
+}
diff --git a/lib/geseml.d b/lib/geseml.d
new file mode 100644 (file)
index 0000000..e732ec2
--- /dev/null
@@ -0,0 +1,111 @@
+geseml.o geseml.d : geseml.c /usr/include/stdc-predef.h /usr/include/dirent.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/dirent.h \
+ /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h \
+ /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/dirent_ext.h /usr/include/errno.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/string.h \
+ /usr/include/bits/libc-header-start.h /usr/include/bits/types/locale_t.h \
+ /usr/include/bits/types/__locale_t.h /usr/include/strings.h \
+ /usr/include/unistd.h /usr/include/bits/posix_opt.h \
+ /usr/include/bits/environments.h /usr/include/bits/confname.h \
+ /usr/include/bits/getopt_posix.h /usr/include/bits/getopt_core.h \
+ /usr/include/bits/unistd_ext.h /usr/include/uuid/uuid.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/sys/time.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h unidns.h \
+ subafn.h /usr/include/netdb.h /usr/include/netinet/in.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h /usr/include/bits/socket.h \
+ /usr/include/bits/socket_type.h /usr/include/bits/sockaddr.h \
+ /usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h unieml.h \
+ /usr/include/stdio.h /usr/include/bits/types/__fpos_t.h \
+ /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h unipar.h unitls.h \
+ /usr/include/openssl/ssl.h /usr/include/openssl/macros.h \
+ /usr/include/openssl/opensslconf.h /usr/include/openssl/configuration.h \
+ /usr/include/openssl/opensslv.h /usr/include/openssl/e_os2.h \
+ /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-least.h /usr/include/openssl/e_ostime.h \
+ /usr/include/openssl/comp.h /usr/include/openssl/crypto.h \
+ /usr/include/stdlib.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/openssl/safestack.h \
+ /usr/include/openssl/stack.h /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h /usr/include/openssl/core.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/comperr.h /usr/include/openssl/bio.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/x509.h \
+ /usr/include/openssl/buffer.h /usr/include/openssl/buffererr.h \
+ /usr/include/openssl/evp.h /usr/include/openssl/core_dispatch.h \
+ /usr/include/openssl/indicator.h /usr/include/openssl/params.h \
+ /usr/include/openssl/bn.h /usr/include/openssl/bnerr.h \
+ /usr/include/openssl/evperr.h /usr/include/openssl/objects.h \
+ /usr/include/openssl/obj_mac.h /usr/include/openssl/asn1.h \
+ /usr/include/openssl/asn1err.h /usr/include/openssl/objectserr.h \
+ /usr/include/openssl/ec.h /usr/include/openssl/ecerr.h \
+ /usr/include/openssl/rsa.h /usr/include/openssl/rsaerr.h \
+ /usr/include/openssl/dsa.h /usr/include/openssl/dh.h \
+ /usr/include/openssl/dherr.h /usr/include/openssl/dsaerr.h \
+ /usr/include/openssl/sha.h /usr/include/openssl/x509err.h \
+ /usr/include/openssl/x509_vfy.h /usr/include/openssl/lhash.h \
+ /usr/include/openssl/pkcs7.h /usr/include/openssl/pkcs7err.h \
+ /usr/include/openssl/http.h /usr/include/openssl/conf.h \
+ /usr/include/openssl/conferr.h /usr/include/openssl/conftypes.h \
+ /usr/include/openssl/pem.h /usr/include/openssl/pemerr.h \
+ /usr/include/openssl/hmac.h /usr/include/openssl/async.h \
+ /usr/include/openssl/asyncerr.h /usr/include/openssl/ct.h \
+ /usr/include/openssl/cterr.h /usr/include/openssl/sslerr.h \
+ /usr/include/openssl/sslerr_legacy.h /usr/include/openssl/prov_ssl.h \
+ /usr/include/openssl/ssl2.h /usr/include/openssl/ssl3.h \
+ /usr/include/openssl/tls1.h /usr/include/openssl/dtls1.h \
+ /usr/include/openssl/srtp.h /usr/include/openssl/quic.h devsql.h \
+ unisql.h geseml.h
diff --git a/lib/geseml.h b/lib/geseml.h
new file mode 100644 (file)
index 0000000..5edb501
--- /dev/null
@@ -0,0 +1,62 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Define all procedure to manage email transport  */
+/*      data.                                           */
+/*                                                     */
+/********************************************************/
+#ifndef        GESEML
+#define GESEML
+
+#include        <stdlib.h>
+#include        <stdio.h>
+#include        <time.h>
+
+#include        "unisql.h"
+
+//procedure to dump a list of transfert record 
+extern _Bool eml_dump_one_tra(FILE *out,TRATYP *tra);
+
+//procedure to dump a list of transfert record 
+extern _Bool eml_dump_list_tra(FILE *out,TRATYP **tra);
+
+//procedure to fee memory used to duplicate a resp list to tra resp
+extern void eml_duptra_resp(TRATYP *tra,char **resp);
+
+//procedure to duplicate a TRATYP structure
+extern TRATYP *eml_duptra(TRATYP *tra);
+
+//procedure to fee memory used by a TRATYP structure
+//extern TRATYP **eml_freeall_tra(TRATYP **tra);
+
+//procedure to open a specific qfile
+extern TRATYP **eml_scanqfile(TRATYP **list,FILE *qfile);
+
+//procedure to Generate todolist file within queue
+extern void eml_todoqfile(TRATYP **list);
+
+//procedure to clean/remove done file within queue
+extern void eml_doneqfile(TRATYP **list);
+
+//procedure to store an email to the local email storage area
+extern _Bool eml_store_email(TRATYP *tra);
+
+//procedure to add execution delay to an email sending
+extern _Bool eml_add_delay(time_t isnow,TRATYP *tra);
+
+//procedure to fork a process and start the "sender" process
+extern _Bool eml_start_sender(char *todo);
+
+//procedure to sort a transmission list according sessionid
+extern _Bool eml_sort_list(TRATYP **List);
+
+//procedure to build a warning/reply from the transfer record
+extern _Bool eml_do_warning(TRATYP *tra);
+
+//procedure to scan on line from email header
+extern _Bool eml_scan_headerline(SESTYP *session,const char *line);
+
+//procedure to update database with transfer record (TRATYP)
+extern int eml_update_tradb(TRATYP **tra);
+
+#endif
diff --git a/lib/gesspf.c b/lib/gesspf.c
new file mode 100644 (file)
index 0000000..8dd8c3f
--- /dev/null
@@ -0,0 +1,678 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Implement all routine to manage SMTP low level  */
+/*     exchange.                                       */
+/*                                                     */
+/********************************************************/
+#include        <errno.h>
+#include        <malloc.h>
+#include        <resolv.h>
+#include        <stdbool.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <unistd.h>
+
+#include       "subrou.h"
+#include       "unidns.h"
+#include       "gesspf.h"
+
+#define         MXDNS      10           //maximun number of DNS acess
+
+typedef enum    {               //mechanism definition
+        mch_all,                //"all"
+        mch_addr,               //'a'
+        mch_exists,             //"existe"
+        mch_include,            //"include"
+        mch_ip4,                //"ip4"
+        mch_ip6,                //"ip6"
+        mch_mx,                 //"mx"
+        mch_ptr,                //"mx"
+        mch_redirect,           //"redirect"
+        mch_unknown             //unknown mechanism
+        }MCHTYP;
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to display SPF debug information      */
+/*                                                      */
+/********************************************************/
+static void dbgspf(int dbg,char *proc,char *domain,AFNTYP *afnnum,char *seq,SPFENU status)
+
+{
+if (debug>=dbg) {
+  (void) rou_alert(dbg,"Origin=\"%s\"\n"
+                       "\tCurrent SPF Value <%s>\n"
+                       "\tPeer IP=[%s]\n"
+                       "\tdomain=<%s> SPF=<%s>",
+                       proc,spf_spfASCII(status),afnnum->strnumip,domain,seq);
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to parse mechanisme string            */
+/*                                                      */
+/********************************************************/
+static MCHTYP tellmechanism(char *seq)
+
+{
+//MUST be set in the MCHTYP order
+static char *voc[]=
+        {
+        "all",          //allways match
+        "a",            //addresss
+        "exists",       //allways match
+        "include",      //include sub domain
+        "ip4",          //IPV4 address format
+        "ip6",          //IPV5 address format
+        "mx",           //MX definition
+        "ptr",          //PTR definition
+        "redirect",     //PTR definition
+        (char *)0
+        };
+
+MCHTYP mch;
+
+mch=mch_unknown;
+for (int i=0;voc[i]!=(char *)0;i++) {
+  register int taille;
+
+  taille=strlen(voc[i]);
+  if (strncmp(voc[i],seq,taille)!=0)
+    continue;
+  (void) memmove(seq,seq+taille,strlen(seq+taille)+1);
+  mch=(MCHTYP)i;
+  break;
+  }
+return mch;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to get the next SPF component         */
+/*      return a dynamic char pointer with the isolated */
+/*      SPF sequence.                                   */
+/*                                                      */
+/********************************************************/
+static char *getspfseq(char *fullspf)
+
+{
+char *seq;
+int phase;
+_Bool proceed;
+
+seq=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Converting the IP number
+      if (fullspf==(char *)0)
+        phase=999;      //no need to go further
+      break;
+    case 1      :       //skipping blank
+      while ((*fullspf==' ')||(*fullspf=='\t'))
+        fullspf++;      //scanning blank
+      if (strlen(fullspf)==0)
+        phase=999;      //no need to go further
+      break;
+    case 2      :       //we got a sequence
+      seq=strdup(fullspf);
+      break;
+    case 3      :       //sync sequence
+      if (seq!=(char *)0) {     //always
+        char *ptr;
+  
+        if ((ptr=strchr(seq,' '))!=(char *)0)
+          *ptr='\000';
+        if ((ptr=strchr(seq,'\t'))!=(char *)0)
+          *ptr='\000';
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return seq;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check the SPF A value againt the   */
+/*      current afnnum.                                 */
+/*      return true if successful.                      */
+/*                                                      */
+/********************************************************/
+static _Bool checkaddr(char *domain,AFNTYP *afnnum,char *addr)
+
+{
+#define OPEP    "gesspf.c:checkaddr"
+
+_Bool found;
+int cidr;
+char *addrdom;
+int phase;
+_Bool proceed;
+
+found=false;
+cidr=128;
+addrdom=domain;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //getting the list of MX
+      if (addrdom!=(char *)0) {      //always
+        char *ptr;
+
+        if ((ptr=strchr(addrdom,'/'))!=(char *)0) {
+          *ptr='\000';
+          cidr=atoi(ptr+1);
+          }
+        if ((ptr=strchr(addrdom,':'))!=(char *)0) {
+          *ptr='\000';
+          addrdom=ptr+1;
+          }
+        }
+      break;
+    case 1      :       //getting the list of address for the domain
+      found=dns_matchiprec(addrdom,afnnum,cidr);
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return found;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check the SPF MX values againt the */
+/*      current afnnum.                                 */
+/*      return true if successful.                      */
+/*                                                      */
+/********************************************************/
+static _Bool checkmx(char *domain,AFNTYP *afnnum,char *mx)
+
+{
+#define OPEP    "gesspf.c:checkmx"
+
+_Bool found;
+int cidr;
+char *mxdom;
+MXTYP **mxlist;
+struct addrinfo hints;
+int phase;
+_Bool proceed;
+
+found=false;
+cidr=128;
+mxdom=domain;
+mxlist=(MXTYP **)0;
+(void) memset(&hints,'\000',sizeof(hints));
+hints.ai_family=PF_UNSPEC;
+hints.ai_socktype=SOCK_STREAM;
+hints.ai_flags=HINTFLG;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //getting the list of MX
+      if (mx!=(char *)0) {      //always
+        char *ptr;
+
+        if ((ptr=strchr(mx,'/'))!=(char *)0) {
+          *ptr='\000';
+          cidr=atoi(ptr+1);
+          }
+        if ((ptr=strchr(mx,':'))!=(char *)0) {
+          *ptr='\000';
+          mxdom=ptr+1;
+          }
+        }
+      break;
+    case 1      :       //getting the list of MX for mxdomain
+      if ((mxlist=dns_getmx(mxdom))==(MXTYP **)0) {
+        (void) rou_alert(0,"%s empty MX list for domain <%s> (remote DNS config?)",
+                            OPEP,mxdom);
+        phase=999;      //trouble no need to go further
+        }
+      break;
+    case 2      :       //getting the list of MX for mxdomain
+      for (int i=0;(found==false)&&(mxlist[i]!=(MXTYP *)0);i++) {
+        int status;
+        struct addrinfo *res;
+        struct addrinfo *rp;
+
+        status=getaddrinfo(mxlist[i]->mxname,"",&hints,&res);
+        if (status!=0) {
+          (void) rou_alert(0,"%s Unable to find addrinfo for <%s> (error=<%s>)",
+                             OPEP,mxlist[i]->mxname,gai_strerror(status));
+          continue;
+          }
+        rp=res;
+        for (int j=0;rp!=(struct addrinfo *)0;rp=rp->ai_next,j++) {
+          AFNTYP *addrnum;
+
+          if ((addrnum=afn_getaddrinfo(rp))==(AFNTYP *)0) {
+            (void) rou_alert(0,"%s Found unknown inet family for <%s> IP number",
+                             OPEP,mxlist[i]->mxname);
+            continue;
+            }
+          switch (afn_cmpipnum(addrnum,afnnum,cidr)) {
+            case false  :       //not found
+              break;
+            case true   :       //found
+              found=true; 
+              break;
+            case -1     :       //trouble?
+              (void) rou_alert(0,"%s Unable to compare MX IP for "
+                                 "host <%s> (error=<%s>)",
+                                 OPEP,mxlist[i]->mxname,strerror(errno));
+              break;
+            }
+          addrnum=afn_freeipnum(addrnum);
+          if (found==true)
+            break;              //no need to loop further
+          }
+        (void) freeaddrinfo(res);
+        }
+      mxlist=dns_freemxlist(mxlist);
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return found;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check ip peerip is within an       */
+/*      spf sequence                                    */
+/*                                                      */
+/********************************************************/
+static _Bool checkip(char *domain,AFNTYP *afnnum,char *seq)
+
+{
+#define OPEP    "gesspf.c:checkip"
+_Bool found;
+int cidr;
+AFNTYP **target;
+int phase;
+_Bool proceed;
+
+found=false;
+cidr=128;
+target=(AFNTYP **)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Converting the IP number
+      if (seq==(char *)0) {
+        (void) rou_alert(2,"%s Sequence missing from domain <%s> "
+                           "(Wrong SPF sequence wrong)",OPEP,domain);
+        phase=999;
+        }
+      break;
+    case 1      :       //calculating the local cidr
+      if (seq!=(char *)0) {     //always
+        char *ptr;
+
+        if ((ptr=strchr(seq,'/'))!=(char *)0) {
+          *ptr='\000'; 
+          cidr=atoi(ptr+1);
+          }
+        }
+      break;
+    case 2      :       //computing the target ipnumber
+      if ((target=afn_getipnums(seq))==(AFNTYP **)0) {
+        (void) rou_alert(0,"%s Unable to convert <%s> from "
+                           "domain <%s> (SPF bug?)",
+                           OPEP,seq,domain);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 3      :       //calculating the local cidr
+      for (int i=0;(target[i]!=(AFNTYP*)0)&&(found==false);i++) {
+        switch (afn_cmpipnum(afnnum,target[i],cidr)) {
+          case -1 :
+            (void) rou_alert(0,"%s Unable to compare IP [%s] from SPF "
+                               "domain <%s> (errno=<%s>) (SPF?)",
+                               OPEP,seq,domain);
+            break;
+          case 1  :
+            found=true;
+            break;
+          case 0  :
+            break;
+          }
+        }
+      target=(AFNTYP **)rou_freelist((void **)target,(genfree_t)afn_freeipnum);
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return found;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return an spf status according     */
+/*      sequence contents                               */
+/*                                                      */
+/********************************************************/
+static SPFENU checkseq(int *try,char *domain,char *seq,AFNTYP *afnnum,SPFENU spf)
+
+{
+#define OPEP    "gesspf.c:checkseq"
+SPFENU locspf;
+int phase;
+_Bool proceed;
+
+spf=spf_neutral;
+locspf=spf_pass;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //is the sequence an SPF Mechanisms
+      if (strchr("?~-+",seq[0])!=(char *)0) {
+        switch (seq[0]) {
+          case '?'      :
+            locspf=spf_neutral;
+            break;
+          case '~'      :
+            locspf=spf_softfail;
+            break;
+          case '+'      :
+            locspf=spf_pass;
+            break;
+          case '-'      :
+            locspf=spf_fail;
+            break;
+          }
+        seq++;
+        }
+      break;
+    case 1      :       //check directive
+      switch (tellmechanism(seq)) {
+        case mch_all            :       //usually last in sequence
+          spf=locspf;
+          break;
+        case mch_addr            :       //check IP addr
+          if (checkaddr(domain,afnnum,seq)==true)
+            spf=locspf;
+          break;
+        case mch_mx             :       //This is a MX refrence
+          if (checkmx(domain,afnnum,seq)==true)
+            spf=locspf;
+          break;
+        case mch_ip4            :       //This is IPV4 number
+        case mch_ip6            :       //This is IPV6 number
+          if (checkip(domain,afnnum,seq+1)==true)
+            spf=locspf;
+          (void) dbgspf(5,"checkip",domain,afnnum,seq+1,spf);
+          break;
+        case mch_include        :       //include sub domain
+          if (seq[0]==':') {
+            (*try)++;
+            spf=spf_getstatus(try,seq+1,afnnum);
+            }
+          else {
+            (void) rou_alert(0,"%s wrong SPF include seq <%s> (SPF format?)",
+                               OPEP,seq);
+            spf=spf_permerr;
+            }
+          break;
+        default                 :       //trouble
+          break;
+        case mch_unknown        :       //trouble
+          (void) rou_alert(0,"%s unknown mechanism <%s> (bug?, config?)",
+                              OPEP,seq);
+          spf=spf_permerr;
+          break;
+        } 
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return spf;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return check if an IP is part of   */
+/*      spf allowed IP                                  */
+/*                                                      */
+/********************************************************/
+static SPFENU is_peerip_ok(int *try,char *domain,AFNTYP *afnnum,char *spfrec)
+
+{
+#define OPEP    "gesspf.c:is_peerip_ok"
+SPFENU spf;
+int phase;
+_Bool proceed;
+
+spf=spf_neutral;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Converting the IP number
+      if (spfrec==(char *)0) {
+        (void) rou_alert(0,"%s SPF record is NULL (bug?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //making sure spfrec is an SPF1
+      if (strlen(spfrec)>0) {
+        char *seq;
+        char *ref;
+
+        ref=spfrec;
+        while ((seq=getspfseq(spfrec))!=(char *)0) {
+          spfrec=strstr(spfrec,seq);
+          spfrec+=strlen(seq);
+          spf=checkseq(try,domain,seq,afnnum,spf);
+          seq=rou_freestr(seq);
+          if (spf!=spf_neutral)
+            break;      //Found a pass status,  breaking loop
+          }
+        (void) dbgspf(5,"is_peerip_ok",domain,afnnum,ref,spf);
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return spf;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return the SPF status string value */
+/*                                                      */
+/********************************************************/
+PUBLIC const char *spf_spfASCII(SPFENU spf)
+
+{
+#define OPEP    "gesspf.c:dns_spfASCII"
+static char *spfascii[]=
+        {
+        "spf_pass",
+        "spf_fail",
+        "spf_softfail",
+        "spf_neutral",
+        "spf_timeout",
+        "spf_missing",
+        "spf_permerr",
+        "spf_unknown"
+        };
+
+const char *ascii;
+
+ascii="Unset (Bug?)";
+switch  (spf) {
+  case spf_pass         :
+  case spf_fail         :
+  case spf_softfail     :
+  case spf_neutral      :
+  case spf_timeout      :
+  case spf_missing      :
+  case spf_permerr      :
+  case spf_unknown      :
+    ascii=spfascii[spf];
+    break;
+  default       :
+    (void) rou_alert(0,"%s, Unexpected '%d' SPF status (Bug!)",OPEP,spf);
+    break;
+  }
+return ascii;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to get the SPF status according a     */
+/*      domain name and and an IP.                      */
+/*                                                      */
+/********************************************************/
+PUBLIC SPFENU spf_getstatus(int *try,char *domain,AFNTYP *afnnum)
+
+{
+#define OPEP    "gesspf.c:spf_getstatus"
+
+SPFENU spf;
+char *list;
+int phase;
+_Bool proceed;
+
+(*try)++;
+spf=spf_permerr;
+list=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  (void) rou_alert(8,"JMPDBG %s, phase='%d', domain=<%s>",OPEP,phase,domain);
+  switch (phase) {
+    case 0      :       //Are the parameters available
+      if ((domain==(char *)0)||(afnnum==(AFNTYP *)0)) {
+        (void) rou_alert(0,"%s missing one or both needed argument",OPEP);
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 1      :       //check the number of DNS access
+      if ((*try)>MXDNS) {
+        (void) rou_alert(0,"%s SPF record scan is too deep (aborting)",OPEP);
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 2      :       //get the spf LIST related to SPF
+      if ((list=dns_getspf(domain))==(char *)0) 
+        phase=999;      //trouble trouble
+      break;
+    case 3      :       //get the spf LIST related to SPF
+      spf=is_peerip_ok(try,domain,afnnum,list);
+      list=rou_freestr(list);
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return spf;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to get the SPF domain/peerip status   */
+/*      return the SPF status.                          */
+/*                                                      */
+/********************************************************/
+PUBLIC SPFENU spf_getspf(char *mailfrom,char *peerip)
+
+{
+#define OPEP    "gesspf.c:spf_getspf"
+
+SPFENU spf;
+char *domain;
+
+spf=spf_unknown;
+domain=strchr(mailfrom,'@');
+if (domain!=(char *)0)
+  domain++;
+if (domain!=(char *)0) {
+  AFNTYP **afns;
+
+  afns=afn_getipnums(peerip);
+  if (afns!=(AFNTYP **)0) {
+    int try;
+
+    try=0;
+    for (int i=0;afns[i]!=(AFNTYP *)0;i++) {
+      spf=spf_getstatus(&try,domain,afns[i]);
+      (void) rou_dbglive(9,OPEP,"SPF[%d] for [%s]=<%s>",
+                           i,afns[i]->strnumip,spf_spfASCII(spf));
+      }
+    afns=(AFNTYP **)rou_freelist((void **)afns,(genfree_t)afn_freeipnum);
+    }
+  }
+return spf;
+
+#undef  OPEP
+}
diff --git a/lib/gesspf.d b/lib/gesspf.d
new file mode 100644 (file)
index 0000000..fa18e8a
--- /dev/null
@@ -0,0 +1,83 @@
+gesspf.o gesspf.d : gesspf.c /usr/include/stdc-predef.h /usr/include/errno.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/malloc.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/__fpos_t.h \
+ /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/resolv.h \
+ /usr/include/sys/param.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/endian.h /usr/include/bits/endianness.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/signal.h /usr/include/bits/signum-generic.h \
+ /usr/include/bits/signum-arch.h /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/types/stack_t.h \
+ /usr/include/sys/ucontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigstksz.h /usr/include/bits/ss_flags.h \
+ /usr/include/bits/types/struct_sigstack.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/bits/param.h \
+ /usr/include/linux/param.h /usr/include/asm/param.h \
+ /usr/include/asm-generic/param.h /usr/include/netinet/in.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h /usr/include/bits/socket.h \
+ /usr/include/bits/socket_type.h /usr/include/bits/sockaddr.h \
+ /usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h /usr/include/asm/bitsperlong.h \
+ /usr/include/asm-generic/bitsperlong.h /usr/include/asm/sockios.h \
+ /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/arpa/nameser.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-least.h /usr/include/arpa/nameser_compat.h \
+ /usr/include/bits/types/res_state.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/stdlib.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h unidns.h subafn.h \
+ /usr/include/netdb.h /usr/include/rpc/netdb.h /usr/include/bits/netdb.h \
+ gesspf.h
diff --git a/lib/gesspf.h b/lib/gesspf.h
new file mode 100644 (file)
index 0000000..5d5f89a
--- /dev/null
@@ -0,0 +1,34 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Define all procedure to manage SPF entry within */
+/*      DNS record.                                     */
+/*                                                     */
+/********************************************************/
+#ifndef        GESSPF
+#define GESSPF
+
+#include        "subafn.h"
+#include        "gesspf.h"
+
+typedef enum    {
+       spf_pass,       //OK if condition apply
+       spf_fail,       //NOK if condition apply
+       spf_softfail,   //Msg origin is dubious
+       spf_neutral,    //same status as no SPF
+       spf_timeout,    //no answer within time
+       spf_missing,    //SPF not found
+        spf_permerr,    //SPF permanent error
+       spf_unknown     //Unknown SPF directive
+       }SPFENU;
+
+//procedure to return spf status as an ASCII string
+extern const char *spf_spfASCII(SPFENU spf);
+
+//get the SPF status for a specific domain and an IP
+extern SPFENU spf_getstatus(int *try,char *domain,AFNTYP *afnnum);
+
+//get the SPF status for a specific domain according remote peer ipnumer
+extern SPFENU spf_getspf(char *domain,char *peerip);
+
+#endif
diff --git a/lib/gessql.c b/lib/gessql.c
new file mode 100644 (file)
index 0000000..d1739ac
--- /dev/null
@@ -0,0 +1,966 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Module to manage complexe SQL request           */
+/*                                                     */
+/********************************************************/
+#include        <malloc.h>
+#include        <stdlib.h>
+#include        <string.h>
+
+#include        "subrou.h"
+#include        "unisql.h"
+#include        "gessql.h"
+
+//DATABASE Table names
+#define EMLTBL  "emails"        //Table about user list
+#define RMTTBL  "remotes"       //Table about remotes site
+#define ACTTBL  "actions"       //action tables
+#define SESTBL  "sessions"      //session tables
+#define EVTTBL  "events"        //events tables 
+
+//field available in table "remotes"
+static const FLDTYP usrfield[]={
+          {1,"remoteip"},
+          {2,"lastscan"},
+          {3,"lastupdate"},
+          {4,"links"},
+          {5,"credit"},
+          {6,"listing"},
+          {7,"reverse"},
+          {0,(char *)0}
+          };
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to update database information about  */
+/*      emails echange current and last status.         */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool sql_mngact(SQLPTR *sqlptr,ACTTYP *act)
+
+{
+#define OPEP    "gessql.c:sql_mngact,"
+
+static const char
+  *del="DELETE FROM "ACTTBL" WHERE sessid=%s AND (rcptto=%s OR rcptto IS NULL)";
+static const char
+  *ins="INSERT INTO "ACTTBL" (%s) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%d,%d,%s)";
+
+_Bool isok;
+
+isok=false;
+if (act!=(ACTTYP *)0) {
+  char strcode[10];
+  char *gid;
+  char *gremoteip;      //remote IP address
+  char *greverse;       //remote reverse name
+  char *gsfrom;         //SMTP "mail from:"
+  char *ghfrom;         //email Header "From:"
+  char *ghsubject;      //email Header "Subject:"
+  char *grcpt;
+  char *gcode;
+
+  (void) memset(strcode,'\000',sizeof(strcode));
+  strcode[0]=act->code;
+  gid=sql_gooddata(sqlptr,act->sessid);
+  gremoteip=sql_gooddata(sqlptr,act->remoteip);
+  greverse=sql_gooddata(sqlptr,act->reverse);
+  gsfrom=sql_gooddata(sqlptr,act->sfrom);
+  ghsubject=sql_gooddata(sqlptr,act->hsubject);
+  ghfrom=sql_gooddata(sqlptr,act->hfrom);
+  grcpt=sql_gooddata(sqlptr,act->rcptto);
+  gcode=sql_gooddata(sqlptr,strcode);
+  if (act->resp!=(char **)0) {
+    char *field;
+
+    field="code,sessid,remoteip,reverse,smtpfrom,emailfrom,subject,rcptto,numline,status,info";
+    (void) sql_request(sqlptr,del,gid,grcpt);
+    if (act->resp!=(char **)0) {
+      char **resp;
+      int num;
+  
+      resp=act->resp;
+      num=1;
+      while (*resp!=(char *)0) {
+        if ((*resp)[3]==' ') {
+          char strint[5];
+          int status;
+          char *ginfo;
+
+
+          (void) memset(strint,'\000',sizeof(strint));
+          (void) strncpy(strint,*resp,3);
+          status=atoi(strint);
+          ginfo=sql_gooddata(sqlptr,(*resp)+3);
+          (void) sql_request(sqlptr,ins,field,
+                                        gcode,gid,
+                                        gremoteip,greverse,
+                                        gsfrom,ghfrom,ghsubject,
+                                        grcpt,num,status,ginfo);
+          ginfo=rou_freestr(ginfo);
+          }
+        resp++;
+        num++;
+        }
+      }
+    }
+  gcode=rou_freestr(gcode);
+  grcpt=rou_freestr(grcpt);
+  ghsubject=rou_freestr(ghsubject);
+  ghfrom=rou_freestr(ghfrom);
+  gsfrom=rou_freestr(gsfrom);
+  greverse=rou_freestr(greverse);
+  gid=rou_freestr(gid);
+  isok=true;
+  }
+return isok;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to set all remotes links value to zero*/
+/*                                                      */
+/********************************************************/
+PUBLIC void sql_droplinks(SQLPTR *sqlptr)
+
+{
+(void) sql_request(sqlptr,"update "RMTTBL" set links=0");
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to update the database with respond   */
+/*      Record associated with TRA.                     */
+/*      Return the number of record stored within the   */
+/*      database.                                       */
+/*                                                      */
+/********************************************************/
+PUBLIC int sql_update_tradb(SQLPTR *sqlptr,TRATYP **tralist)
+
+{
+#define OPEP    "gesql.c:qlupdate_tradb,"
+
+int num;
+
+num=0;
+if (tralist!=(TRATYP **)0) {
+  while (*tralist!=(TRATYP *)0) {
+    if ((*tralist)->resp!=(char **)0) {
+      ACTTYP action;
+
+      (void) memset(&action,'\000',sizeof(ACTTYP));
+      action.sessid=(*tralist)->sessid;
+      action.code=(*tralist)->code;
+      action.remoteip=(*tralist)->remoteip;
+      action.reverse=(*tralist)->reverse;
+      action.sfrom=(*tralist)->sfrom;
+      action.hfrom=(*tralist)->hfrom;
+      action.hsubject=(*tralist)->hsubject;
+      action.rcptto=(*tralist)->rcptto;
+      action.resp=(*tralist)->resp;
+      (void) sql_mngact(sqlptr,&action); 
+      num++;
+      }
+    tralist++;
+    }
+  }
+return num;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to select a user from emails tables   */
+/*      and return contents;                            */
+/*                                                      */
+/********************************************************/
+static _Bool select_user(SQLPTR *sqlptr,char *email,USRTYP **usr)
+
+{
+#define OPEP    "gessql.c:select_user,"
+
+static const char *sel="SELECT * FROM "EMLTBL" WHERE email=%s";
+
+//field available in table "emails"
+static const FLDTYP usrfield[]={
+          {1,"email"},
+          {2,"password"},
+          {3,"hash"},
+          {4,"space"},
+          {5,"mxspace"},
+          {6,"locked"},
+          {0,(char *)0}
+          };
+
+_Bool isok;
+USRTYP *locusr;
+SQLRES *rs;
+register int phase;
+register _Bool proceed;
+
+isok=false;
+locusr=(USRTYP *)0;
+rs=(SQLRES *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //checking parameters
+      if ((usr==(USRTYP **)0)||(email==(char *)0)||(strlen(email)==0)) {
+        (void) rou_alert(0,"%s usr=<%p> or email=<%s> sing (Bug?)",OPEP,usr,email);
+        phase=999;
+        }
+      break;
+    case 1      :       //Selecting user
+      if ((rs=sql_gettupple(sqlptr,sel,email))==(SQLRES *)0) {
+        (void) rou_alert(0,"%s Unable to get data for user <%s> (Database?)",
+                            OPEP,email);
+        phase=999;      //user not found within database 
+        }
+      break;
+    case 2      :       //do we have ONE user
+      int nbr;
+
+      nbr=sql_getnbrtupple(sqlptr,rs);
+      switch (nbr) {
+        case 0  :       //No user email found
+          phase=999;    //User name not in database
+          break;
+        case 1  :       //we have only one record, found user
+          break;
+        default :      //such case should never ever happen
+          (void) rou_alert(0,"%s got '%d' users record %s (Database corrupted?)",
+                             OPEP,nbr,"while only 1 is expected");
+          phase=999;
+          break;
+        }
+      break;
+    case 3      :       //user data extraction
+      isok=true;
+      locusr=(USRTYP *)calloc(1,sizeof(USRTYP));
+      for (int i=0;(usrfield[i].name!=(char *)0)&&(isok==true);i++) {
+        const char *locval;
+      
+        if ((locval=sql_getvalue(sqlptr,rs,0,usrfield[i].name))==(const char *)0)
+          continue;
+        switch (usrfield[i].num) {
+          case 1        :       //user email
+            locusr->email=strdup(locval);
+            break;
+          case 2        :       //User crypted password
+            locusr->password=strdup(locval);
+            break;
+          case 3        :       //'email:realm:password' MD5
+            locusr->hash=strdup(locval);
+            break;
+          case 4        :       //user used space
+            locusr->space=atoi(locval);
+            break;
+          case 5        :       //user max space available
+            locusr->mxspace=atoi(locval);
+            break;
+          case 6        :       //lock status
+            locusr->lock=atoi(locval);
+            break;
+          default       :
+            (void) rou_alert(0,"%s field <%d:%s> not implemented (Bug?)",
+                                   OPEP,usrfield[i].num,usrfield[i].name);
+            locusr=sql_freeusr(locusr);
+            isok=false;
+            break;
+          }
+        }
+      break;
+    default     :       //SAFE Guard
+      rs=sql_droptupple(sqlptr,rs);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+*usr=locusr;
+return isok;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to delete a remote record from the    */
+/*      table.                                          */
+/*                                                      */
+/********************************************************/
+static _Bool delete_remote(SQLPTR *sqlptr,char *rmtip,SRVTYP *srv)
+
+{
+#define OPEP    "gessql.c:delete_remote,"
+
+static const char *del="DELETE FROM "RMTTBL" WHERE remoteip=%s";
+
+_Bool isok;
+register int phase;
+register _Bool proceed;
+
+isok=false;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //checking parameters
+      if (strlen(rmtip)==0) {
+        (void) rou_alert(0,"%s rmtip=<%s> missing (Bug?)",OPEP,rmtip);
+        phase=999;
+        }
+      break;
+    case 1      :       //Selecting user
+      if (sql_request(sqlptr,del,rmtip)!=1) {
+        (void) rou_alert(0,"%s Unable to delete remote_ip <%s> (Database?)",
+                            OPEP,rmtip);
+        phase=999;      //Trouble trouble
+        }
+      break;
+    case 2      :       //everything fine
+      isok=true;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return isok;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to select a remote server from remotes*/
+/*      table and return contents                       */
+/*                                                      */
+/********************************************************/
+static _Bool select_remote(SQLPTR *sqlptr,char *rmtip,SRVTYP **srv)
+
+{
+#define OPEP    "gessql.c:select_remote,"
+
+static const char *sel="SELECT * FROM "RMTTBL" WHERE remoteip=%s";
+
+_Bool isok;
+SRVTYP *locsrv;
+SQLRES *rs;
+register int phase;
+register _Bool proceed;
+
+isok=false;
+locsrv=(SRVTYP *)0;
+rs=(SQLRES *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //checking parameters
+      if ((srv==(SRVTYP **)0)||(rmtip==(char *)0)||(strlen(rmtip)==0)) {
+        (void) rou_alert(0,"%s srv=<%p> or rmtip=<%s> missing (Bug?)",
+                            OPEP,srv,rmtip);
+        phase=999;
+        }
+      break;
+    case 1      :       //Selecting user
+      if ((rs=sql_gettupple(sqlptr,sel,rmtip))==(SQLRES *)0) {
+        (void) rou_alert(0,"%s Unable to get data for user <%s> (Database?)",
+                            OPEP,rmtip);
+        phase=999;      //user not found within database 
+        }
+      break;
+    case 2      :       //do we have ONE user
+      int nbr;
+
+      nbr=sql_getnbrtupple(sqlptr,rs);
+      switch (nbr) {
+        case 1  :       //we have only one record, found remote
+          break;
+        case 0  :      //no record found!
+        default :      //such case should never ever happen
+          (void) rou_alert(0,"%s got '%d' users record %s (Database corrupted?)",
+                             OPEP,nbr,"while only 1 is expected");
+          phase=999;
+          break;
+        }
+      break;
+    case 3      :       //user data extraction
+      isok=true;
+      locsrv=(SRVTYP *)calloc(1,sizeof(SRVTYP));
+      for (int i=0;(usrfield[i].name!=(char *)0)&&(isok==true);i++) {
+        const char *locval;
+      
+        if ((locval=sql_getvalue(sqlptr,rs,0,usrfield[i].name))==(const char *)0)
+          continue;
+        switch (usrfield[i].num) {
+          case 1        :       //user remoteip
+            locsrv->rmtip=strdup(locval);
+            break;
+          case 2        :       //lastscan
+            locsrv->lastscan=sql_tounixtime(sqlptr,locval);
+            break;
+          case 3        :       //last update
+            locsrv->update=sql_tounixtime(sqlptr,locval);
+            break;
+          case 4        :       //Number of links
+            locsrv->links=atoi(locval);
+            break;
+          case 5        :       //remote server credibility
+            locsrv->credit=atoi(locval);
+            break;
+          case 6        :       //scanning status
+            locsrv->listing=strdup(locval);
+            break;
+          case 7        :       //scanning status
+            locsrv->reverse=strdup(locval);
+            break;
+          default       :
+            (void) rou_alert(0,"%s field <%d:%s> not implemented (Bug?)",
+                                   OPEP,usrfield[i].num,usrfield[i].name);
+            locsrv=sql_freesrv(locsrv);
+            isok=false;
+            break;
+          }
+        }
+      break;
+    default     :       //SAFE Guard
+      rs=sql_droptupple(sqlptr,rs);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+*srv=locsrv;
+return isok;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to select a remote server from remotes*/
+/*      table and update contents                       */
+/*                                                      */
+/********************************************************/
+static _Bool update_remote(SQLPTR *sqlptr,char *rmtip,SRVTYP *srv)
+
+{
+#define OPEP    "gessql.c:update_remote,"
+
+static const char *upd= "UPDATE "RMTTBL" SET "
+                        "lastscan='%s',lastupdate='%s',"
+                        "credit=%d,listing=%s,reverse=%s "
+                        "WHERE remoteip='%s'";
+
+_Bool isok;
+char *lastscan;
+char *update;
+char *listing;
+char *reverse;
+
+isok=true;
+lastscan=sql_fromunixtime(sqlptr,srv->lastscan);
+update=sql_fromunixtime(sqlptr,srv->update);
+listing=sql_gooddata(sqlptr,srv->listing);
+reverse=sql_gooddata(sqlptr,srv->reverse);
+if (sql_request(sqlptr,upd,lastscan,update,srv->credit,listing,reverse,srv->rmtip)!=1) {
+  (void) rou_alert(0,"%s Unable to update remote_ip <%s> (Database?)",
+                    OPEP,srv->rmtip);
+  isok=false;
+  }
+reverse=rou_freestr(reverse);
+listing=rou_freestr(listing);
+update=rou_freestr(update);
+lastscan=rou_freestr(lastscan);
+return isok;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to update a session contents          */
+/*                                                      */
+/********************************************************/
+static _Bool update_ses(SQLPTR *sqlptr,char *seskey,SESTYP **ses)
+
+{
+#define OPEP    "devseql.c:update_ses,"
+
+static const char *upd="UPDATE "SESTBL" set %s WHERE sessid=%s";
+
+//field available in table "sessions"
+static const FLDTYP sesfield[]={
+          {0,"sessid"},
+          {1,"sesstitle"},
+          {2,"sessfrom"},
+          {3,"emailfrom"},
+          {4,"taille"},
+          {5,"duration"},
+          {6,(char *)0}
+          };
+
+_Bool isok;
+char *cmtset;
+
+isok=true;
+cmtset=strdup("");
+for (int i=0;(isok==true)&&(sesfield[i].name!=(char *)0);i++) {
+  char *data;
+  char items[50];
+
+  data=(char *)0;
+  (void) strcpy(items,"");
+  switch (sesfield[i].num) {
+    case 0              :       //session ID
+      break;                    //nothing to do
+    case 1              :       //title
+      data=sql_gooddata(sqlptr,(*ses)->hsubject);
+      break;                    //nothing to do
+    case 2              :       //sfrom
+      data=sql_gooddata(sqlptr,(*ses)->sfrom);
+      break;                    //nothing to do
+    case 3              :       //efrom
+      data=sql_gooddata(sqlptr,(*ses)->hfrom);
+      break;                    
+    case 4              :       //taille
+      (void) rou_asprintf(&data,"%lu",(*ses)->taille);
+      break;                    
+    case 5              :       //duration
+      data=sql_gooddata(sqlptr,(*ses)->duration);
+      break;                    
+    default             :
+      isok=false;
+      (void) rou_alert(0,"%s Unexpected field <%s> (Bug?)",OPEP,sesfield[i]);
+      break;
+    }
+  if (data!=(char *)0) {
+    char *newset;
+    char *sep;
+    newset=(char *)0; 
+    sep="";
+    if (strlen(cmtset)>0)
+      sep=",";
+    (void) rou_asprintf(&newset,"%s%s%s=%s",cmtset,sep,sesfield[i].name,data);
+    cmtset=rou_freestr(cmtset);
+    cmtset=newset;
+    data=rou_freestr(data);
+    }
+  }
+if (isok==true)
+  isok=(sql_request(sqlptr,upd,cmtset,seskey)==1);
+cmtset=rou_freestr(cmtset);
+return isok;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to retreive information about user    */
+/*      known within database.                          */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool sql_mngusr(SQLPTR *sqlptr,SQLENUM action,char *key,USRTYP **usr)
+
+{
+#define OPEP    "gessql.c:sql_mngusr,"
+
+_Bool isok;
+char *gooddata;
+int phase;
+_Bool proceed;
+
+isok=false;
+gooddata=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //checking SQL and user record
+      if (sqlptr==(SQLPTR *)0) {
+        (void) rou_alert(0,"%s SQL pointer is NULL (Bug?)",OPEP);
+        phase=999;
+        }
+      if (usr==(USRTYP **)0) {
+        (void) rou_alert(0,"%s USER pointer is NULL (Bug?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //do we have a good key
+      if ((gooddata=sql_gooddata(sqlptr,key))==(char *)0) {
+        (void) rou_alert(0,"%s %s table, key <%s> is empty (Bug?!)",
+                            OPEP,EMLTBL,key);
+        phase=999;
+        }
+      break;
+    case 2      :       //getting user information
+      switch (action) {
+        case sql_select :
+          isok=select_user(sqlptr,gooddata,usr);
+          break;
+        default :
+          (void) rou_alert(0,"%s action='%d' not yet implemented!",OPEP,action);
+        }
+      gooddata=rou_freestr(gooddata);
+      break;
+    default     :
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return isok;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to manage  information about email    */
+/*      exchange session.                               */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool sql_mngses(SQLPTR *sqlptr,SQLENUM action,SESTYP **ses)
+
+{
+#define OPEP    "gessql.c:sql_mngses,"
+
+static const char *ins="INSERT INTO "SESTBL" (sessid) VALUES(%s)";
+
+_Bool isok;
+char *gooddata;
+int phase;
+_Bool proceed;
+
+isok=false;
+gooddata=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //checking SQL
+      if ((sqlptr==(SQLPTR *)0)||(ses==(SESTYP **)0)||(*ses==(SESTYP *)0)) {
+        (void) rou_alert(0,"%s SQL pointer is NUll (Bug?)",OPEP);
+        phase=999;
+        }
+      if ((gooddata=sql_gooddata(sqlptr,(*ses)->sessid))==(char *)0) {
+        (void) rou_alert(0,"%s %s table, key <%s> is empty (Bug?!)",
+                            OPEP,SESTBL,(*ses)->sessid);
+        phase=999;
+        }
+      break;
+    case 1      :       //getting user information
+      switch (action) {
+        case sql_insert :
+          isok=(sql_request(sqlptr,ins,gooddata)==1);
+          break;
+        case sql_update :
+          isok=update_ses(sqlptr,gooddata,ses);
+          break;
+        default :
+          (void) rou_alert(0,"%s action='%d' not yet implemented! (BUG?)",
+                              OPEP,action);
+        }
+      gooddata=rou_freestr(gooddata);
+      break;
+    default     :
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return isok;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to increase (of decrease the remote   */
+/*      IP connection number.                           */
+/*                                                      */
+/********************************************************/
+PUBLIC int sql_newconnect(SQLPTR *sqlptr,char *rmtip,int delta)
+
+{
+#define OPEP    "gessql.c:sql_newconnect,"
+
+static const char *sel="SELECT * FROM %s WHERE remoteip='%s' FOR UPDATE";
+static const char *upd="UPDATE %s SET links=links+%d,lastupdate=now() WHERE remoteip='%s'";
+static const char *ins="INSERT INTO %s (remoteip) values('%s')";
+
+int connect;
+SQLRES *sqlres;
+SRVTYP *srv;
+int phase;
+_Bool proceed;
+
+connect=-1;
+srv=(SRVTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d' connect=%d",OPEP,phase,connect);
+  switch (phase) {
+    case 0      :       //lets lock table access
+      if (sql_lock(sqlptr,RMTTBL)==false) {
+        (void) rou_alert(0,"%s Unable to lock %s table",OPEP,RMTTBL);
+        phase=999;      //Unable to lock table!!!!
+        }
+      break;
+    case 1      :       //updating remotes record with links
+      if ((sqlres=sql_gettupple(sqlptr,sel,RMTTBL,rmtip))==(SQLRES *)0) {
+        (void) rou_alert(0,"%s Unable to get tupple for remote ip <%s> "
+                           "in table <%s> (DB trouble?)",
+                            OPEP,rmtip,RMTTBL);
+        phase=999;      //no tupple information?
+        }
+      break; 
+    case 2      :       //is record already existing
+      if (sql_getnbrtupple(sqlptr,sqlres)==1) {
+        phase++;        //No need to create it
+        }
+      sqlres=sql_droptupple(sqlptr,sqlres);
+      break;
+    case 3      :       //Let create a new unique record
+      if (sql_request(sqlptr,ins,RMTTBL,rmtip)!=1) {
+        (void) rou_alert(0,"%s Unable to create entry "
+                           "for remote ip <%s> in table <%s> (DB trouble?)",
+                            OPEP,rmtip,RMTTBL);
+        (void) sql_unlock(sqlptr,true);
+        phase=999;
+        }
+      else
+        phase++;        //record open
+      break; 
+    case 4      :       //lets update link
+      if (sql_request(sqlptr,upd,RMTTBL,delta,rmtip)!=1) {
+        (void) rou_alert(0,"%s Unable to update entry "
+                           "for remote ip <%s> in table <%s> (DB trouble?)",
+                            OPEP,rmtip,RMTTBL);
+        }
+      break; 
+    case 5      :       //lets find the the number of connection
+      if (sql_mngremote(sqlptr,sql_select,rmtip,&srv)==false) {
+        (void) rou_alert(0,"%s Unable to get record "
+                           "for remote ip <%s> in table <%s> (DB trouble?)",
+                            OPEP,rmtip,RMTTBL);
+        }
+      else {
+        connect=srv->links;
+        srv=sql_freesrv(srv);
+        }
+      break; 
+    case 6      :       //unlock database
+      (void) sql_unlock(sqlptr,true);
+      break; 
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return connect;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to retreive information about remote  */
+/*      server and get how reliable/trustable it is.    */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool sql_mngremote(SQLPTR *sqlptr,SQLENUM action,char *key,SRVTYP **srv)
+
+{
+#define OPEP    "gessql.c:sql_mnremote,"
+
+_Bool isok;
+char *gooddata;
+int phase;
+_Bool proceed;
+
+isok=false;
+gooddata=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //checking SQL and user record
+      if (sqlptr==(SQLPTR *)0) {
+        (void) rou_alert(0,"%s SQL pointer is NULL (Bug?)",OPEP);
+        phase=999;
+        }
+      if (srv==(SRVTYP **)0) {
+        (void) rou_alert(0,"%s USER pointer is NULL (Bug?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //do we have a good key
+      if ((gooddata=sql_gooddata(sqlptr,key))==(char *)0) {
+        (void) rou_alert(0,"%s %s table, key <%s> is empty (Bug?!)",
+                            OPEP,EMLTBL,key);
+        phase=999;
+        }
+      break;
+    case 2      :       //getting user information
+      switch (action) {
+        case sql_select :
+          isok=select_remote(sqlptr,gooddata,srv);
+          break;
+        case sql_update :
+          isok=update_remote(sqlptr,gooddata,*srv);
+          break;
+        case sql_delete :
+          isok=delete_remote(sqlptr,gooddata,*srv);
+          break;
+        default :
+          (void) rou_alert(0,"%s action='%d' not yet implemented!",OPEP,action);
+          break;
+        }
+      gooddata=rou_freestr(gooddata);
+      break;
+    default     :
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return isok;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to retreive a list of newly connected */
+/*      remote IP to be scanned to establish credibilty.*/
+/*                                                      */
+/********************************************************/
+PUBLIC char **sql_getnewrmtip(SQLPTR *sqlptr,int delay)
+
+{
+#define OPEP    "gessql.c:sql_getnewrmtip,"
+
+static char *cmd="select remoteip from "RMTTBL" WHERE "
+                 "(lastscan<lastupdate) and (lastscan<%s) "
+                 "order by lastscan asc limit 10";
+
+char **rmtip;
+SQLRES *rs;
+int num;
+char *again;
+int phase;
+_Bool proceed;
+
+rmtip=(char **)0;
+rs=(SQLRES *)0;
+num=0;
+again=sql_caldate(sqlptr,"NOW()",delay);
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //do we have some remote IP
+      if ((rs=sql_gettupple(sqlptr,cmd,again))==(SQLRES *)0) {
+        (void) rou_alert(0,"%s unable to get remoteip list "
+                           "(Database BUG?)",OPEP);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 1      :       //how many entry do we have
+      if ((num=sql_getnbrtupple(sqlptr,rs))<=0) {
+        phase++;        //no need to extract list
+        }
+      break;
+    case 2      :       //extracting list
+      for (int i=0;i<num;i++) {
+        const char *ip;
+
+        ip=sql_getvalue(sqlptr,rs,i,"remoteip");
+        if (ip!=(const char *)0)
+          rmtip=(char **)rou_addlist((void **)rmtip,
+                                     (void *)strdup(ip));
+        }
+      break;
+    case 3      :       //clearing result
+      rs=sql_droptupple(sqlptr,rs);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+again=rou_freestr(again);
+return rmtip;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to add an event reference within the  */
+/*      data-base event table.                          */
+/*                                                      */
+/********************************************************/
+PUBLIC int sql_addevent(SQLPTR *sqlptr,char *session,int debut,int fin)
+
+{
+static char *ins="INSERT INTO "EVTTBL" (sessid,start,stop) values(%s)";
+
+int done;
+
+done=-1;
+if (sqlptr!=(SQLPTR *)0) {
+  char *goodses;
+  char *values;
+
+  goodses=sql_gooddata(sqlptr,session);
+  (void) rou_asprintf(&values,"%s,%d,%d",goodses,debut,fin);
+  done=sql_request(sqlptr,ins,values);
+  values=rou_freestr(values);
+  goodses=rou_freestr(goodses);
+  }
+return done;
+}
diff --git a/lib/gessql.d b/lib/gessql.d
new file mode 100644 (file)
index 0000000..35cf9fa
--- /dev/null
@@ -0,0 +1,51 @@
+gessql.o gessql.d : gessql.c /usr/include/stdc-predef.h /usr/include/malloc.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/__fpos_t.h \
+ /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/stdlib.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h subrou.h /usr/include/linux/types.h \
+ /usr/include/asm/types.h /usr/include/asm-generic/types.h \
+ /usr/include/asm-generic/int-ll64.h /usr/include/asm/bitsperlong.h \
+ /usr/include/asm-generic/bitsperlong.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h unisql.h gessql.h unieml.h \
+ subafn.h /usr/include/netdb.h /usr/include/netinet/in.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h /usr/include/bits/socket.h \
+ /usr/include/bits/socket_type.h /usr/include/bits/sockaddr.h \
+ /usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h devsql.h
diff --git a/lib/gessql.h b/lib/gessql.h
new file mode 100644 (file)
index 0000000..16ee261
--- /dev/null
@@ -0,0 +1,42 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/************************************************/
+/*                                             */
+/*     gestion level fto manage sql request    */
+/*                                             */
+/************************************************/
+#ifndef        GESSQL
+#define GESSQL
+
+#include        <stdbool.h>
+
+#include        "unieml.h"
+#include        "devsql.h"
+
+//procedure to update actions table within database
+extern _Bool sql_mngact(SQLPTR *sqlptr,ACTTYP *act);
+
+//procedure to remove ALL link counts within the remotes tables
+extern void sql_droplinks(SQLPTR *sqlptr);
+
+//procedure to update transaction information
+extern int sql_update_tradb(SQLPTR *sqlptr,TRATYP **tralist);
+
+//procedure to manage information on exiting user
+extern _Bool sql_mngusr(SQLPTR *sqlptr,SQLENUM action,char *key,USRTYP **usr);
+
+//procedure to manage information on email exchange session
+extern _Bool sql_mngses(SQLPTR *sqlptr,SQLENUM action,SESTYP **ses);
+
+//Procedure to increment (or decrement) the remoteip links number
+extern int sql_newconnect(SQLPTR *sqlptr,char *rmtip,int delta);
+
+//procedure to extract the information about remotes server
+extern _Bool sql_mngremote(SQLPTR *sqlptr,SQLENUM action,char *key,SRVTYP **srv);
+
+//procedure to collect a list of "to be checked" remote IP
+extern char **sql_getnewrmtip(SQLPTR *sqlptr,int frequency);
+
+//procedure to create a new event entry
+extern int sql_addevent(SQLPTR *sqlptr,char *session,int debut,int fin);
+
+#endif
diff --git a/lib/gestcp.c b/lib/gestcp.c
new file mode 100644 (file)
index 0000000..f89a5e6
--- /dev/null
@@ -0,0 +1,293 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Module to manage TCP communication              */
+/*                                                     */
+/********************************************************/
+#include        <sys/wait.h>
+#include        <sys/socket.h>
+#include        <errno.h>
+#include        <fcntl.h>
+#include        <netdb.h>
+#include        <poll.h>
+#include        <stdlib.h>
+#include        <stdio.h>
+#include        <string.h>
+#include        <unistd.h>
+
+#include        "unieml.h"
+#include        "unisig.h"
+#include        "uniprc.h"
+#include        "gestcp.h"
+
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to add a line to a TCP buffer         */
+/*      return the a point to the new busffer           */
+/*                                                      */
+/********************************************************/
+PUBLIC char *tcp_addline(char *buffer,char *line)
+
+{
+register int taille;
+
+taille=0;
+if (strlen(line)>0) {
+  taille=strlen(line)+strlen(CRLF)+3;
+  if (buffer==(char *)0) 
+    buffer=(char *)calloc(taille,sizeof(char));
+  taille+=strlen(buffer);
+  buffer=realloc(buffer,taille);
+  (void) strcat(buffer,line);
+  (void) strcat(buffer,CRLF);
+  }
+return buffer;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to read char from remote peer, wait   */
+/*      for a CRLF AS EOL.                              */
+/*      - Return "got", which could be:                 */
+/*      - a positive number of character read.          */
+/*      -  0, reading reach time out                    */
+/*      - -1, signal received                           */
+/*      - -2, remote disconnect                         */
+/*                                                      */
+/********************************************************/
+PUBLIC int tcp_getline(SOCPTR *socptr,u_int secwait,char **lineptr)
+
+{
+#define OPEP    "gestcp.c:tcp_getline"
+
+int got;
+int phase;
+_Bool proceed;
+
+got=0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  (void) rou_dbglive(9,OPEP,"phase='%d' line=<%s>",phase,*lineptr);
+  switch (phase) {
+    case 0      :       //link still open?
+      if (soc_receive(socptr)<0) {
+        got=-2;
+        phase=999;
+        }
+      break;
+    case 1      :       //get nextline if ready
+      if ((got=soc_getnextline(socptr,lineptr))>=0)
+        phase=999;      //we got a line.
+      break;
+    case 2      :       //lets wait secwait*1000 millisec for input 
+      got=soc_waitforchar(socptr,secwait*1000);
+      switch (got) {
+        case -2         :       //remote is disconnectd
+          (void) rou_alert(0,"%s remote found disconnected",OPEP);
+          break;                
+        case -1         :       //trouble? signal?
+          if ((hangup==true)||(reload==true))
+            phase=999;          //we got a real signal
+          break;                
+        case  0         :       //normal time out
+          got=soc_receive(socptr);
+          phase=999;            
+          break;                
+        default         :       //char available
+          phase=0;              //is new line ready?
+          break;
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return got;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to write a string on the socket output*/
+/*      return the number of char sent on channel.      */
+/*                                                      */
+/********************************************************/
+PUBLIC int tcp_write(SOCPTR *socptr,char *buffer)
+
+{
+int sent;
+
+sent=-1;
+if (socptr!=(SOCPTR *)0) {
+  int taille;
+
+  taille=strlen(buffer);
+  if (taille>0) 
+    sent=soc_writebuffer(socptr,buffer,taille);
+  //(void) rou_alert(0,"tcp_write,JMPDBG <%s>",buffer);
+  }
+return sent;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to wait answer from remote and log    */
+/*      all answer from remote.                         */
+/*      return an SMTP reply code.                      */
+/*                                                      */
+/********************************************************/
+PUBLIC int tcp_get_smtp_reply(RMTTYP *rmt,int wait,char ***resp)
+
+{
+#define OPEP    "gestcp.c:tcp_get_smtp_reply,"
+
+int code;
+int maxlines;
+
+code=ERRPROC;
+maxlines=20;     //maximun number of line error
+if (debug>0)
+  wait/=10;     //debug mode not waiting for long
+while (maxlines>0) {
+  char *line;
+  int sofar;
+  
+  line=(char *)0;
+  if (tcp_getline(rmt->socptr,wait,&line)<=0) {
+    char cmt[100]; 
+
+    (void) snprintf(cmt,sizeof(cmt),"%d Timeout waiting '%d' sec for MX <%s>",
+                                    ERRPROC,wait,rmt->curmx->mxname);
+    line=strdup(cmt);
+    }
+  (void) log_fprintlog(rmt->logptr,true,line);
+  if (sscanf(line,"%d%n",&code,&sofar)==1) {
+    if (line[sofar]==' ')
+      maxlines=0;       //found remote status
+    else
+      code=ERRPROC;
+    }
+  if (resp==(char ***)0) 
+    line=rou_freestr(line);
+  else
+    *resp=(char **)rou_addlist((void **)*resp,(void *)line);
+  maxlines--;
+  }
+return code;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to build a command and send it to     */
+/*      remote server, log request and return the       */
+/*      remote response code.                           */
+/*                                                      */
+/********************************************************/
+PUBLIC int tcp_smtp_command(RMTTYP *rmt,char ***resp,char *fmt,...)
+
+{
+va_list args;
+char strloc[300];
+
+va_start(args,fmt);
+(void) vsnprintf(strloc,sizeof(strloc)-4,fmt,args);
+va_end(args);
+(void) log_fprintlog(rmt->logptr,false,strloc);
+(void) strcat(strloc,CRLF);
+(void) tcp_write(rmt->socptr,strloc);
+return tcp_get_smtp_reply(rmt,WAITRMT,resp);
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to send a queued email data contents  */
+/*      to a remote server.                             */
+/*      Return the number of character sent.            */
+/*                                                      */
+/********************************************************/
+PUBLIC int tcp_send_smtp_data(RMTTYP *rmt,const char *qemail)
+
+{
+#define OPEP    "gestcpc.c:tcp_send_smtp_data,"
+#define ENDDATA "."CRLF
+
+int sent;
+FILE *qfile;
+int phase;
+_Bool proceed;
+
+sent=-1;
+qfile=(FILE *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //opending email data file
+      if ((qfile=eml_openqfile(qemail,(const char *)0))==(FILE *)0) {
+        (void) rou_alert(0,"%s, Unable to open file <%s> (bug?)",
+                            OPEP,qemail); 
+        phase=999;      //no need to go further
+        }
+      break;
+    case 1      :  {    //transmitting data to remote
+      char data[300];
+
+      sent=0;
+      while (fgets(data,sizeof(data)-4,qfile)!=(char *)0) {
+        do {
+          register int taille;
+          register char *ptr;
+
+          taille=strlen(data)-1;
+          if (taille<0)
+            break;
+          ptr=data+taille; 
+          if ((*ptr!='\n')&&(*ptr!='\r'))
+            break;      //Line too long!
+          *ptr='\000';
+          }
+        while (strlen(data)>0);
+        //dot escape
+        if ((strlen(data)>0)&&(data[0]=='.'))
+          (void) memmove(data+1,data,strlen(data));
+        (void) strcat(data,CRLF);
+        sent+=tcp_write(rmt->socptr,data);
+        }
+      break;
+      }
+    case 2      :       //end of transmission
+      sent+=tcp_write(rmt->socptr,ENDDATA);
+      break;
+    case 3      :       //closing datafile
+      qfile=eml_closeqfile(qfile);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return sent;
+
+#undef  ENDDATA
+#undef  OPEP
+}
diff --git a/lib/gestcp.d b/lib/gestcp.d
new file mode 100644 (file)
index 0000000..05d67dd
--- /dev/null
@@ -0,0 +1,121 @@
+gestcp.o gestcp.d : gestcp.c /usr/include/stdc-predef.h /usr/include/sys/wait.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/signal.h \
+ /usr/include/bits/signum-generic.h /usr/include/bits/signum-arch.h \
+ /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timespec.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/types/stack_t.h /usr/include/sys/ucontext.h \
+ /usr/include/bits/sigstack.h /usr/include/bits/sigstksz.h \
+ /usr/include/bits/ss_flags.h /usr/include/bits/types/struct_sigstack.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/bits/types/idtype_t.h \
+ /usr/include/sys/socket.h /usr/include/bits/types/struct_iovec.h \
+ /usr/include/bits/socket.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/socket_type.h /usr/include/bits/sockaddr.h \
+ /usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h /usr/include/asm/bitsperlong.h \
+ /usr/include/asm-generic/bitsperlong.h /usr/include/asm/sockios.h \
+ /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/errno.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/fcntl.h \
+ /usr/include/bits/fcntl.h /usr/include/bits/fcntl-linux.h \
+ /usr/include/bits/stat.h /usr/include/bits/struct_stat.h \
+ /usr/include/netdb.h /usr/include/netinet/in.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h /usr/include/poll.h \
+ /usr/include/sys/poll.h /usr/include/bits/poll.h /usr/include/stdlib.h \
+ /usr/include/bits/libc-header-start.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/stdio.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h unieml.h \
+ subafn.h unisig.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h uniprc.h gestcp.h unidns.h \
+ devlog.h devsoc.h unitls.h /usr/include/openssl/ssl.h \
+ /usr/include/openssl/macros.h /usr/include/openssl/opensslconf.h \
+ /usr/include/openssl/configuration.h /usr/include/openssl/opensslv.h \
+ /usr/include/openssl/e_os2.h /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-least.h /usr/include/openssl/e_ostime.h \
+ /usr/include/sys/time.h /usr/include/openssl/comp.h \
+ /usr/include/openssl/crypto.h /usr/include/openssl/safestack.h \
+ /usr/include/openssl/stack.h /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h /usr/include/openssl/core.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/comperr.h /usr/include/openssl/bio.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/x509.h \
+ /usr/include/openssl/buffer.h /usr/include/openssl/buffererr.h \
+ /usr/include/openssl/evp.h /usr/include/openssl/core_dispatch.h \
+ /usr/include/openssl/indicator.h /usr/include/openssl/params.h \
+ /usr/include/openssl/bn.h /usr/include/openssl/bnerr.h \
+ /usr/include/openssl/evperr.h /usr/include/openssl/objects.h \
+ /usr/include/openssl/obj_mac.h /usr/include/openssl/asn1.h \
+ /usr/include/openssl/asn1err.h /usr/include/openssl/objectserr.h \
+ /usr/include/openssl/ec.h /usr/include/openssl/ecerr.h \
+ /usr/include/openssl/rsa.h /usr/include/openssl/rsaerr.h \
+ /usr/include/openssl/dsa.h /usr/include/openssl/dh.h \
+ /usr/include/openssl/dherr.h /usr/include/openssl/dsaerr.h \
+ /usr/include/openssl/sha.h /usr/include/openssl/x509err.h \
+ /usr/include/openssl/x509_vfy.h /usr/include/openssl/lhash.h \
+ /usr/include/openssl/pkcs7.h /usr/include/openssl/pkcs7err.h \
+ /usr/include/openssl/http.h /usr/include/openssl/conf.h \
+ /usr/include/openssl/conferr.h /usr/include/openssl/conftypes.h \
+ /usr/include/openssl/pem.h /usr/include/openssl/pemerr.h \
+ /usr/include/openssl/hmac.h /usr/include/openssl/async.h \
+ /usr/include/openssl/asyncerr.h /usr/include/openssl/ct.h \
+ /usr/include/openssl/cterr.h /usr/include/openssl/sslerr.h \
+ /usr/include/openssl/sslerr_legacy.h /usr/include/openssl/prov_ssl.h \
+ /usr/include/openssl/ssl2.h /usr/include/openssl/ssl3.h \
+ /usr/include/openssl/tls1.h /usr/include/openssl/dtls1.h \
+ /usr/include/openssl/srtp.h /usr/include/openssl/quic.h
diff --git a/lib/gestcp.h b/lib/gestcp.h
new file mode 100644 (file)
index 0000000..af3b656
--- /dev/null
@@ -0,0 +1,46 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/************************************************/
+/*                                             */
+/*     gestion level to handle TCP             */
+/*      communication                           */
+/*                                             */
+/************************************************/
+#ifndef        GESTCP
+#define GESTCP
+
+#include        <stdbool.h>
+
+#include        "subrou.h"
+#include        "unidns.h"
+#include        "devlog.h"
+#include        "devsoc.h"
+
+//structure handling remote
+typedef struct  {
+    SOCPTR *socptr;     //socket to remote pointer
+    LOGPTR *logptr;     //Log pointer
+    char *orgdomain;    //originator domain
+    char *dstdomain;    //Domain to be reached
+    MXTYP *curmx;       //Current MX number
+    MXTYP **mxs;        //MX list form domain
+    }RMTTYP;
+
+//procedure to add a line to a buffer
+extern char *tcp_addline(char *buffer,char *line);
+
+//read a line from contact up to CRLF
+extern int tcp_getline(SOCPTR *socptr,u_int secwait,char **lineptr);
+
+//Transmit formated data to the contact channel
+extern int tcp_write(SOCPTR *socptr,char *buffer);
+
+//wait and answer from remote and return the reply code
+extern  int tcp_get_smtp_reply(RMTTYP *rmt,int wait,char ***resp);
+
+//Transmit a command to remote SMTP server
+extern int tcp_smtp_command(RMTTYP *rmt,char ***resp,char *fmt,...);
+
+//send the email data
+extern int tcp_send_smtp_data(RMTTYP *rmt,const char *qemail);
+
+#endif
diff --git a/lib/lvleml.c b/lib/lvleml.c
new file mode 100644 (file)
index 0000000..97085e7
--- /dev/null
@@ -0,0 +1,3197 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Implement all routine to manage SMTP low level  */
+/*     exchange.                                       */
+/*                                                     */
+/********************************************************/
+#include        <arpa/inet.h>
+#include        <sys/types.h>
+
+#include        <ctype.h>
+#include        <errno.h>
+#include        <netdb.h>
+#include        <stdbool.h>
+#include        <stdlib.h>
+#include        <stdio.h>
+#include        <string.h>
+#include        <signal.h>
+#include        <time.h>
+#include        <unistd.h>
+
+#include       "subafn.h"
+#include       "subrou.h"
+#include       "subcnv.h"
+#include       "unidig.h"
+#include       "unieml.h"
+#include       "uniprc.h"
+#include       "devlog.h"
+#include       "gestcp.h"
+#include       "gessql.h"
+#include       "geseml.h"
+#include       "lvleml.h"
+
+//env variable to define the list of relayable
+#define RELAYS  "RELAYABLE"
+
+static const char *cry="(crypted link is now set in '%s' mode, security level='%d')";
+
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to update the termination condition   */
+/*      within contact.                                 */
+/*      current session.                                */
+/*                                                      */
+/********************************************************/
+static void setterminator(CONTYP *contact,const char *condition)
+
+{
+if (contact!=(CONTYP *)0) {
+  contact->termend=rou_freestr(contact->termend);
+  contact->termend=strdup(condition);
+  }
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to update database and free the       */
+/*      current session.                                */
+/*                                                      */
+/********************************************************/
+static void freesessid(CONTYP *contact)
+
+{
+if (contact->session!=(SESTYP *)0) {
+  if (contact->mailfrom!=(char *)0) {
+    contact->session->sfrom=strdup(contact->mailfrom);
+    (void) sql_mngses(contact->sqlptr,sql_update,&(contact->session));
+    }
+  contact->session=sql_freeses(contact->session);
+  }
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to manage curent SMTP session data    */
+/*      within contact.                                 */
+/*                                                      */
+/********************************************************/
+static void getsessid(CONTYP *contact)
+
+{
+if (contact!=(CONTYP *)0) {
+  char *newsid;
+
+  newsid=eml_getcursesid(contact->mainsesid,contact->numreset);
+  contact->session=(SESTYP *)calloc(1,sizeof(SESTYP));
+  contact->session->sessid=newsid;
+  (void) sql_mngses(contact->sqlptr,sql_insert,&(contact->session));
+  }
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to free memory used by contact        */
+/*                                                      */
+/********************************************************/
+static CONTYP *freecontact(CONTYP *contact)
+
+{
+#define OPEP    "lvleml.c:freecontact"
+
+if (contact!=(CONTYP *)0) {
+  long debut;
+  long fin;
+
+  debut=0;
+  fin=0;
+  (void) freesessid(contact);
+  contact->logptr=log_closelog(contact->logptr,&debut,&fin);
+  if (contact->mainsesid!=(char *)0) {
+    (void) sql_addevent(contact->sqlptr,contact->mainsesid,debut,fin);
+    contact->mainsesid=rou_freestr(contact->mainsesid);
+    }
+  contact->sqlptr=sql_closesql(contact->sqlptr);
+  contact->termend=rou_freestr(contact->termend);
+  contact->relayok=(AFNTYP **)rou_freelist((void **)contact->relayok,
+                                         (genfree_t)afn_freeipnum);
+  contact->recipients=(RCPTYP **)rou_freelist((void **)contact->recipients,
+                                              (genfree_t)eml_freerecipient);
+  contact->authname=rou_freestr(contact->authname);
+  contact->mailfrom=rou_freestr(contact->mailfrom);
+  contact->fqdn=rou_freestr(contact->fqdn);
+  contact->peername=rou_freestr(contact->peername);
+  contact->peerip=rou_freestr(contact->peerip);
+  contact->locserv=rou_freestr(contact->locserv);
+  contact->localafns=(AFNTYP **)rou_freelist((void **)contact->localafns,
+                                             (genfree_t)afn_freeipnum);
+  contact->locip=rou_freestr(contact->locip);
+  contact->locname=rou_freestr(contact->locname);
+  (void) free(contact);
+  contact=(CONTYP *)0;
+  }
+return contact;
+#undef OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to get the domain code                */
+/*                                                      */
+/********************************************************/
+static _Bool setlocdom(CONTYP *contact,RCPTYP *rcpt)
+
+{
+#define OPEP    "lvleml.c:setlocdom,"
+
+char done;
+int phase;
+_Bool proceed;
+
+done=true;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d' parm=<%s>",OPEP,phase,parameter);
+  switch (phase) {
+    case 0      :       //is RCPT ok;
+      if ((rcpt==(RCPTYP *)0)||(rcpt->domain==(char *)0)) {
+        (void) rou_alert(0,"%s Recipient is NULL (Bug!?)",OPEP);
+        done=false;
+        phase=999;
+        }
+      break;
+    case 1      :       //check about local domain, TEMPORARY code
+      int status;
+
+      status=dns_is_domain_local(rcpt->domain,contact->localafns);
+      switch (status) {
+        case dns_nomx   :
+          done=false;
+          break;
+        case dns_local  :
+          rcpt->code='L';       //domain is local
+          break;
+        case dns_remote :
+          rcpt->code='R';       //domain is remote
+          break;
+        default         :
+          (void) rou_alert(0,"%s Unexpected status='%d' for domain<%s>",
+                              OPEP,status,rcpt->domain);
+          break;
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return done;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to send the signon once the contact   */
+/*      with peer is detected.                          */
+/*                                                      */
+/********************************************************/
+static void signon(CONTYP *contact)
+
+{
+#define FMT     "%d %s, ESMTP (%s) %s-%s; %s"
+
+if (contact!=(CONTYP *)0) {
+  const char *mode;
+  char signon[100];
+
+  mode=soc_getstrmode(contact->socptr);
+  (void) snprintf(signon,sizeof(signon),FMT,
+                                        SIGNON,contact->locname,
+                                        mode,
+                                        APPNAME,
+                                        rou_getversion(),
+                                        rou_ascsysstamp(time((time_t *)0)));
+  (void) eml_transmit(contact,true,"%s",signon);
+  }
+#undef  FMT
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to send ready link information        */
+/*      is a correct one                                */
+/*                                                      */
+/********************************************************/
+static void linkready(CONTYP *contact,_Bool suite)
+
+{
+const char *mode;
+_Bool flush;
+char sepa;
+
+sepa=' ';
+flush=true;
+if (suite==true) {
+  flush=false;
+  sepa='-';
+  }
+mode=soc_getstrmode(contact->socptr);
+(void) eml_transmit(contact,flush,"%d%c%s, link (%s) ready, your IP/FQDN=[%s/%s]",
+                                  CMDOK,sepa,contact->locname,mode,
+                                  contact->peerip,contact->peername);
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to add received information to        */
+/*      the email contents.                             */
+/*                                                      */
+/********************************************************/
+static _Bool addreceived(CONTYP *contact,FILE *data)
+
+{
+_Bool status;
+time_t curtime;
+const char *esmtp;
+int phase;
+_Bool proceed;
+
+status=true;
+esmtp="ESMTP";
+curtime=time((time_t *)0);
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Inserting the remote information
+      (void) fprintf(data,"Received: from %s (IP=[%s], originator=<%s>)\n",
+                           contact->peername,contact->peerip,contact->mailfrom);
+      break;
+    case 1      :       //Inserting the Receive information
+      if (contact->privilege==rel_authentic) 
+        esmtp="ESMTPA";
+      (void) fprintf(data,"\tby %s ([%s:%s]/%s-%s) with %s\n",
+                          contact->locname,
+                          contact->locip,
+                          contact->locserv,
+                          appname,rou_getversion(),
+                          esmtp);
+      break;
+    case 2      :       //Inserting TLS information
+      if (soc_iscrypted(contact->socptr)==true) {
+        char *cipherid;
+
+        cipherid=soc_getcipherid(contact->socptr);
+        if (cipherid!=(char *)0)
+          (void) fprintf(data,"\t(%s)\n",cipherid);
+        cipherid=rou_freestr(cipherid);
+        }
+      break;
+    case 3      :       //Inserting ID information
+      (void) fprintf(data,"\tid <%s@%s>;\n",
+                          contact->session->sessid,contact->locname);
+      break;
+    case 4      :       //date information
+      (void) fprintf(data,"\t%s\n",rou_ascsysstamp(curtime));
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check remote IP credit             */
+/*                                                      */
+/********************************************************/
+static int checkcredit(CONTYP *contact)
+
+{
+#define OPEP     "lvleml.c:checkcredit,"
+
+int status;
+SRVTYP *srv;
+int phase;
+_Bool proceed;
+
+status=FAILED;
+srv=(SRVTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //reading remote server information
+      if (sql_mngremote(contact->sqlptr,sql_select,contact->peerip,&srv)==false) {
+        (void) rou_alert(0,"%s Unable to find server data for remote <%s> (BUg?)",
+                            OPEP,contact->peerip);
+        phase=999;
+        }
+      break;
+    case 1      :       //checking credential
+      if (srv->links>1) {
+        status=ALREADY;
+        (void) eml_note(contact,NULL,"%d 4.5.6 already connected",status);
+        (void) eml_transmit(contact,true,"%d 4.5.6 already connected",status);
+        phase=999;      //To many server connected
+        }
+      break;
+    case 2      :       //checking if black listed (low credit)
+      if (srv->credit<0) {
+        char *cmt;
+        const char *ninfo;
+
+        cmt=srv->listing;
+        ninfo="%d-4.5.7 Originator server IP [%s] black listed <%s>";
+        if (cmt==(char *)0)
+          cmt="credit too low";
+        (void) log_fprintlog(contact->logptr,false,"(Remote %s[%s] credit='%d' %s)",
+                                                    contact->peername,
+                                                    contact->peerip,
+                                                    srv->credit,"too low");
+        status=BLCKLST;
+        (void) eml_note(contact,NULL,ninfo,status,contact->peerip,cmt);
+        (void) eml_transmit(contact,true,ninfo,status,contact->peerip,cmt);
+        (void) setterminator(contact,"Remote server credit is too low");
+        phase=999;      //To many server connected
+        }
+      break;
+    case 3      :       //remote server is "credible"
+      status=CMDOK;
+      break;
+    default     :       //SAFE Guard, freeing memory
+      srv=sql_freesrv(srv);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to split a plain authentication string*/
+/*      Match with the user provided.                   */
+/*                                                      */
+/********************************************************/
+static void split_auth_plain(char *sequence,char *data[3])
+
+{
+char *locdata[3];
+char *dup;
+char *ptr;
+int count;
+
+(void) memset(locdata,'\000',sizeof(locdata));
+dup=strdup(sequence);
+ptr=dup;
+for (count=0;(count<3)&&(ptr!=(char *)0);count++) {
+  char *mark;
+
+  if ((mark=strstr(ptr,IOBNULL))!=(char *)0) {
+    *mark='\000';
+    mark+=strlen(IOBNULL);
+    }
+  locdata[count]=strdup(ptr);
+  ptr=mark; 
+  }
+(void) memmove(data,locdata,sizeof(locdata));
+dup=rou_freestr(dup);
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check if the password submitted    */
+/*      Match with the user provided.                   */
+/*                                                      */
+/********************************************************/
+static _Bool checklogin(CONTYP *contact,char **rmtpass,char *sequence)
+
+{
+#define OPEP    "lvleml.c:checklogin,"
+
+_Bool isok;
+char *data[3];
+char *givenhash;
+int phase;
+_Bool proceed;
+
+isok=false;
+(void) memset(data,'\000',sizeof(data));
+givenhash=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //make sure we have data
+      (void) split_auth_plain(sequence,data);
+      if ((data[1]==(char *)0)||(data[2]==(char *)0)) {
+        (void) rou_alert(0,"%s missing data from <%s> (Bug?)",OPEP,sequence);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 1      :       //do we have valide data?
+      if ((strlen(data[1])==0)||(strlen(data[2])==0)) {
+        (void) rou_alert(0,"%s data[1]=<%s> or data[2]=<%s> missing (Remote Bug?)",
+                            OPEP,data[1],data[2]);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 2      :  {    //checking user password
+      USRTYP *usr;
+
+      usr=(USRTYP *)0;
+      contact->authname=rou_freestr(contact->authname);
+      contact->authname=strdup(data[1]);
+      *rmtpass=strdup(data[2]);
+      if (sql_mngusr(contact->sqlptr,sql_select,data[1],&usr)==true) {
+        char *givenpass;;
+
+        givenpass=data[2];
+        if (usr->password==(char *)0) {
+          (void) rou_alert(0,"%s usr=<%s> password empty, assigning one",
+                              OPEP,data[1]);
+          usr->password=cnv_getrndstr(10);
+          }
+        if (usr->password[0]=='$') {
+          char *ptr;
+          char idsalt[100];
+
+          (void) memset(idsalt,'\000',sizeof(idsalt));
+          (void) strncpy(idsalt,usr->password,sizeof(idsalt)-1);
+          if ((ptr=strrchr(idsalt,'$'))!=(char *)0) {
+            ptr++;
+            *ptr='\000';
+            }
+          if ((ptr=crypt(givenpass,idsalt))==(char *)0) {
+            (void) rou_alert(0,"%s Trouble to crypt (Bug?) givenpass=<%s> "
+                               "salt=<%s> (error=<%s>)",
+                                OPEP,givenpass,idsalt,strerror(errno));
+            ptr=data[2]; //trying to overcome
+            }
+          givenpass=ptr;
+          }
+        isok=(strcmp(givenpass,usr->password)==0);
+        usr=sql_freeusr(usr);
+        }
+      }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+givenhash=rou_freestr(givenhash); 
+for (int i=0;i<3;i++) 
+  data[i]=rou_freestr(data[i]);
+return isok;
+
+#undef OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to manage authentication in "plain"   */
+/*      mode, return "decoded", NULL if not extracted   */
+/*                                                      */
+/********************************************************/
+static void get_auth_plain(CONTYP *contact,char *received,char **rmtpass)
+
+{
+#define OPEP    "lvleml.c:get_auth_plain,"
+
+char *decoded;
+
+*rmtpass=(char *)0;
+decoded=(char *)0;
+if ((received==(char *)0)||(strlen(received)==0)) {
+  char *line;
+  int got;
+
+  line=(char *)0;
+  (void) eml_transmit(contact,true,"%d 5.7.1 Please provide auth sequence",SENDB64);
+  got=tcp_getline(contact->socptr,contact->delay,&line);
+  if (got>0) {
+    (void) log_fprintlog(contact->logptr,false,"%s",line);
+    decoded=cnv_getb64(line);
+    line=rou_freestr(line);
+    }
+  }
+else 
+  decoded=cnv_getb64(received);
+if (decoded!=(char *)0) {
+  if (checklogin(contact,rmtpass,decoded)==true) {
+    contact->credit+=2;
+    contact->privilege=rel_authentic;
+    }
+  decoded=rou_freestr(decoded);
+  }
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to manage authentication in "login"   */
+/*      mode, return goodpass  true if successful       */
+/*                                                      */
+/********************************************************/
+static void get_auth_login(CONTYP *contact,char **rmtpass)
+
+{
+static char *logdat[]={"VXNlcm5hbWU6",  //Username: in B64
+                       "UGFzc3dvcmQ6",  //Password: in B64
+                       (char *)0};
+char local[200];
+
+(void) memset(local,'\000',sizeof(local));
+for (int i=0;i<2;i++) {
+   char *line;
+   char *ptr;
+   int got;
+
+   (void) strcat(local,IOBNULL);
+   (void) eml_transmit(contact,true,"%d %s",SENDB64,logdat[i]);
+   got=tcp_getline(contact->socptr,contact->delay,&line);
+   if (got<=0) {
+     const char *ninfo="%d 5.7.2 auth sequence missing";
+
+     (void) strcpy(local,"");
+     (void) eml_note(contact,NULL,ninfo,UKNUSER);
+     (void) eml_transmit(contact,true,ninfo,UKNUSER);
+     break;                 //exiting from loop
+     }
+  (void) log_fprintlog(contact->logptr,false,"%s",line);
+  if ((ptr=cnv_getb64(line))!=(char *)0) {
+    (void) strncat(local,ptr,(sizeof(local)-1)-strlen(ptr));
+     ptr=rou_freestr(ptr);
+     }
+  line=rou_freestr(line);
+  }
+if (strlen(local)>0) {
+  if (checklogin(contact,rmtpass,local)==true) {
+    contact->credit+=2;
+    contact->privilege=rel_authentic;
+    }
+  }
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to manage authentication in           */
+/*      digest-md5 mode.                                */
+/*      Return a builded "decoded" string from          */
+/*      the exchange with the remote sereur             */
+/*      see RFC 2831.                                   */
+/*                                                      */
+/*                                                      */
+/********************************************************/
+static void get_auth_digest_md5(CONTYP *contact,char **rmtpass)
+
+{
+#define OPEP    "lvleml.c:get_auth_digest_md5,"
+
+char *challenge;
+RSPTYP *resp;
+char answer[300];
+char hash[40];
+int phase;
+_Bool proceed;
+
+*rmtpass=strdup("$1(a_digest_md5)");
+challenge=(char *)0;
+resp=(RSPTYP *)0;
+(void) memset(answer,'\000',sizeof(answer));
+(void) memset(hash,'\000',sizeof(hash));
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //assign the reaml challeng
+      break;
+    case 1      :       //Building the challenge sequence
+      if ((challenge=dig_getchallenge())==(char *)0) {
+        (void) rou_alert(0,"%s Unable to get challenge sequence (Bug!)",OPEP);
+        phase=999;
+        }
+      break;
+    case 2      :  {    //sending challenge to remote
+      char *b64;
+
+      b64=cnv_setb64(challenge);
+      (void) eml_transmit(contact,true,"%d %s",SENDB64,b64);
+      b64=rou_freestr(b64);
+      }
+      break;
+    case 3      :  {    //getting the challenge answer
+      char *line;
+
+      if (tcp_getline(contact->socptr,contact->delay,&line)>0) {
+        //if a clear text QUIT is received because of deep trouble
+        if (strcasecmp(line,"QUIT")!=0) {
+          char *res;
+
+          res=cnv_getb64(line);
+          (void) rou_alert(2,"%s received=<%s>",OPEP,res);
+          (void) snprintf(answer,sizeof(answer),"%s",res);
+          res=rou_freestr(res);
+          }
+        line=rou_freestr(line); 
+        }
+      if (strlen(answer)==0)
+        phase=999;      //no need to go further
+      }
+      break;
+    case 4      :       //Parsing the answer
+      if ((resp=dig_parseresp(answer))==(RSPTYP *)0)
+        phase=999;      //Unable to parse answer
+      break;
+    case 5      :       //checking if we have a user name
+      if (resp->username==(char *)0) {
+        (void) rou_alert(0,"%s No username in challenge answer!",OPEP);
+        phase=999;      //Unable to parse answer
+        }
+      break;
+    case 6      :  {    //getting the user name and password
+      USRTYP *usr;
+
+      contact->authname=rou_freestr(contact->authname);
+      contact->authname=strdup(resp->username);
+      if (sql_mngusr(contact->sqlptr,sql_select,resp->username,&usr)==true) {
+        if (usr->hash!=(char *)0)
+          (void) strncpy(hash,usr->hash,sizeof(hash)-1);
+        usr=sql_freeusr(usr);
+        }
+      if (strlen(hash)==0) {
+        (void) rou_alert(0,"%s user <%s> missing hash value!",OPEP,resp->username);
+        phase=999;      //No need to go further
+        }
+      }
+      break;
+    case 7      :  {    //sending the rspauth sequence.
+      char *HAS;
+      char *rspauth;
+      char *b64;
+
+      if ((HAS=dig_hashresp(resp,"",hash))==(char *)0) {
+        (void) rou_alert(0,"%s Unable to get the hash rspauth (Bug!)",OPEP);
+        break;
+        }
+      (void) rou_asprintf(&rspauth,"rspauth=%s",HAS);
+      b64=cnv_setb64(rspauth);
+      (void) eml_transmit(contact,true,"%d %s",SENDB64,b64);
+      b64=rou_freestr(b64);
+      rspauth=rou_freestr(rspauth);
+      HAS=rou_freestr(HAS);
+      }
+      break;
+    case 8      :  {    //waiting client to send an empty line
+      char *line;
+      int count;
+
+      line=(char *)0;
+      count=tcp_getline(contact->socptr,contact->delay,&line);
+      if (count<0) 
+        (void) rou_alert(0,"%s delay expired to get remote empty line (network?)",
+                            OPEP);
+      line=rou_freestr(line);      //EMPTY Line!
+      }
+      break;
+    case 9      :  {    //comparing result.
+      char *HA0;
+
+      if ((HA0=dig_hashresp(resp,"AUTHENTICATE",hash))==(char *)0) {
+        (void) rou_alert(0,"%s Unable to get the hash response (Bug!)",OPEP);
+        break;
+        }
+      if (strcmp(HA0,resp->response)==0) {
+        contact->credit+=3;     //very good authentication
+        contact->privilege=rel_authentic;
+        }
+      HA0=rou_freestr(HA0);
+      }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+resp=dig_freeresp(resp);
+challenge=rou_freestr(challenge); 
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to extract authentication info.       */
+/*      return a login sequence as                      */
+/*      'username password' if successful               */
+/*      NULL if unable to extract login                 */
+/*                                                      */
+/********************************************************/
+static _Bool getauth(CONTYP *contact,char *buffer)
+
+{
+#define OPEP    "lvleml.c:getauth,"
+#define DISP    "---- "
+
+static char *vocloc[]={"PLAIN","LOGIN","DIGEST-MD5",(char *)0};
+
+int code;
+char *rmtpass;
+char local[200];
+int phase;
+_Bool proceed;
+
+code=-1;
+rmtpass=(char *)0;
+(void) memset(local,'\000',sizeof(memset));
+phase=0;
+proceed=true;
+while (proceed==true) {
+  (void) rou_dbglive(9,OPEP,"phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :               //empty phase
+      break;
+    case 1      :       //check buffer
+      if ((buffer==(char *)0)||(strlen(buffer)==0)) {
+        const char *ninfo="%d 5.7.1 Missing auth type";
+
+        (void) eml_note(contact,NULL,ninfo,UKNUSER);
+        (void) eml_transmit(contact,true,ninfo,UKNUSER);
+        phase=999;      //No need to go further 
+        }
+      break;
+    case 2      :       //check the authentication type
+      (void) strncpy(local,buffer,sizeof(local)-1);
+      for (code=0;vocloc[code]!=(char *)0;code++) {
+        if (strncasecmp(local,vocloc[code],strlen(vocloc[code]))==0) {
+          register int taille;
+          register char *ptr;
+
+          taille=strlen(vocloc[code]);
+          ptr=local+taille+1;
+          (void) memcpy(local,ptr,strlen(ptr)+1);
+          break;
+          }
+        }
+      if (vocloc[code]==(char *)0) {
+        static char *cmt="bad authentication type";
+
+        (void) rou_alert(0,"%s auth type <%s> unknown (Bug?)",OPEP,local);
+        (void) eml_note(contact,NULL,"%d %s",BADPAR,cmt);
+        (void) eml_transmit(contact,true,"%d 5.7.2 %s",BADPAR,cmt);
+        (void) setterminator(contact,cmt);
+        phase=999;      //Trouble Trouble
+        }
+      break;
+    case 3      :       //getting "username password" accorind login type
+      switch (code) {
+        case 0          :       //AUTH PLAIN
+          if (soc_iscrypted(contact->socptr)==true) 
+            (void) get_auth_plain(contact,local,&rmtpass);
+          else {
+            static char *cmt="Unsafe PLAIN auth mode";
+
+            contact->credit-=2;
+            (void) eml_note(contact,NULL,"%d %s",FAILED,cmt);
+            (void) eml_transmit(contact,true,"%d 5.7.3 %s",FAILED,cmt);
+            (void) setterminator(contact,cmt);
+            phase=999;
+            }
+          break;
+        case 1          :       //AUTH LOGIN
+          if (soc_iscrypted(contact->socptr)==true) 
+            (void) get_auth_login(contact,&rmtpass);
+          else {
+            static char *cmt="Unsafe LOGIN auth mode";
+
+            contact->credit-=2;
+            (void) eml_note(contact,NULL,"%d %s",FAILED,cmt);
+            (void) eml_transmit(contact,true,"%d 5.7.3 %s",FAILED,cmt);
+            (void) setterminator(contact,cmt);
+            phase=999;
+            }
+          break;
+        case 2          :       //AUTH DIGEST-MD5
+          (void) get_auth_digest_md5(contact,&rmtpass);
+          break;
+        default         :  {    //not yet implemented
+          static char *cmt="Unexpected auth mode";
+
+          (void) rou_alert(0,"%s auth type <%d> not yet implemented (Bug?!)",
+                              OPEP,code);
+          (void) eml_note(contact,NULL,"%d 5.7.5 %s",FAILED,cmt);
+          (void) eml_transmit(contact,true,"%d 5.7.5 %s",FAILED,cmt);
+          (void) setterminator(contact,cmt);
+          phase=999;
+          }
+          break;
+        }
+      break;
+    case 4      :  {    //do we have a decoded sequence??
+      char *fmt;
+      char *auth;
+
+      fmt=DISP"Auth accepted for user=<%s>";
+      auth=contact->authname;
+      if (contact->privilege!=rel_authentic) {
+        fmt=DISP"Auth Rejected status='%d' for user=<%s> pass=<%s>";
+        (void) log_fprintlog(contact->logptr,true,fmt,BADAUTH,auth,rmtpass);
+        (void) sleep(2);
+        (void) eml_note(contact,NULL,"%d user <%s> pass=<%s> bad authentication",
+                                      BADAUTH,auth,rmtpass);
+        (void) eml_transmit(contact,true,"%d 5.7.4 wrong authentication",BADAUTH);
+        (void) setterminator(contact,"Authentication failure");
+        contact->credit-=1;
+        phase=999;
+        }
+      else {
+        (void) log_fprintlog(contact->logptr,true,fmt,auth);
+        (void) eml_note(contact,NULL,"%d 5.7.5 authentication successful",IDOK);
+        (void) eml_transmit(contact,true,"%d 5.7.5 authentication successful",IDOK);
+        contact->credit+=2;
+        }
+      }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+rmtpass=rou_freestr(rmtpass);
+return (contact->privilege==rel_authentic);
+
+#undef  DISP
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     checking if local recipient is acceptable.      */
+/*                                                      */
+/********************************************************/
+static _Bool is_user_good(CONTYP *contact,char *rcptto)
+
+{
+_Bool isok;
+USRTYP *usr;
+int phase;
+_Bool proceed;
+
+isok=false;
+usr=(USRTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //is user a local user
+      if ((sql_mngusr(contact->sqlptr,sql_select,rcptto,&usr))==false) {
+        (void) eml_note(contact,rcptto,"%d 5.6.0 <%s> unknown user",
+                                        UKNUSER,rcptto);
+        (void) eml_transmit(contact,true,"%d 5.6.0 <%s> unknown user",
+                                          UKNUSER,rcptto);
+        phase=999;      //No user found in database
+        }
+      break;
+    case 1      :       //are we in relaying mode
+      switch (contact->privilege) {
+        case rel_authentic      :
+        case rel_isrelay        :       //everything fine
+          isok=true;                    //we accepte "internal user"
+          phase=999;                    //no need to check lock and spf
+          break;
+        default                 :
+          break;
+        }
+      break;
+    case 2      :       //is user lock?
+      if (usr->lock==1) {
+        (void) eml_note(contact,rcptto,"%d 5.6.1 <%s> account lock",
+                                        EXPIRED,rcptto);
+        (void) eml_transmit(contact,true,"%d 5.6.1 <%s> account lock",
+                                          EXPIRED,rcptto);
+        phase=999;      //No user found in database
+        }
+      break;
+    case 3      :       //check is origin is SPF OK
+      switch (contact->fromspf) {
+        case spf_neutral        :       //'?' status
+        case spf_pass           :       //good SPF
+          isok=true;       
+          break;
+        case spf_fail           :       //Bad SPF
+          (void) eml_note(contact,rcptto,"%d 5.6.6 %s from IP=[%s]",
+                                         FAILED,"Relaying not allowed",
+                                         contact->peerip);
+          (void) eml_transmit(contact,rcptto,"%d 5.6.6 %s from IP=[%s]",
+                                              FAILED,"Relaying not allowed",
+                                              contact->peerip);
+          break;
+        case spf_softfail       :       //Bad SPF
+          (void) eml_note(contact,rcptto,"%d 5.6.7 %s from IP=[%s]",
+                                         FAILED,"SPF soft fail not allowed",
+                                         contact->peerip);
+          (void) eml_transmit(contact,rcptto,"%d 5.6.7 %s from IP=[%s]",
+                                              FAILED,"SPF soft fail not allowed",
+                                              contact->peerip);
+          break;
+        default                 :       //trouble trouble
+          (void) eml_note(contact,rcptto,"%d 5.6.8 %s (<%s> SPF unknown)",
+                                          FAILED,
+                                          "Originator domain BAD SPF definition",
+                                          contact->mailfrom);
+          (void) eml_transmit(contact,true,"%d 5.6.8 %s (<%s> SPF unknown)",
+                                            FAILED,
+                                            "Originator domain BAD SPF definition",
+                                            contact->mailfrom);
+          break;
+        }
+      break;
+    default     :       //SAFE Guard
+      usr=sql_freeusr(usr);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return isok;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     checking if remote recipient is relayable.      */
+/*                                                      */
+/********************************************************/
+static _Bool is_remote_good(CONTYP *contact,RCPTYP *rmtusr)
+
+{
+#define OPEP    "lvleml.c:is_remote_good,"
+
+_Bool remotegood;
+const char *cmt;
+int phase;
+_Bool proceed;
+
+remotegood=false;
+cmt=(const char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :  {    //Is there an MX for the user remote domain
+      MXTYP **mxs;
+
+      cmt="No MX nor IP for";
+      if ((mxs=dns_getmx(rmtusr->domain))!=(MXTYP **)0) {
+        for (int i=0;mxs[i]!=(MXTYP *)0;i++) {
+          if (mxs[i]->mxip!=(char **)0) {
+            phase++;        //No need to check for domain IP
+            break;
+            }
+          }
+        mxs=dns_freemxlist(mxs); 
+        }
+      }
+      break;
+    case 1      :  {    //is the domain with an IP address
+      char **iplist;
+
+      if ((iplist=dns_get_ip_list(rmtusr->domain))==(char **)0)
+        phase=999;      //not even an IP for domain
+      iplist=(char **)rou_freelist((void **)iplist,(genfree_t)rou_freestr);
+      }
+      break;
+    case 2      :       //is connection acceptable
+      switch (contact->privilege) {
+        case rel_authentic      :
+        case rel_isrelay        :       //everything fine
+          break;
+        case rel_plain          :
+          cmt="No relay accepted";
+          contact->credit-=2;           //Trying to abuse server
+          phase=999;    //not relayable
+          break;
+        default         :
+          cmt="Unchecked status";
+          phase=999;    //not relayable
+          break;
+        }
+      break;
+    case 3      :       //so it a good remote
+      remotegood=true;
+      break;
+    default     :       //SAFE Guard
+      if (remotegood==false) {
+        char *rcptto;
+
+        (void) rou_asprintf(&rcptto,"%s@%s",rmtusr->userid,rmtusr->domain);
+        (void) eml_note(contact,rcptto,"%d 2.8.0 %s for domain <%s>",
+                                        NORELAY,cmt,rmtusr->domain);
+        (void) eml_transmit(contact,true,"%d 2.8.0 %s for domain <%s>",
+                                          NORELAY,cmt,rmtusr->domain);
+        rcptto=rou_freestr(rcptto);
+        }
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return remotegood;
+
+#undef   OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check if helo or ehlo parameter    */
+/*      is a correct one                                */
+/*                                                      */
+/********************************************************/
+static _Bool isgoodfqdn(CONTYP *contact,char *parameter)
+
+{
+#define        OPEP    "lvleml.c:isgoodfqdn"
+#define HELL    " !@#$%^&*()=+[]{}|\\:;'\"<>,?"
+
+_Bool good;
+int phase;
+_Bool proceed;
+
+good=false;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  (void) rou_dbglive(7,OPEP,"phase='%d' parm=<%s>",phase,parameter);
+  switch (phase) {
+    case 0      :       //is parameter starting with '.'
+      if ((parameter[0]=='.')||(parameter[0]=='-'))
+        phase=999;      //no allowed to start with dot or dash
+      break;
+    case 1      :       //do we have '..' somewehre
+      if ((strstr(parameter,"..")!=(char *)0)||
+          (strstr(parameter,"-.")!=(char *)0)||
+          (strstr(parameter,".-")!=(char *)0))
+        phase=999;      //no allowed to start with dot
+      break;
+    case 2      :       //check if char set is allowed
+      if (strpbrk(parameter,HELL)!=(char *)0) 
+        phase=999;      //no a good charset
+      break;
+    case 3      :       //checking if parameter is only ascii
+      int taille;
+
+      taille=strlen(parameter);
+      for (int i=0;i<taille;i++) {
+        if (isascii(parameter[i])==false) {
+          phase=999;    //non ascii charatere
+          break;             
+          }
+        }
+      break;
+    case 4      :       //seems to be a good fqdn
+      good=true;
+      contact->fqdn=rou_freestr(contact->fqdn);
+      contact->fqdn=strdup(parameter);
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return good;
+
+#undef  HELL
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check if helo or ehlo parameter    */
+/*      is an acceptable one                            */
+/*                                                      */
+/********************************************************/
+static _Bool isgoodhelo(CONTYP *contact,char *parameter)
+
+{
+#define        OPEP    "lvleml.c:isgoodhelo"
+#define DETAIL  "HELO argument is incorrect, closing connection"
+
+_Bool good;
+int last;
+char original[100];
+int phase;
+_Bool proceed;
+
+good=false;
+last=0;
+(void) memset(original,'\000',sizeof(original));
+phase=0;
+proceed=true;
+while (proceed==true) {
+  (void) rou_dbglive(7,OPEP,"phase='%d' domain=<%s>",phase,parameter);
+  switch (phase) {
+    case 0      :       //checking if we have a parameter
+      if (parameter==(char *)0) {
+        (void) rou_alert(0,"%s sesid=<%s> fqdn is missing!",
+                            OPEP,contact->mainsesid);
+        phase=999;      //no parameter
+        }
+      break;
+    case 1      :       //strip possible space at the end pf parameter
+      (void) strncpy(original,parameter,sizeof(original)-1);
+      last=strlen(parameter)-1;
+      while ((parameter[last]==' ')&&(last>=0)) {
+        parameter[last]='\000';
+        last--;
+        }
+      break;
+    case 2      :       //cannot start with '.'
+      if (parameter[0]=='.') {
+        (void) rou_alert(0,"%s sesid=<%s> wrong fqdn=<%s>"
+                            OPEP,contact->mainsesid,parameter);
+        phase=999;      //wrong parameter
+        }
+      break;
+    case 3      :       //check if we have an fqdn
+      if ((good=isgoodfqdn(contact,parameter))==true)
+        phase=999;
+      break;
+    case 4      :       //is it an enclosed IP
+      if ((parameter[0]=='[')&&(parameter[last]==']')) {
+        parameter[last]='\000';
+        (void) memmove(parameter,parameter+1,last);
+        last--;
+        last--;
+        if (strlen(parameter)==0) {
+          phase=999;
+          }
+        }
+      break;
+    case 5      :       //checking if it is starting or endig with dot
+      if ((parameter[0]=='.')||(parameter[last]=='.'))
+        phase=999;      //yes but not acceptable
+      break;
+    case 6      :       //checking fi double dot inside
+      if (strstr(parameter,"..")!=(char *)0) 
+        phase=999;      //yes but not acceptable
+      break;
+    case 7      :       //are all character good
+      if (strlen(parameter)==strspn(parameter,".0123456789")) 
+        good=true;
+      break;
+    default     :       //SAFE guard
+      if (good==false) {
+        (void) eml_note(contact,NULL,"%d 5.5.4 %s.",BADPAR,DETAIL);
+        (void) eml_transmit(contact,true,"%d 5.5.4 %s.",BADPAR,DETAIL);
+        }
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return good;
+
+#undef  DETAIL
+#undef  OPEP
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to set create the sessid count*/
+/*      file. This file is used to keep track   */
+/*      of the number of the sessionid|(rcpt)   */
+/*      used to send email to all recipient.    */
+/*                                             */
+/************************************************/
+static _Bool setcountfile(CONTYP *contact)
+
+{
+#define OPEP    "lvleml.c:setcountfile,"
+
+_Bool status;
+int num;
+FILE *qfile;
+int phase;
+_Bool proceed;
+
+status=false;
+num=0;
+qfile=(FILE *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //is contact good
+      if ((contact==(CONTYP *)0)||(contact->session==(SESTYP *)0)) {
+        (void) rou_alert(0,"%s contact pointer or session is NULL (bug?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //counting recipient number
+      if ((num=rou_nbrlist((void **)contact->recipients))==0)
+        phase=999;      //No recipient!
+      break;
+    case 2      :       //creating the count file
+      if ((qfile=eml_createqfile(contact->session->sessid,EXTCNT))==(FILE *)0) {
+        (void) rou_alert(0,"%s Unable to open 'count' qfile <%s> (error=<%s>)",
+                            OPEP,contact->session->sessid,strerror(errno));
+        phase=999;
+        }
+      break;
+    case 3      :       //inserting number of receipient
+      if (fprintf(qfile,"%d\n",num)<1) {
+        (void) rou_alert(0,"%s Unable to set 'count' in qfile <%s> (error=<%s>)",
+                            OPEP,contact->session->sessid,strerror(errno));
+        (void) fclose(qfile);
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 4      :       //closing count file
+      if (fclose(qfile)!=0) {
+        (void) rou_alert(0,"%s Unable to close qfile <%s> (error=<%s>)",
+                            OPEP,contact->session->sessid,strerror(errno));
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 5      :       //everything fine
+      status=true;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+#undef  OPEP
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to set RCPT directive to      */
+/*      forward email to SMTP peers.            */
+/*                                             */
+/************************************************/
+static _Bool setdirectives(CONTYP *contact,char *ext)
+
+{
+#define        OPEP    "lvleml.c:setdirectives,"
+
+_Bool status;
+FILE *trans;
+int phase;
+_Bool proceed;
+
+status=false;
+trans=(FILE *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Creating the file
+      if ((trans=eml_createqfile(contact->session->sessid,ext))==(FILE *)0) 
+        phase=999;      //trouble trouble
+      break;
+    case 1      :       //write data to trans file;
+      if (eml_mktransfile(contact,trans)==false) {
+        (void) eml_closeqfile(trans);
+        phase=999;              //Trouble trouble
+        }
+      break;
+    case 2      :       //closing transfile
+      if (eml_closeqfile(trans)<0)
+        phase=999;              //Trouble trouble
+      break;
+    case 3      :       //everythin fine
+      status=true;
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+#undef  OPEP
+return status;
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to send a simple SMTP ccommand*/
+/*      (no need to have the response wording)  */
+/*                                             */
+/************************************************/
+static int simple_smtp_command(RMTTYP *rmt,char *fmt,...)
+
+{
+va_list args;
+char strloc[300];
+
+va_start(args,fmt);
+(void) vsnprintf(strloc,sizeof(strloc),fmt,args);
+va_end(args);
+return tcp_smtp_command(rmt,(char ***)0,strloc);
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to accept EMAIL contents from */
+/*      SMTP peers.                             */
+/*     Return true, if everything is fine      */
+/*                                             */
+/************************************************/
+//NOTE 
+//MAIL FROM <sender> [BODY=7BIT|BODY=8BITMIME] [SIZE=number_of_bytes]
+static _Bool getdata(CONTYP *contact)
+
+{
+#define        OPEP    "lvleml.c:getdata,"
+#define EXTMP   "tmp"
+
+_Bool done;
+FILE *queue;
+_Bool inheader;
+_Bool completed;
+u_long total;
+TIMESPEC start;
+int phase;
+_Bool proceed;
+
+done=false;
+queue=(FILE *)0;
+inheader=true;
+completed=false;
+total=0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //checking if we have recipient
+      if (rou_nbrlist((void **)contact->recipients)==0)  {
+        char *cmt;
+      
+        cmt="RCPT first. transaction protocol command out of sequence";
+        (void) eml_transmit(contact,true,"%d 5.5.0 %s",BADSEQ,cmt);
+        done=true;      //lets say DATA will be issued
+        proceed=false; //No recipients
+        }
+      break;
+    case 1      :       //opening the queue email
+      if ((queue=eml_createqfile(contact->session->sessid,""))==(FILE *)0) 
+        phase=999;      //trouble trouble
+      break;
+    case 2      :       //adding received stamp to file
+      if (addreceived(contact,queue)==false) 
+        phase=999;      //trouble trouble
+      break;
+    case 3      :       //sending 'go ahead' to remote
+      (void) clock_gettime(CLOCK_REALTIME,&start);
+      (void) eml_transmit(contact,true,"%d 3.5.0 %s",
+                                        DATAOK,"End data with <CR><LF>.<CR><LF>");
+      break;
+    case 4      :       //get incoming line, detect 'single dot' as end
+      while (completed==false) {
+        int got;
+        char *line;
+        
+        completed=false;
+        got=tcp_getline(contact->socptr,WAITRMT,&line);
+        if (got<0) {    //data timeout
+          phase=999;    //trouble trouble
+          break;        //exiting loop
+          }
+        total+=got;
+        if (inheader==true) {
+          (void) eml_scan_headerline(contact->session,line);
+          if (strlen(line)==0)
+            inheader=false;
+          }
+        if (strcmp(line,".")==0) {
+          completed=true;
+          }
+        else {
+          if ((strlen(line)>1)&&(line[0]=='.'))
+            (void) memmove(line,line+1,strlen(line));
+          (void) fprintf(queue,"%s\n",line); 
+          }
+        line=rou_freestr(line);
+        }
+      break;
+    case 5      :       //got all data
+      if (eml_closeqfile(queue)<0)
+        phase=999;              //Trouble trouble
+      break;
+    case 6      :       //creating the count file
+      if (setcountfile(contact)==false) 
+        phase=999;      //Unable to create count file???
+      break;
+    case 7      :       //renameing directive
+      contact->session->taille=total;
+      (void) setdirectives(contact,EXTMP);
+      if (eml_renameqfile(contact->session->sessid,EXTMP,EXTRANS)==false)
+        phase=999;              //Trouble trouble
+      break;
+    case 8      :       //everything fine
+      const char *fmt;
+      unsigned int delta;
+      char duration[40];
+
+      total+=1023;
+      total/=1024;    //KBytes
+      delta=rou_getdifftime(&start);
+      (void) snprintf(duration,sizeof(duration),"%d.%03d",delta/1000,delta%1000);
+      contact->session->duration=strdup(duration);;
+      fmt="%d-3.5.3 Session ID=<%s>";
+      (void) eml_transmit(contact,false,fmt,CMDOK,contact->session->sessid);
+      fmt="%d-3.5.3 data stream received: %d Kbytes within %s seconds)";
+      (void) eml_transmit(contact,false,fmt,CMDOK,total,duration);
+      fmt="%d 3.5.3 Message accepted for delivery";
+      (void) eml_transmit(contact,true,fmt,CMDOK,contact->session->sessid);
+      done=true;
+      proceed=false;    //task done
+      break;
+    default     :       //SAFE guard
+      (void) eml_transmit(contact,true,"%d 5.5.4 Server does not accept mail",
+                                        DATRJC);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return done;
+
+#undef  EXTMP
+#undef  OPEP
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to send an "HELO" message     */
+/*     if EHLO message is not accepted.        */
+/*                                             */
+/************************************************/
+static _Bool dohelo(CONTYP *contact,char *parameter)
+
+{
+#define        OPEP    "lvleml.c:dohelo"
+
+_Bool done;
+
+if ((done=isgoodhelo(contact,parameter))==false) {
+  (void) setterminator(contact,"HELO parameter missing");
+  contact->credit-=2;
+  }
+else
+  (void) linkready(contact,false);
+return done;
+#undef  OPEP
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to send an "HELO" message     */
+/*     if EHLO message is not accepted.        */
+/*                                             */
+/************************************************/
+static _Bool doehlo(CONTYP *contact,char *parameter)
+
+{
+static struct   {
+      int display;      //0 ->always, 1-->plain 2-->crypted
+      char *str;        //the message to display
+      }ehlo[]={
+        {1,"STARTTLS"},
+        {1,"AUTH DIGEST-MD5"},
+        {2,"AUTH PLAIN LOGIN DIGEST-MD5"},
+        {0,"SIZE "MXMSIZE},
+        {0,"8BITMIME"},
+        {0,"ENHANCEDSTATUSCODES"},
+        {0,(char *)0}
+        };
+
+#define        OPEP    "lvleml.c:doehlo"
+
+_Bool done;
+int strstart;
+int phase;
+_Bool proceed;
+
+done=false;
+strstart=0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Do we have a parameter
+      if ((done=isgoodhelo(contact,parameter))==false) {
+        (void) setterminator(contact,"HELO parameter incorrect");
+        contact->credit-=2;     //penalty
+        phase=999;      //Trouble trouble
+        }
+      break;
+    case 1      :       //thereis an FQDN
+      (void) linkready(contact,true);
+      if (soc_iscrypted(contact->socptr)==true) 
+        strstart++;
+      for (int i=strstart;ehlo[i].str!=(char *)0;i++) {
+        char space;
+  
+        space='-';
+        if (ehlo[i+1].str==(char *)0)
+          space=' ';
+        switch (ehlo[i].display) {
+          case 0        :       //always display
+            break;
+          case 1        :       //display on plain only
+            if (soc_iscrypted(contact->socptr)==true)
+              continue;
+            break;
+          case 2        :       //display on crypted only
+            if (soc_iscrypted(contact->socptr)==false)
+              continue;
+            break;
+          }
+        (void) eml_transmit(contact,false,"%d%c%s",CMDOK,space,ehlo[i].str);
+        }
+      (void) eml_transmit(contact,true,"");
+      done=true;
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return done;
+#undef  DETAIL
+#undef  OPEP
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to manage a "ORGN:"           */
+/*      command from the SMTP client.           */
+/*      Purpose of this command is to simulate  */
+/*      (or overide) the IP remote client.      */
+/*                                             */
+/*      NOTE:   Only compiled in debug mode.    */
+/*                                             */
+/************************************************/
+#ifdef  MODEDEBUG
+
+static _Bool set_orgn_rmtip(CONTYP *contact,char *rmtip)
+
+{
+#define OPEP    "lvleml.c:set_orgn_rmtip,"
+
+_Bool isok;
+AFNTYP *afn;
+int phase;
+_Bool proceed;
+
+isok=false;
+afn=(AFNTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //is the new IP valide
+      if ((afn=afn_getoneipnum(rmtip))==(AFNTYP *)0) {
+        (void) rou_alert(0,"%s <%s> is not a good ip (Testing?)",OPEP,rmtip);
+        phase=999;
+        }
+      break;
+    case 1      :       //we have good ip, lets disconnect previous
+      if (sql_newconnect(contact->sqlptr,contact->peerip,-1)<0) {
+        (void) rou_alert(0,"%s Unable to update database remotes table (system?)"
+                           OPEP);
+        phase=999;
+        }
+      break;
+    case 2      :  {    //ready to change IP'
+      char *reverse;
+
+      contact->peerip=rou_freestr(contact->peerip);
+      contact->peerip=strdup(rmtip);
+      reverse=afn_reversipnum(afn);
+      contact->peername=rou_freestr(contact->peername);
+      contact->peername=dns_get_reverse_addr(reverse);
+      reverse=rou_freestr(reverse);
+      }
+      break;
+    case 3      :       //adjust privilege according new IP
+      switch (contact->privilege) {
+        case rel_authentic  :
+          break;
+        case rel_isrelay    :   //need to force rel_plain
+          contact->privilege=rel_plain;
+          //NO BREAK;
+        case rel_plain      :
+          if (afn_is_ip_relayable(contact->peerip,contact->relayok)==true)
+            contact->privilege=rel_isrelay;     //IP is relayable
+          break;
+        default             :
+          (void) rou_alert(0,"%s unexpected privilege '%d' (Bug?)",
+                              OPEP,contact->privilege);
+          phase=999;
+          break;
+        }
+      break;
+    case 4      :       //let update links
+      if (sql_newconnect(contact->sqlptr,rmtip,1)!=1) {
+        static char *end;
+
+        end="You have another connection still in progress";
+        (void) rou_alert(0,"%s <%s> Too many '%d' links! (Testing?)",
+                            OPEP,rmtip,sql_newconnect(contact->sqlptr,rmtip,0));
+        (void) eml_transmit(contact,true,"%d contact denied <%s>",NOTNOW,end);
+        contact->termend=rou_freestr(contact->termend);
+        contact->termend=strdup(end);
+        contact->credit=-1;     //small penalty
+        (void) sleep(1);        //To avoid avalanche
+        (void) sql_newconnect(contact->sqlptr,rmtip,-1);
+        phase++;        //connection rejected
+        }
+      break;
+    case 5      :       //everything is now fine
+      (void) signon(contact);
+      isok=true;
+      break;
+    default     :       //SAFE Guard
+      afn=afn_freeipnum(afn);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return isok;
+
+#undef  OPEP
+}
+#endif
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to manage a "MAIL FROM:"      */
+/*      ommand from the SMTP client.            */
+/*                                             */
+/************************************************/
+static _Bool checkfrom(CONTYP *contact,char *mailfrom)
+
+{
+#define OPEP    "lvleml.c:checkfrom"
+
+_Bool success;
+char *strsize;
+int status;
+int phase;
+_Bool proceed;
+
+success=false;
+strsize=(char *)0;
+status=CMDOK;
+proceed=true;
+phase=0;
+while (proceed==true) {
+  (void) rou_dbglive(9,OPEP,"Phase='%d'",phase);
+  switch (phase) {
+    case 0      :       //do we have an originator
+      if ((mailfrom==(char *)0)||(strlen(mailfrom)<3)) {
+        (void) eml_transmit(contact,true,"%d 5.5.0 <%s> originator not specified",
+                                          BADPAR,mailfrom);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 1      :       //do we have already a from
+      if (contact->mailfrom!=(char *)0) {
+        (void) eml_transmit(contact,true,"%d 5.5.1 '%s' %s",
+                                          BADPAR,contact->mailfrom,
+                                          "was previously defined as originator");
+        phase=999;      //no need to go further
+        }
+      break;
+    case 2      :       //do we have SIZE= component
+      if ((strsize=strchr(mailfrom,' '))!=(char *)0) {
+        while (*strsize==' ') {
+          *strsize='\000';
+          strsize++;
+          }
+        //JMPDBG need to check size
+        }
+      break;
+    case 3      :       //check from format
+      if ((mailfrom[0]!='<')||(mailfrom[strlen(mailfrom)-1]!='>')) {
+        (void) eml_transmit(contact,true,"%d 5.5.3 '%s' bad Format error",
+                                          BADPAR,mailfrom);
+        phase=999;      //no need to go further
+        }
+      mailfrom[strlen(mailfrom)-1]='\000'; 
+      (void) memmove(mailfrom,mailfrom+1,strlen(mailfrom));
+      break;
+    case 4      :       //check if contact authenticated
+      switch (contact->privilege) {
+        case rel_isrelay        :       //Remote IP was already relaying
+          //NO BREAK;                     
+        case rel_authentic      :       //User was authenticated
+          break;
+        case rel_plain          :       //remote IP is plain
+          if ((status=checkcredit(contact))!=CMDOK) {
+            contact->credit=-1;         //found bad guy.
+            (void) eml_transmit(contact,true,"%d 5.5.4 Closing connection",status);
+            phase=999;      //bad credit not need to go further
+            }
+          break;
+        default :
+          (void) rou_alert(0,"%s unexpected priviliged='%d' (Bug?)",
+                              OPEP,contact->privilege);
+          (void) eml_transmit(contact,true,"%d 5.5.5 Closing connection",FAILED);
+          phase=999;      //bad credit not need to go further
+          break;
+        }
+      break;
+    case 5      :       //Checking if the SPF is good from the originator
+      contact->fromspf=spf_getspf(mailfrom,contact->peerip);
+      (void) rou_dbglive(5,OPEP,"found SPF for <%s/[%s]> to be <%s>",
+                                  mailfrom,contact->peerip,
+                                  spf_spfASCII(contact->fromspf));
+      break;
+    case 6      :       //everything ok
+      contact->mailfrom=strdup(mailfrom);
+      (void) eml_transmit(contact,true,"%d 2.1.3 %s sender ok",
+                                        CMDOK,contact->mailfrom);
+      success=true;
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return success;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to manage a "RCPT TO:"        */
+/*      command from the SMTP client.            */
+/*                                             */
+/************************************************/
+static _Bool checkto(CONTYP *contact,char *rcptto)
+
+{
+#define OPEP     "lvleml.c:checkto,"
+_Bool success;
+RCPTYP *neu;
+const char *detail;
+char *report;
+_Bool proceed;
+int phase;
+
+success=false;
+neu=(RCPTYP *)0;
+detail="Address will be processed";
+report=(char *)0;
+proceed=true;
+phase=0;
+while (proceed==true) {
+  (void) rou_dbglive(9,OPEP,"phase='%d' rcptto=<%s>",phase,rcptto);
+  switch (phase) {
+    case 0      :       //do we have a mailfrom
+      if ((contact->mailfrom==(char *)0)||(strlen(contact->mailfrom)==0)) {
+        (void) eml_transmit(contact,true,"%d 5.6.0 Bad sequence of commands.",
+                                          BADSEQ);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 1      :       //do we have an originator
+      if ((rcptto==(char *)0)||(strlen(rcptto)==0)) {
+        (void) eml_note(contact,rcptto,"%d 5.6.1 recipient not specified",BADPAR);
+        (void) eml_transmit(contact,true,"%d 5.6.1 recipient not specified",BADPAR);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 2      :       //check rcpt format
+      if ((rcptto[0]!='<')||(rcptto[strlen(rcptto)-1]!='>')) {
+        (void) eml_note(contact,rcptto,"%d 5.6.2 '%s' bad Format error",
+                                        BADPAR,rcptto);
+        (void) eml_transmit(contact,true,"%d 5.6.2 '%s' bad Format error",
+                                          BADPAR,rcptto);
+        phase=999;      //no need to go further
+        }
+      rcptto[strlen(rcptto)-1]='\000'; 
+      (void) memmove(rcptto,rcptto+1,strlen(rcptto));
+      break;
+    case 3      :       //checking rcptto format
+      neu=eml_isemailok(rcptto,&report);
+      if (neu==(RCPTYP *)0) {
+        (void) eml_note(contact,rcptto,"%d 5.6.3 %s",NOTEML,report);
+        (void) eml_transmit(contact,true,"%d 5.6.3 %s",NOTEML,report);
+        report=rou_freestr(report);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 4      :       //Do we have a domain MX
+      if (setlocdom(contact,neu)==false) {
+        (void) eml_note(contact,rcptto,"%d 5.6.4 %s (domain=%s)",
+                                        MISSMX,
+                                        "No valid MX found for recipient"
+                                        " domain name",
+                                        neu->domain);
+        (void) eml_transmit(contact,true,"%d 5.6.4 %s (domain=%s)",
+                                          MISSMX,
+                                          "No valid MX found for recipient"
+                                          " domain name",
+                                        neu->domain);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 5      :       //if the local user acceptable
+      switch (neu->code) {
+        case 'L'        :       //local user/domain
+          if (is_user_good(contact,rcptto)==false) 
+            phase=999;
+          break; 
+        case 'R'        :       //remote user/domain
+          if (is_remote_good(contact,neu)==false) 
+            phase=999;
+          break; 
+        default         :
+          (void) eml_transmit(contact,true," 5.6.5 %s (domain=%s,code='%c')",
+                                            FAILED,
+                                            "system error with domain status",
+                                            neu->domain,neu->code);
+          phase=999;      //no need to go further
+          break;
+        }
+      break;
+    case 6      :       //Storing rcpt to
+      if (eml_addrecipient(&(contact->recipients),neu)==false) {
+        detail="duplicate recipients will be consolidated";
+        neu=eml_freerecipient(neu);
+        }
+      break;
+    case 7      :       //everything ok
+      (void) eml_note(contact,rcptto,"%d 5.6.7 %s <%s>",CMDOK,detail,rcptto);
+      (void) eml_transmit(contact,true,"%d 5.6.7 %s <%s>",CMDOK,detail,rcptto);
+      success=true;
+      break;
+    default     :       //SAFE guard
+      if (success==false) 
+        neu=eml_freerecipient(neu);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return success;
+#undef  OPEP
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to reset the current session  */
+/*                                             */
+/************************************************/
+static _Bool doreset(CONTYP *contact,char *parameter)
+
+{
+#define LOCSEQ  "2.1.0"
+
+(void) eml_transmit(contact,true,"%d-%s flushed session %s",
+                                  CMDOK,LOCSEQ,contact->session->sessid);
+(void) freesessid(contact);
+contact->numreset++;
+(void) getsessid(contact);
+contact->recipients=(RCPTYP **)rou_freelist((void **)(contact->recipients),
+                                            (genfree_t)eml_freerecipient);
+contact->mailfrom=rou_freestr(contact->mailfrom);
+(void) eml_transmit(contact,true,"%d %s opening new session %s",
+                                  CMDOK,LOCSEQ,contact->session->sessid);
+return true;
+
+#undef  LOCSEQ
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to send a confirmation ehlo on the    */
+/*      crypted link.                                   */
+/*                                                      */
+/********************************************************/
+_Bool sendehlo(RMTTYP *rmt)
+
+{
+_Bool done;
+
+done=false;
+return done;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to send ehlo (or helo) to remote      */
+/*      MX server.                                      */
+/*      Return true if succesfull                       */
+/*                                                      */
+/********************************************************/
+static _Bool greetings_rmt(RMTTYP *rmt)
+
+{
+#define OPEP    "lvleml.c:greetings_rmt,"
+
+_Bool done;
+int phase;
+_Bool proceed;
+
+done=false;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  int rspcode;
+
+  rspcode=ERRPROC; 
+  switch (phase) {
+    case 0      :       //Sending EHLO
+      rspcode=simple_smtp_command(rmt,"EHLO %s",rmt->orgdomain);
+      switch (rspcode) {
+        case CMDOK      :       //So fare, so good
+          break;
+        default         :       //Trouble
+          (void) rou_alert(0,"%s EHLO Remote <%s> unexpected answer code '%d'",
+                              OPEP,rmt->curmx,rspcode);
+          phase++;              //No STARTTLS available
+          break;
+        }
+      break;
+    case 1      :       //Starting starttls
+      phase=999;        //No need to go futher
+      rspcode=simple_smtp_command(rmt,"STARTTLS",rmt->orgdomain);
+      switch (rspcode) {
+        case SIGNON      :       //link is now encrypted
+          done=soc_starttls(rmt->socptr,false);
+          switch (done) {
+            case true   :  {
+              int level;
+
+              level=soc_get_sec_level(rmt->socptr);
+              (void) log_fprintlog(rmt->logptr,false,cry,"client",level);
+              (void) usleep(10000);     //10 ms delay
+              rspcode=simple_smtp_command(rmt,"EHLO %s",rmt->orgdomain);
+              switch (rspcode) {
+                case CMDOK      :
+                  break;
+                default         :
+                  (void) log_fprintlog(rmt->logptr,false,"No server confirmation");
+                  done=false;
+                  break;
+                }
+              }
+              break;
+            case false  :
+              (void) log_fprintlog(rmt->logptr,false,"Unable to set crypted link");
+              break;
+            }
+          phase=999;            //link is now established
+          break;
+        default         :       //Trouble
+          (void) rou_alert(0,"%s Unable to establish crypted link with <%s>",
+                              OPEP,rmt->curmx);
+          break;
+        }
+      break;
+    case 2      :       //Sending HELO
+      rspcode=simple_smtp_command(rmt,"HELO %s",rmt->orgdomain);
+      switch (rspcode) {
+        case CMDOK      :       //So fare, so good
+          done=true;
+          phase=999;
+          break;
+        default         :       //Trouble
+          (void) rou_alert(0,"%s, HELO Remote <%s> unexpected answer code '%d'",
+                              OPEP,rmt->curmx,rspcode);
+          break;
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return done;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to close the remote connection with MX*/
+/*                                                      */
+/********************************************************/
+static RMTTYP *close_mx(RMTTYP *rmt)
+
+{
+#define OPEP    "lvleml.c:close_mx,"
+
+int phase;
+_Bool proceed;
+
+phase=0;
+proceed=(rmt!=(RMTTYP *)0);
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //need to disconnect
+      if (rmt->socptr!=(SOCPTR *)0) {
+        char **resp;
+        int rspcode;
+
+        resp=(char **)0;
+        rspcode=tcp_smtp_command(rmt,&resp,"QUIT");
+        switch (rspcode) {
+          case QUITOK   :       //Everything fine
+            break;
+          default       :
+            (void) rou_alert(0,"%s Unexpected QUIT status='%d' (check code!)",
+                                OPEP,rspcode);
+            break;
+          }
+        resp=(char **)rou_freelist((void **)resp,(genfree_t)rou_freestr);
+        rmt->socptr=soc_closefeedsock(rmt->socptr);
+        }
+      break;
+    case 1      :       //free memory
+      rmt->mxs=dns_freemxlist(rmt->mxs);
+      rmt->orgdomain=rou_freestr(rmt->orgdomain);
+      rmt->dstdomain=rou_freestr(rmt->dstdomain);
+      (void) free(rmt);
+      rmt=(RMTTYP *)0;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return rmt;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to connect to the remote SMTP server  */
+/*                                                      */
+/********************************************************/
+static RMTTYP *connect_mx(LOGPTR *logptr,TRATYP *tra)
+
+{
+#define OPEP    "lvleml.c:connect_to_mx,"
+
+_Bool done;
+char *orgdomain;
+char *dstdomain;
+MXTYP **mxs;
+RMTTYP *rmt;
+int phase;
+int proceed;
+
+done=false;
+orgdomain=strrchr(tra->sfrom,'@');
+dstdomain=strrchr(tra->rcptto,'@');
+mxs=(MXTYP **)0;
+rmt=(RMTTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //preparing RMT record
+      if ((orgdomain==(char *)0)||(dstdomain==(char *)0)) {
+        char cmt[100];
+
+        (void) snprintf(cmt,sizeof(cmt),"from <%s> or rcpt <%s>, without domain",
+                                        tra->sfrom,tra->rcptto);
+        (void) log_fprintlog(rmt->logptr,false,cmt);
+        (void) rou_alert(0,"%s  %s (Bug!)",OPEP,cmt);
+        phase=999;
+        }
+      break;
+    case 1      :       //looking for MX list
+      orgdomain++;
+      dstdomain++;
+      mxs=dns_getmx(dstdomain);
+      if (mxs==(MXTYP **)0) {
+        (void) log_fprintlog(rmt->logptr,false,"NO MX found for domain <%s>",
+                                                dstdomain);
+        phase=999;
+        }
+      break;
+    case 2      :       //creating rmt record
+      rmt=(RMTTYP *)calloc(1,sizeof(RMTTYP));
+      rmt->logptr=logptr;
+      rmt->mxs=mxs;
+      rmt->orgdomain=strdup(orgdomain);
+      rmt->dstdomain=strdup(dstdomain);
+      while (*mxs!=(MXTYP *)0) {
+        rmt->curmx=*mxs;
+        (void) rou_alert(0,"%s JMPDBG trying connect to <%s>",
+                            OPEP,rmt->curmx->mxname);
+        rmt->socptr=soc_openfeedsock(pro_smtp,(char *)0,rmt->curmx->mxname,EMLPORT);
+        if (rmt->socptr!=(SOCPTR *)0) { 
+          char cmt[100];
+
+          (void) snprintf(cmt,sizeof(cmt),"Now Connected to MX <%s>",
+                                          rmt->curmx->mxname);
+          (void) log_fprintlog(rmt->logptr,false,cmt);
+          break;        //MX available found
+          }
+         mxs++;
+        }
+      if (rmt->socptr==(SOCPTR *)0) {
+        (void) rou_alert(0,"%s Found NO MX available for domain <%s>",
+                            OPEP,dstdomain);
+        rmt=close_mx(rmt);      //closing remote connection
+        phase=999;              //No NX found!
+        }
+      break;
+    case 3      :       //waiting MX signon
+      int res;
+
+      res=tcp_get_smtp_reply(rmt,WAITRMT,(char ***)0);
+       switch (res) {
+        case SIGNON     :       //everything fine
+          break;
+        default         :       //not getting the right signon
+          rmt=close_mx(rmt);    //closing remote connection
+          phase=999;            //No need to go further
+          break;
+        }
+      break;
+    case 4      :
+      if ((done=greetings_rmt(rmt))==false)
+        rmt=close_mx(rmt);    //closing remote connection
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return rmt;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to reset the remote session           */
+/*                                                      */
+/********************************************************/
+static _Bool reset_mx(RMTTYP *rmt)
+
+{
+#define OPEP    "lvleml.c:reset_mx,"
+
+_Bool ok;
+int rspcode;
+char **resp;
+
+ok=false;
+resp=(char **)0;
+rspcode=tcp_smtp_command(rmt,&resp,"RSET");
+switch (rspcode) {
+  case CMDOK    :       //Everythin fine
+    ok=true;
+    break;
+  default       :
+    (void) rou_alert(0,"%s Unexpected code='%d' (Code?)",OPEP,rspcode);
+    for (int i=0;resp[i]!=(char *)0;i++)
+      (void) rou_alert(0,"%s resp[%d]=<%s>",OPEP,i,resp[i]);
+    break;
+  }
+resp=(char **)rou_freelist((void **)resp,(genfree_t)rou_freestr);
+return ok;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to send the data to the remote server */
+/*                                                      */
+/********************************************************/
+static void senddata(RMTTYP *rmt,TRATYP **tosend)
+
+{
+#define OPEP    "lvleml.c:senddata,"
+
+int rspcode;
+int sent;
+TIMESPEC start;
+char *sessid;
+char **resp;
+int phase;
+_Bool proceed;
+
+rspcode=0;
+sent=0;
+sessid=(*tosend)->sessid;
+resp=(char **)0;
+phase=0;
+proceed=true;
+(void) clock_gettime(CLOCK_REALTIME,&start);
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Sendig data request
+      rspcode=simple_smtp_command(rmt,"DATA");
+      switch (rspcode) {
+        case DATAOK     :       //data accepted
+          break;
+        default         :       //Unexpected return code
+          phase=999;            //Not accepting data
+          break;
+        }
+      break;
+    case 1      :       //sending session data
+      sent=tcp_send_smtp_data(rmt,sessid);
+      if (sent<0)
+        phase=999;      //No need to wait form response code
+      break;
+    case 2      :       //send the text termination
+      rspcode=tcp_get_smtp_reply(rmt,WAITRMT,&resp);
+      break;
+    case 3      :       //sending session data
+      const char *fmt;
+      unsigned int delta;
+
+      sent+=1023;
+      sent/=1024;       //Sent now in KByes
+      fmt="Data stream Sent: %d Kbytes within %d.%03d seconds";
+      delta=rou_getdifftime(&start);
+      (void) log_fprintlog(rmt->logptr,false,fmt,sent,delta/1000,delta%1000);
+      switch (rspcode) {
+        case CMDOK      :
+          const char *cmt;
+      
+          cmt="Transmission completed successfully";
+          (void) log_fprintlog(rmt->logptr,false,cmt);
+          break;
+        case MALABRT    :       //Data rejected
+          break;
+        default         :       //Unexpected return code
+          break;
+        }
+      break;
+    case 4      :      //Updating sending
+      TRATYP **dest;
+
+      dest=tosend;
+      while (*dest!=(TRATYP *)0) {
+        switch ((*dest)->sendcode) {
+          case CMDOK    :
+            (void) eml_duptra_resp(*dest,resp);
+            if (rspcode==CMDOK) {
+              (*dest)->code='C';  //Data was sent properly
+              }
+            break;
+          case UKNUSER  :       //NO BREAK
+          case BADPAR   :
+            break;
+          default       :
+            (void) rou_alert(0,"%s sessid=<%s>, Unexpected code='%d' (Bug?)",
+                                OPEP,sessid,rspcode);
+            break;
+          }
+        dest++;
+        }
+      break;
+    default     :       //SAFE Guard            
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+resp=(char **)rou_freelist((void **)resp,(genfree_t)rou_freestr);
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to send one email session to intended */
+/*      recipient.                                      */
+/*                                                      */
+/********************************************************/
+static void send_to_mx(RMTTYP *rmt,TRATYP **tosend)
+
+{
+#define OPEP    "lvleml.c:send_to_mx,"
+
+int tobesend;
+char *sessid;
+TRATYP **dest;
+
+tobesend=0;
+sessid=(*tosend)->sessid;
+dest=tosend;
+while (*dest!=(TRATYP *)0) {
+  int rspcode;
+  char ***resp;
+
+  (void) eml_freetra_resp(*dest);
+  resp=&((*dest)->resp);
+  rspcode=tcp_smtp_command(rmt,resp,"RCPT TO: <%s>",(*dest)->rcptto);
+  switch (rspcode) {
+    case CMDOK    :       //originator accepted
+      tobesend++;
+      (*dest)->sendcode=rspcode;
+      break;
+    default       :       //Not accepted recipient
+      (void) rou_alert(0,"%s sessid=<%s>, unknwon code='%d' (Bug?)",
+                          OPEP,sessid,rspcode);
+      //NO BREAK
+   case UKNUSER  :       //recipient is unknown
+   case NORELAY  :       //email no relayed
+     (*dest)->code='W';  //Need to send a Warning
+     (*dest)->sendcode=rspcode;
+     break;
+   }
+ dest++;
+ }
+if (tobesend>0) 
+  (void) senddata(rmt,tosend);
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to send the whole email transaction   */
+/*                                                      */
+/********************************************************/
+static void sending_email(LOGPTR *logptr,TRATYP **tra)
+
+{
+#define OPEP    "lvleml.c:sending_email,"
+
+time_t isnow;
+char *sessid;
+RMTTYP *rmt;
+TRATYP **tosend;
+int phase;
+_Bool proceed;
+
+isnow=time((time_t *)0);
+sessid=(char *)0;
+rmt=(RMTTYP *)0;
+tosend=(TRATYP **)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //do we have a email request
+      if ((tra)==(TRATYP **)0) {
+        (void) rou_alert(0,"%s No email to send to remote! (Bug?)",OPEP);
+        phase=999;      //all scanning done
+        }
+      break;
+    case 1      :       //process completed?
+      if ((*tra)==(TRATYP *)0)
+        phase=999;      //all scanning done
+      break;
+    case 2      :       //do a remote reset if needed
+      if (rmt!=(RMTTYP *)0) {
+        if (reset_mx(rmt)==false) {
+          rmt=close_mx(rmt);
+          phase=999;    //Trouble Trouble
+          }
+        }
+      break;
+    case 3      :       //collecting email to be sent
+      sessid=(*tra)->sessid;
+      while (*tra!=(TRATYP *)0) {
+        if (strcmp((*tra)->sessid,sessid)!=0) {
+          sessid=(*tra)->sessid;
+          break;
+          }
+        if (((*tra)->date+(*tra)->delay)<=isnow) {
+          switch ((*tra)->code) {
+            case 'R'    :       //remote email (MX is remote)
+              (void) eml_add_delay(isnow,*tra);
+              tosend=(TRATYP **)rou_addlist((void **)tosend,(void *)(*tra));
+              break;
+            default     :       //Status is not to be send
+              (void) rou_alert(0,"%s Directive is not! remote email (Bug?)",OPEP);
+              break;
+            }
+          }
+        tra++;
+        }
+      break;
+    case 4      :       //Checking if we have email to be sent
+      if (tosend==(TRATYP **)0) 
+        phase=0;       //lets continue to see other email
+      break;
+    case 5      :       //is MX link already open??
+      if (rmt!=(RMTTYP *)0)
+        phase++;         //No need to open MX link (again);
+      break;
+    case 6      :       //Opening connection if not open
+      if ((rmt=connect_mx(logptr,*tosend))==(RMTTYP *)0) {
+        TRATYP **ptr;
+        char cmt[100];
+        char note[150];
+
+        ptr=tosend;
+        (void) snprintf(cmt,sizeof(cmt),"Unable to contact ANY MX to reach <%s>",
+                                         (*tosend)->rcptto); 
+        (void) log_fprintlog(logptr,false,cmt);
+        (void) snprintf(note,sizeof(note),"%d %s",NOANSWR,cmt);
+        while (*ptr!=(TRATYP *)0) {
+          (void) eml_freetra_resp(*ptr);
+          (*ptr)->resp=(char **)rou_addlist((void **)(*ptr)->resp,
+                                            (void *)strdup(note));
+          ptr++;
+          }
+        (void) free(tosend);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 7      :  {    //sending originator
+      int rspcode;
+
+      rspcode=simple_smtp_command(rmt,"MAIL FROM: <%s>",(*tosend)->sfrom);
+      switch (rspcode) {
+        case CMDOK      :       //originator accepted
+          break;
+        default         :       //unexpected answer
+          (void) rou_alert(0,"%s JMPDBG unexpected response code='%d'",
+                              OPEP,rspcode);
+          phase+=2;     //not sending data
+          break;
+        }
+      break;
+      }
+    case 8      :       //sending recipient list
+      (void) send_to_mx(rmt,tosend);
+      (void) free(tosend);
+      tosend=(TRATYP **)0;
+      phase=0;          //Trying to send NEXT sequence of email to be sent
+      break;
+    default     :       //SAFE Guard
+      rmt=close_mx(rmt);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to transmit a string to the remot peer*/
+/*                                                      */
+/********************************************************/
+PUBLIC void eml_transmit(CONTYP *contact,_Bool flush,const char *fmt,...)
+
+{
+va_list args;
+char *line;
+
+va_start(args,fmt);
+line=(char *)0;
+if (rou_vasprintf(&line,fmt,args)>0) {
+  (void) log_fprintlog(contact->logptr,true,"%s",line);
+  contact->transout=tcp_addline(contact->transout,line); 
+  }
+if (flush==true) {
+  (void) tcp_write(contact->socptr,contact->transout);
+  contact->transout=rou_freestr(contact->transout);
+  }
+line=rou_freestr(line);
+va_end(args);
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to report the contact ongoing */
+/*      informationn. store this information    */
+/*      within the database.                    */
+/*                                             */
+/************************************************/
+PUBLIC void eml_note(CONTYP *contact,char *rcpt,const char *fmt,...)
+
+{
+#define OPEP    "lvleml.c:eml_status"
+
+va_list args;
+char **resp;
+char *report;
+ACTTYP action;
+
+va_start(args,fmt);
+resp=(char **)0;
+(void) rou_vasprintf(&report,fmt,args);
+resp=(char **)rou_addlist((void **)resp,(void *)report);
+(void) memset(&action,'\000',sizeof(ACTTYP));
+action.code='X';
+action.sessid=contact->session->sessid;
+action.reverse=contact->peername;
+action.remoteip=contact->peerip;
+if (contact->mailfrom!=(char *)0)
+  action.sfrom=contact->mailfrom;
+if (contact->session->hfrom!=(char *)0)
+  action.hfrom=contact->session->hfrom;
+if (contact->session->hsubject!=(char *)0)
+  action.hsubject=contact->session->hsubject;
+action.rcptto=rcpt;
+action.resp=resp;
+if (sql_mngact(contact->sqlptr,&action)==false)
+  (void) rou_alert(0,"%s, unable to store report within database (bug?",OPEP);
+resp=(char **)rou_freelist((void **)resp,(genfree_t)rou_freestr);
+va_end(args);
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to proceed with peer exchange         */
+/*      5 return values:                                */
+/*         1 exiting via "quit"                         */
+/*         0 contact timeout                            */
+/*        -1 signal received                            */
+/*        -2 remote disconnected                        */
+/*        -3 Multiple remote connection                 */
+/*        -4 BUG!                                       */
+/*                                                      */
+/********************************************************/
+PUBLIC int eml_docontact(CONTYP *contact)
+
+{
+#define OPEP    "lvleml.c:eml_docontact"
+
+int status;
+int got;
+int penalty;    //answer time penalty, in case of wrong auth
+_Bool proceed;
+
+status=1;
+got=0;
+penalty=1;
+proceed=true;
+(void) signon(contact);
+while (proceed==true) {
+  char *line;
+  CODTYP code;
+
+  line=(char *)0;
+  got=tcp_getline(contact->socptr,contact->delay,&line);
+  if (got<=0) {       //timeout or trouble?
+    char str[100];
+
+    switch (got) {
+      case  0   :       //timeout
+        (void) snprintf(str,sizeof(str),"No data from remote within %d sec",
+                                        contact->delay); 
+        break;
+      case -1   :       //signal received
+        (void) snprintf(str,sizeof(str),"Signal Received"); 
+        break;
+      case -2   :       //contact lost
+        (void) snprintf(str,sizeof(str),"Lost link with remote"); 
+        break;
+      default   :
+        (void) snprintf(str,sizeof(str),"Unexpected status='%d' (Bug?)",status); 
+        status=-3;
+        break;
+      }
+    (void) setterminator(contact,str);
+    //(void) rou_alert(0,"%s exit status='%d'",OPEP,status);
+    break;              //no need to go further
+
+    }
+  (void) log_fprintlog(contact->logptr,false,"%s",line);
+  code=eml_getcode(line);
+  switch (code) {
+    case c_data         :       //Peer request to transfer email corps.
+      proceed=getdata(contact);
+      break;
+    case c_helo         :       //HELO SMTP protocol
+      proceed=dohelo(contact,line);
+      break;
+    case c_ehlo         :       //EHLO SMTP protocol
+      proceed=doehlo(contact,line);
+      break;
+    case c_help         :       //HELP SMTP protocol
+      (void) eml_transmit(contact,true,"%d 2.0.1 see https://datatracker.ietf.org"
+                                        "/doc/html/rfc5321",
+                                        GOTHELP);
+      (void) sleep(1);          //avoiding abuse
+      break;
+    case c_noop         :       //No Operation
+      (void) eml_transmit(contact,true,"%d 2.0.0 OK, %s",    
+                                       CMDOK,contact->mainsesid);
+      break;
+    case c_quit         :       //QUIT SMTP protocol
+      contact->credit++;        //clean disconnect
+      (void) setterminator(contact,"QUIT received");
+      (void) eml_transmit(contact,true,"%d 2.0.0 Bye, closing connection CNT=%s",
+                                        QUITOK,contact->mainsesid);
+      status=1;                 //every thing fine
+      proceed=false;
+      break;
+    case c_mail         :       //MAIL FROM: checking originator
+      proceed=checkfrom(contact,line);
+      break;
+#ifdef  MODEDEBUG
+    case c_orgn         :       //Debug mode to override remote IP
+      proceed=set_orgn_rmtip(contact,line);
+      break;
+#endif
+    case c_auth         :       //Auth request
+      if (getauth(contact,line)==false)
+        proceed=false;          //Authentication failed
+      break;
+    case c_rcpt         :       //Doing rpt scanning
+      if (checkto(contact,line)==false) {
+        (void) sleep(penalty);  //relaxing bad guys
+        penalty*=2;
+        }
+      break;
+    case c_rset         :       //Doing session reset
+      proceed=doreset(contact,line);
+      break;
+    case c_starttls     :       //EHLO start encrypted link in server mode
+      (void) usleep(100000);
+      (void) soc_purge(contact->socptr,contact->peerip);
+      (void) eml_transmit(contact,true,"%d 2.0.0 Ready to start TLS",SIGNON);
+      switch (soc_starttls(contact->socptr,true)) {
+        case true       :  {    //link now in TLS crypted mode (server mode)
+          int level;
+
+          level=soc_get_sec_level(contact->socptr);
+          (void) log_fprintlog(contact->logptr,false,cry,"server",level);
+          }
+          break;
+        case false      :       //unable to establish link
+          (void) setterminator(contact,"starttls not successful");
+          (void) eml_transmit(contact,true,"%d 5.3.3 command starttls not "
+                                           "successful",CMDBAD);
+          status=-1;
+          proceed=false;
+          break;
+        }
+      break;
+    case c_unknown      :       //unknown keyword
+      contact->credit-=2;       //Trying to confuse server?
+      (void) rou_alert(0,"SMTP Command <%s> from [%s] is unknown (config?)",
+                         line,contact->peerip);
+      (void) eml_transmit(contact,true,"%d-5.5.1 Unrecognized command, "
+                                       "see RFC 5321",CMDBAD);
+      (void) eml_transmit(contact,true,"%d-5.5.1 https://www.rfc-editor.org",
+                                        CMDBAD);
+      (void) eml_transmit(contact,true,"%d 5.5.1 session %s is still running",
+                                        CMDBAD,contact->mainsesid);
+      break;
+    default             : 
+      contact->credit-=10;      //Trying to bug server
+      (void) rou_alert(0,"%s Unable to find entry for code='%d' (Bug?)",OPEP,code);
+      (void) eml_transmit(contact,true,"%d-5.5.1 Unrecognized command, "
+                                       "see RFC 5321",CMDBAD);
+      (void) eml_transmit(contact,true,"%d 2.0.0 Bug!, closing connection "
+                                       "Immediatly (%s)",    
+                                         QUITOK,contact->mainsesid);
+      status=-1;                 //remote is a trouble maker
+      status=-3;
+      proceed=false;
+      break;
+    }
+  line=rou_freestr(line);
+  }
+return status;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to wait for a remote client.          */
+/*      return all reference to the established contact.*/
+/*                                                      */
+/********************************************************/
+PUBLIC CONTYP *eml_getcontact(SOCPTR *socptr,int pos)
+
+{
+#define OPEP    "lvleml.c:eml_getcontact"
+#define MXCARIN 200             //maximun number of char 
+                                //within carpile
+#define MXDELAY 300             //maximun waiting time 5 minutes standard delay
+
+CONTYP *contact;
+int phase;
+_Bool proceed;
+
+contact=(CONTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  (void) rou_dbglive(9,OPEP,"phase='%d'",phase);
+  switch (phase){
+    case 0      :       //check for binding
+      if (socptr==(SOCPTR *)0) {
+        (void) rou_alert(0,"%s socket pointer is NULL (Bug!?)",OPEP);
+        phase=999;      //not going further
+        }
+      break;
+    case 1      :       //connecting to database
+      contact=(CONTYP *)calloc(1,sizeof(CONTYP));
+      contact->sqlptr=sql_opensql();
+      contact->credit=0;
+      if (contact->sqlptr==(SQLPTR *)0) {
+        (void) rou_alert(0,"%s Unable to contact database",OPEP);
+        (void) sleep(2);//delay to avoid avalanche
+        (void) eml_dropcontact(contact);
+        contact=(CONTYP *)0;
+        phase=999;      //no contact possible.
+        }
+      break;
+    case 2      :       //loading the relayable IP list
+      contact->relayok=eml_load_relayed(getenv(RELAYS));
+      break;
+    case 3      :       //waiting from contact
+      if ((contact->socptr=soc_accept(socptr,pos))==(SOCPTR *)0) {
+        (void) rou_alert(3,"%s Unable to open contact",OPEP);
+        contact=freecontact(contact);
+        phase=999;      //no contact 
+        }
+      break;
+    case 4      :       //Preparing contact
+      contact->mainsesid=eml_getmainsesid();
+      contact->delay=MXDELAY;
+      contact->privilege=rel_plain;     //Standard connection
+      if (debug>1) 
+        contact->delay/=10;             //30 sec in debug mode
+      (void) getsessid(contact);
+      contact->locname=soc_getaddrinfo(contact->socptr,true,true);
+      contact->locip=soc_getaddrinfo(contact->socptr,true,false);
+      if (contact->locip!=(char *)0) {
+        char *ptr;
+        
+        //extracting service port number
+        if ((ptr=strrchr(contact->locip,'|'))!=(char *)0) {
+          *ptr='\000';
+          contact->locserv=strdup(ptr+1);
+          }
+        }
+      contact->peername=soc_getaddrinfo(contact->socptr,false,true);
+      contact->peerip=soc_getaddrinfo(contact->socptr,false,false);
+      contact->logptr=log_openlog(contact->mainsesid,(const char *)0);
+      contact->localafns=afn_getipnums(contact->locip);
+      (void) rou_alert(0,"Contact from peer <%s> to port <%s> started",
+                          contact->peerip,contact->locserv);
+      break;
+    case 5      :       //check contact validity
+      if ((contact->locname==(char *)0)||(contact->peerip==(char *)0)) {
+        (void) rou_alert(0,"%s Unable to establish contact entities",OPEP);
+        contact=freecontact(contact);
+        phase=999;      //no identity
+        }
+      break;
+    case 6      :       //checking if remote IP is relayable
+      if (afn_is_ip_relayable(contact->peerip,contact->relayok)==true) {
+        contact->privilege=rel_isrelay;     //IP is relayable
+        contact->credit++;
+        }
+      break;
+    case 7      :       //contact is good, then sending a signon
+      (void) prc_settitle("%s:%s, incoming contact from [%s] on [%s:%s]",
+                          APPNAME,appname,contact->peerip,
+                          contact->locname,contact->locserv);
+      (void) log_fprintlog(contact->logptr,true,"Start CNT=%s",contact->mainsesid);
+      (void) log_fprintlog(contact->logptr,false,"(Contact open from [%s] to "
+                                                  "[%s:%s])",
+                                                  contact->peerip,
+                                                  contact->locip,
+                                                  contact->locserv);
+      if (soc_iscrypted(contact->socptr)==true) {
+        int level;
+
+        level=soc_get_sec_level(contact->socptr);
+        (void) log_fprintlog(contact->logptr,false,cry,"server",level);
+        }
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return contact;
+#undef  MXCARIN
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to wait for a remote client.          */
+/*      return the fee memory contact (NULL pointer)    */
+/*                                                      */
+/********************************************************/
+PUBLIC CONTYP *eml_dropcontact(CONTYP *contact)
+
+{
+#define OPEP    "lvleml.c:eml_dropcontact"
+
+SRVTYP *srv;
+int phase;
+_Bool proceed;
+
+srv=(SRVTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase){
+    case 0      :       //check for binding
+      if (contact==(CONTYP *)0) {
+        (void) rou_alert(0,"%s Contact pointer is NULL (Bug!?)",OPEP);
+        phase=999;      //not going further
+        }
+      break;
+    case 1      :       //getting the remote server information
+      if (sql_mngremote(contact->sqlptr,sql_select,contact->peerip,&srv)==false) {
+        (void) rou_alert(0,"%s Unable to get remote [%s] data (Bug!?)",
+                            OPEP,contact->peerip);
+        phase++;        //No need to do update
+        }
+      break;
+    case 2      :       //Updating remote server data
+      if (srv!=(SRVTYP *)0) {   //Always
+        char *bank;
+
+        srv->credit+=contact->credit;
+        srv->update=time((time_t *)0);
+        if (sql_mngremote(contact->sqlptr,sql_update,contact->peerip,&srv)==false) {
+          (void) rou_alert(0,"%s Unable to UPDATE remote [%s] data (Bug!?)",
+                              OPEP,contact->peerip);
+          }
+        bank=eml_showcredit(contact->peerip,contact->peername,
+                            contact->credit,srv->credit);
+        (void) log_fprintlog(contact->logptr,true,"(%s)",bank);
+        bank=rou_freestr(bank);
+        srv=sql_freesrv(srv);
+        }
+      break;
+    case 3      :       //properly closing remote contact
+      (void) rou_alert(0,"Contact from peer <%s> to port <%s> terminated",
+                          contact->peerip,contact->locserv);
+      contact->socptr=soc_release(contact->socptr);
+      break;
+    case 4      :       //freeing contact memory
+      contact=freecontact(contact);              
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return contact;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to generate transfile contents        */
+/*      return all reference to contact.                 */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_mktransfile(CONTYP *contact,FILE *qfile)
+
+{
+#define OPEP    "lvleml.c:eml_mktrans"
+
+_Bool status;
+
+status=true;
+if ((contact!=(CONTYP *)0)&&(contact->recipients!=(RCPTYP **)0)) {
+  RCPTYP **ptr;
+  TRATYP **tralist;
+  time_t isnow;
+
+  ptr=contact->recipients;
+  tralist=(TRATYP **)0;
+  isnow=time((time_t *)0);
+  while (*ptr!=(RCPTYP *)0) {
+    TRATYP *tra;
+    char data[300];
+
+    (void) snprintf(data,sizeof(data),"%s@%s",(*ptr)->userid,(*ptr)->domain);
+    tra=(TRATYP *)calloc(1,sizeof(TRATYP));
+    tra->code=(*ptr)->code;
+    tra->date=isnow;
+    tra->delay=0;
+    tra->remoteip=strdup(contact->peerip);
+    tra->reverse=strdup(contact->peername);
+    tra->sessid=strdup(contact->session->sessid);
+    tra->sfrom=strdup(contact->mailfrom);
+    tra->rcptto=strdup(data);
+    (void) strcpy(data,"email header, 'From:' missing");
+    if (contact->session->hfrom!=(char *)0) 
+      (void) strncpy(data,contact->session->hfrom,sizeof(data)-1);
+    tra->hfrom=strdup(data);
+    (void) strcpy(data,"email header, 'Subject:' missing");
+    if (contact->session->hsubject!=(char *)0) 
+      (void) strncpy(data,contact->session->hsubject,sizeof(data)-1);
+    tra->hsubject=strdup(data);
+    tralist=(TRATYP **)rou_addlist((void **)tralist,(void *)tra);
+    ptr++;
+    }
+  (void) eml_dump_list_tra(qfile,tralist);
+  (void) sql_update_tradb(contact->sqlptr,tralist);
+  tralist=(TRATYP **)rou_freelist((void **)tralist,(genfree_t)eml_freetra);
+  }
+return status;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to store local email (MX local) within*/
+/*      the serveur right directory.                    */
+/*                                                      */
+/********************************************************/
+PUBLIC void eml_local_email(TRATYP **tra)
+
+{
+#define OPEP    "lvleml.c:eml_local_email,"
+
+time_t isnow;
+
+isnow=time((time_t *)0);
+while (*tra!=(TRATYP *)0) {
+  if (((*tra)->date+(*tra)->delay)>isnow)
+    continue;
+  (void) eml_add_delay(isnow,*tra);
+  switch ((*tra)->code) {
+    case 'L'    :       //local email (MX is local)
+      (*tra)->code='C'; 
+      if (eml_store_email(*tra)==false) {
+        (*tra)->code='L';       //Will try to deliver later one
+        (void) rou_alert(0,"%s Unable to deliver local email <%s> to <%s>",
+                            OPEP,(*tra)->sessid,(*tra)->rcptto);
+        }
+      break;
+    default     :       //Unexpected local? email??
+      (void) rou_alert(0,"%s Directive is not! local email (Bug?)",OPEP);
+      (void) rou_alert(0,"%s TRA=<%cs %lu %04u %s %s %s",OPEP,
+                          (*tra)->code,
+                          (*tra)->date,
+                          (*tra)->delay,
+                          (*tra)->sessid,
+                          (*tra)->sfrom,
+                          (*tra)->rcptto);
+      break;
+    }
+  tra++;
+  }
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to send email to remote recipient     */
+/*                                                      */
+/********************************************************/
+PUBLIC void eml_remote_email(TRATYP **tra)
+
+{
+#define OPEP    "lvleml.c:eml_remote_email,"
+
+char *mainsesid;
+LOGPTR *logptr;
+long debut;
+long fin;
+int phase;
+_Bool proceed;
+
+mainsesid=(char *)0;
+logptr=(LOGPTR *)0;
+debug=0;
+fin=0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Check if we have tra
+      if (tra==(TRATYP **)0) {
+        (void) rou_alert(0,"%s Transaction pointer is NULL (Bug?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //checking if we have a session ID
+      if ((*tra)->sessid==(char *)0) {
+        (void) rou_alert(0,"%s Session ID is NULL (Bug?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 2      :       //extracting the main session number
+      char *ptr;
+      char cmt[100];
+
+      mainsesid=strdup((*tra)->sessid);
+      ptr=strrchr(mainsesid,'-');
+      if (ptr!=(char *)0)
+        *ptr='\000';
+      (void) snprintf(cmt,sizeof(cmt),"main-session-id=%s",mainsesid);
+      logptr=log_openlog(mainsesid,cmt);
+      mainsesid=rou_freestr(mainsesid);
+      break;
+    case 3      :       //sending all email within transaction
+      (void) sending_email(logptr,tra);  
+      break;
+    case 4      :       //closing log linked to session
+      logptr=log_closelog(logptr,&debut,&fin);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to rebounce an email which was NOT    */
+/*      deliverable.                                    */
+/*                                                      */
+/********************************************************/
+PUBLIC void eml_rebounce_email(TRATYP *tra)
+
+{
+char *tmp;
+
+(void) rou_alert(0,"JMPDG rebounce");
+tmp=tra->sfrom;
+tra->sfrom=tra->rcptto;
+tra->sfrom=tmp;
+tra->code='L';
+}
+
diff --git a/lib/lvleml.d b/lib/lvleml.d
new file mode 100644 (file)
index 0000000..37b40a1
--- /dev/null
@@ -0,0 +1,119 @@
+lvleml.o lvleml.d : lvleml.c /usr/include/stdc-predef.h /usr/include/arpa/inet.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/netinet/in.h /usr/include/bits/stdint-uintn.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/socket.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/endian.h /usr/include/bits/endianness.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/socket_type.h \
+ /usr/include/bits/sockaddr.h /usr/include/asm/socket.h \
+ /usr/include/asm-generic/socket.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/ctype.h /usr/include/bits/types/locale_t.h \
+ /usr/include/bits/types/__locale_t.h /usr/include/errno.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/netdb.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/stdlib.h /usr/include/bits/libc-header-start.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/bits/floatn.h /usr/include/bits/floatn-common.h \
+ /usr/include/alloca.h /usr/include/bits/stdlib-float.h \
+ /usr/include/stdio.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/string.h \
+ /usr/include/strings.h /usr/include/signal.h \
+ /usr/include/bits/signum-generic.h /usr/include/bits/signum-arch.h \
+ /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/types/stack_t.h \
+ /usr/include/sys/ucontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigstksz.h /usr/include/bits/ss_flags.h \
+ /usr/include/bits/types/struct_sigstack.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/time.h \
+ /usr/include/bits/time.h /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subafn.h \
+ subrou.h /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ subcnv.h unidig.h unieml.h uniprc.h devlog.h gestcp.h unidns.h devsoc.h \
+ unitls.h /usr/include/openssl/ssl.h /usr/include/openssl/macros.h \
+ /usr/include/openssl/opensslconf.h /usr/include/openssl/configuration.h \
+ /usr/include/openssl/opensslv.h /usr/include/openssl/e_os2.h \
+ /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-least.h /usr/include/openssl/e_ostime.h \
+ /usr/include/sys/time.h /usr/include/openssl/comp.h \
+ /usr/include/openssl/crypto.h /usr/include/openssl/safestack.h \
+ /usr/include/openssl/stack.h /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h /usr/include/openssl/core.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/comperr.h /usr/include/openssl/bio.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/x509.h \
+ /usr/include/openssl/buffer.h /usr/include/openssl/buffererr.h \
+ /usr/include/openssl/evp.h /usr/include/openssl/core_dispatch.h \
+ /usr/include/openssl/indicator.h /usr/include/openssl/params.h \
+ /usr/include/openssl/bn.h /usr/include/openssl/bnerr.h \
+ /usr/include/openssl/evperr.h /usr/include/openssl/objects.h \
+ /usr/include/openssl/obj_mac.h /usr/include/openssl/asn1.h \
+ /usr/include/openssl/asn1err.h /usr/include/openssl/objectserr.h \
+ /usr/include/openssl/ec.h /usr/include/openssl/ecerr.h \
+ /usr/include/openssl/rsa.h /usr/include/openssl/rsaerr.h \
+ /usr/include/openssl/dsa.h /usr/include/openssl/dh.h \
+ /usr/include/openssl/dherr.h /usr/include/openssl/dsaerr.h \
+ /usr/include/openssl/sha.h /usr/include/openssl/x509err.h \
+ /usr/include/openssl/x509_vfy.h /usr/include/openssl/lhash.h \
+ /usr/include/openssl/pkcs7.h /usr/include/openssl/pkcs7err.h \
+ /usr/include/openssl/http.h /usr/include/openssl/conf.h \
+ /usr/include/openssl/conferr.h /usr/include/openssl/conftypes.h \
+ /usr/include/openssl/pem.h /usr/include/openssl/pemerr.h \
+ /usr/include/openssl/hmac.h /usr/include/openssl/async.h \
+ /usr/include/openssl/asyncerr.h /usr/include/openssl/ct.h \
+ /usr/include/openssl/cterr.h /usr/include/openssl/sslerr.h \
+ /usr/include/openssl/sslerr_legacy.h /usr/include/openssl/prov_ssl.h \
+ /usr/include/openssl/ssl2.h /usr/include/openssl/ssl3.h \
+ /usr/include/openssl/tls1.h /usr/include/openssl/dtls1.h \
+ /usr/include/openssl/srtp.h /usr/include/openssl/quic.h gessql.h \
+ devsql.h unisql.h geseml.h lvleml.h gesspf.h
diff --git a/lib/lvleml.h b/lib/lvleml.h
new file mode 100644 (file)
index 0000000..c5b3d8e
--- /dev/null
@@ -0,0 +1,74 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Define all routine to manage SMTP high level     */
+/*     exchange.                                       */
+/*                                                     */
+/********************************************************/
+#ifndef        LVLEML
+#define LVLEML
+
+#include        <stdio.h>
+
+#include        "devsoc.h"
+#include        "devsql.h"
+#include        "gestcp.h"
+#include        "gesspf.h"
+#include        "geseml.h"
+
+typedef struct  {
+        SOCPTR *socptr;         //established contact socket context
+        SQLPTR *sqlptr;         //established contact database access
+        int delay;              //communication max delay
+        _Bool isgoodfqdn;       //Remote presented a valid fqdn
+        char *fqdn;             //fully qualified domain from peer
+        int credit;             //Credits to be added or removed from contact
+        RELTYP privilege;       //onnection privilege
+        char *authname;         //Link is authenticated name
+        AFNTYP **localafns;     //local IP as an AFNUM
+        char *locip;            //socket local IP num
+        char *locname;          //socket local hostname
+        char *locserv;          //local service port
+        char *peername;         //socket remote peer FQDN
+        char *peerip;           //socket remote peer IP
+        AFNTYP **relayok;       //List of IP which are relayable
+        int numreset;           //number of SMTP reset received
+        char *mainsesid;        //session main ID
+        char *termend;          //contact ending condition
+        SESTYP *session;        //SMTP current session information
+        char *mailfrom;         //current mail from originator
+        SPFENU fromspf;         //remote email SPF status
+        RCPTYP **recipients;    //List of email recipient
+        LOGPTR *logptr;         //reference to session log
+        char *transout;         //data to be flush out to remote
+        }CONTYP;
+
+
+//Procedure to transmit data on the contact channel
+extern void eml_transmit(CONTYP *contact,_Bool flush,const char *fmt,...);
+
+//To store connection status and transmit action code to remote
+extern void eml_note(CONTYP *contact,char *rcpt,const char *fmt,...);
+
+//procedure to extract line and proceed with peer contact
+extern int eml_docontact(CONTYP *contact);
+
+//wait for an incoming contact
+extern CONTYP *eml_getcontact(SOCPTR *socptr,int pos);
+
+//drop contact established by remote
+extern CONTYP *eml_dropcontact(CONTYP *contact);
+
+//generate trans file contents
+extern _Bool eml_mktransfile(CONTYP *contact,FILE *qfile);
+
+//store local email within domain proper directory
+extern void eml_local_email(TRATYP **tra);
+
+//sending email to remote recipents
+extern void eml_remote_email(TRATYP **tra);
+
+//Rebouncing Email to the first originator
+extern void eml_rebounce_email(TRATYP *tra);
+
+#endif
diff --git a/lib/modrec.c b/lib/modrec.c
new file mode 100644 (file)
index 0000000..dfc5f7e
--- /dev/null
@@ -0,0 +1,516 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Module to handle all email incoming             */
+/*                                                     */
+/********************************************************/
+#include        <errno.h>
+#include        <mcheck.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <syslog.h>
+#include        <unistd.h>
+
+#include        "subrou.h"
+#include        "uniprc.h"
+#include        "unieml.h"
+#include        "unisig.h"
+#include        "devsoc.h"
+#include        "gessql.h"
+#include        "gestcp.h"
+#include        "lvleml.h"
+#include        "modrec.h"
+
+//env variable to set the rejection level
+#define ENVRJCT         "RJCTCRED"
+#define DFLTRJCT        -50     //default reject max level
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to returne the local argv             */
+/*                                                     */
+/********************************************************/
+static char **getconfargv()
+
+{
+char **confargv;
+char *data;
+char *conf;
+
+confargv=(char **)0;
+conf=(char *)0;
+if ((conf=getenv("SMTPPORTS"))!=(char *)0)
+  conf=strdup(conf);
+data=conf;
+while (data!=(char *)0) {
+  char *ptr;
+  char *seg;
+
+  if ((ptr=strchr(data,','))!=(char *)0) {
+    *ptr='\000';
+    ptr++;
+    }
+  seg=strdup(data);
+  confargv=(char **)rou_addlist((void **)confargv,(void *)seg);
+  data=ptr;
+  }
+conf=rou_freestr(conf);
+return confargv;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to scan argument and generate binding */
+/*      information.                                    */
+/*      build a SOCPTR list and return the number of    */
+/*      entries.                                        */
+/*                                                     */
+/*      Possible argument format are:                   */
+/*      protocol:ipnum:port:iteration                   */
+/*      example:                                        */
+/*      |ipnum||| -> smtp|ipnum|25|1                    */
+/*      smtps|ipnum|465|2                               */
+/*              -> smtps protocol,port 465,2 iteration  */
+/*                                                     */
+/********************************************************/
+static int prepbinding(SOCPTR ***bindings,int argc,char *argv[])
+
+{
+#define OPEP    "moderec.c:prepbinding"
+#define DFLTSET "smtp|0.0.0.0|25|2"
+#define DIP     "0.0.0.0"
+#define DPORT   "25"
+
+char *locargv[2];
+
+(void) memset(locargv,'\000',sizeof(locargv));
+*bindings=(SOCPTR **)0;
+if (argc==0) {
+  argc=1;
+  locargv[0]=DFLTSET; //very last default configuration
+  argv=locargv;
+  }
+for (int i=0;i<argc;i++) {
+  int iteration;
+  PROTYP proto;
+  char *ipnum;
+  char *port;
+  char *ptr;
+  char config[80];
+
+  (void) memset(config,'\000',sizeof(config));
+  (void) strncpy(config,argv[i],sizeof(config)-2);
+  ptr=config;
+  iteration=1;
+  proto=pro_smtp; 
+  ipnum=DIP;
+  port=DPORT;
+  for (int j=0;j<3;j++) {
+    char *sofar;
+
+    if ((sofar=strchr(ptr,'|'))==(char *)0) 
+      break;
+    *sofar='\000';
+    sofar++;
+    switch (j) {
+      case 0    :
+        if ((proto=soc_getprotocol(ptr))==pro_unknwn) {
+          (void) rou_alert(0,"%s, protocol unknown within config <%s>",
+                             OPEP,argv[i]);
+          proto=pro_smtp;
+          }
+        break;
+      case 1    :
+        if (strlen(ptr)>0)
+          ipnum=ptr;
+        //(void) rou_alert(0,"%s JMPDBG ipnum=[%s]",OPEP,ipnum);
+        break;
+      case 2    :
+        if (strlen(ptr)>0) 
+          port=ptr;
+        if (strlen(sofar)>0) 
+          iteration=atoi(sofar);
+        break;
+      default   :
+        (void) rou_alert(0,"%s, Code fault, '%d' unexpected value",OPEP,j);
+        break;
+      }
+    ptr=sofar;
+    }
+  *bindings=soc_mkbindinf(*bindings,proto,ipnum,port,iteration);
+  }
+return rou_nbrlist(*bindings);
+
+#undef  DPORt
+#undef  DIP
+#undef  DFLSET
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to detect and accept a contact from   */
+/*      a remote TCP connection.                        */
+/*                                                     */
+/********************************************************/
+static void docontact(SOCPTR *socptr,int pos) 
+
+{
+#define OPEP    "modrec.c:docontact"
+
+CONTYP *contact;
+int intstat;
+int phase;
+_Bool proceed;
+
+contact=(CONTYP *)0;
+intstat=0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  (void) rou_dbglive(9,OPEP,"phase='%d'",phase);
+  switch (phase) {
+    case 0      :       //waiting contact
+      if ((contact=eml_getcontact(socptr,pos))==(CONTYP *)0)
+        phase=999;      //No contact!
+      break;
+    case 1      :       //check if remote link is acceptable
+      if (sql_newconnect(contact->sqlptr,contact->peerip,1)!=1) {
+        char *end;
+  
+        end="Found ip=[%s] already connected";
+        (void) rou_asprintf(&(contact->termend),end,contact->peerip);
+        end="You have another connection still in progress";
+        (void) eml_note(contact,NULL,"%d 6.6.5 contact denied <%s>",NOTNOW,end);
+        (void) eml_transmit(contact,true,"%d 6.6.5 contact denied <%s>",NOTNOW,end);
+        contact->credit=-1;     //small penalty
+        (void) sleep(1);        //To avoid avalanche
+        phase=999;              //no Need to go further
+        }
+      break;
+    case 2      :  {     //checking remote credit
+      static char *cmt="Remote server credit too low";
+      int reject;
+      char *ptr;
+      SRVTYP *srv;
+
+      reject=DFLTRJCT;
+      if ((ptr=getenv(ENVRJCT))!=(char *)0)
+        reject=atoi(ptr);
+      if (sql_mngremote(contact->sqlptr,sql_select,contact->peerip,&srv)==true) {
+        if (srv->credit<=reject) {
+          contact->termend=rou_freestr(contact->termend);
+          contact->termend=strdup(cmt);
+          contact->credit=-1;
+          (void) sleep(2);
+          (void) eml_note(contact,NULL,"%d remote server, credit (%d) is too low",
+                                        DATRJC,srv->credit);
+          (void) eml_transmit(contact,true,"%d 6.6.6 %s",DATRJC,cmt);
+          phase=999;    //rejecting remote first hand
+          }
+        srv=sql_freesrv(srv);
+        }
+      }
+      break;
+    case 3      :       //do contact
+      intstat=eml_docontact(contact);
+      switch (intstat) {
+        case  1 :       //command 'quit' recieved
+          break;
+        case  0 :       //exit under timeout
+          (void) rou_alert(0,"Contact timeout with peer <%s>",contact->peerip);
+          break;
+        case -1 :       //Signal received 
+          (void) rou_alert(0,"Signal received within contact");
+          break;
+        case -2 :       //Signal received 
+          (void) rou_alert(0,"Remote disconnected");
+          break;
+        default :       //trouble trouble
+          (void) rou_alert(0,"%s Unexpected status='%d' (BUG?!)",OPEP,intstat);
+          break;
+        }
+      break;
+    default     :       //SAFE guard
+      if (contact!=(CONTYP *)0) { 
+        static const char *cmt;
+        SRVTYP *srv;
+
+        cmt="(Contact terminated, condition=<%s>)";
+        if (sql_mngremote(contact->sqlptr,sql_select,contact->peerip,&srv)==true) {
+          srv=sql_freesrv(srv);
+          }
+        (void) log_fprintlog(contact->logptr,true,cmt,contact->termend);
+        (void) sql_newconnect(contact->sqlptr,contact->peerip,-1);
+        contact=eml_dropcontact(contact);
+        }
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to activate child process if necessary*/
+/*                                                     */
+/********************************************************/
+static void setready(SOCPTR *socptr)
+
+{
+#define OPEP    "modrec.c:setready"
+#define TSLEEP  5
+
+int maxretry;
+pid_t *childs;
+int iterations;
+int phase;
+_Bool proceed;
+
+maxretry=5;
+childs=(pid_t *)0;
+iterations=soc_getiterations(socptr);
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //empty phase
+      if (iterations>0) 
+        childs=(pid_t *)calloc(iterations,sizeof(pid_t));
+      else
+        proceed=false;  //Empty Soc!?!
+      break;
+    case 1      :       //empty phase
+      if (foreground==true) {
+        (void) docontact(socptr,1);
+        phase++;        //no fork
+        }
+      break;
+    case 2      :       //check need to dispatch a process
+      for (int i=0;i<iterations;i++) {
+        (void)  prc_nozombie();
+        if (prc_checkprocess(childs[i])==true)
+          continue;
+        childs[i]=fork();
+        switch (childs[i]) {
+          case  -1      :       //trouble trouble to fork?
+            childs[i]=(pid_t)0;
+            (void) sleep(1);    //Weathering the storme
+            break;
+          case   0      :       //Child process itself
+            (void) free(childs);
+            (void) docontact(socptr,i+1);
+            (void) exit(0);
+            break;
+          default       :       //Main process relax
+            (void) usleep(10000);
+            break;
+          }
+        }
+      break;
+    case 3      :       //Relax time
+      if (foreground==true)
+        phase=999;      //foreground ->one shot deal
+      break;
+    case 4      :       //Relax time
+      phase=0;          //lets continue to check childs
+      (void) rou_alert(10,"%s, start sleep '%d' second",OPEP,TSLEEP);
+      (void) sleep(TSLEEP);  //signal received to exit fast.
+      if (childout==true) {
+        (void) rou_alert(9,"%s, Got child out",OPEP);
+        childout=false; 
+        }
+      (void) rou_alert(10,"%s, Exit from sleep",OPEP);
+      if ((hangup==true)||(reload==true))
+        phase=999;      //exiting under signal
+      break;
+    default     :       //SAFE Guard
+      (void) prc_killchilds(childs,iterations,maxretry);
+      (void) free(childs);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+#undef  TSLEEP
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to start a binding+waiting process    */
+/*                                                     */
+/********************************************************/
+static void startwaiter(SOCPTR *socptr)
+
+{
+#define OPEP    "modrec.c:startwaiter"
+
+int phase;
+_Bool proceed;
+
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Opening logs
+      (void) closelog();
+      (void) openlog(appname,LOG_NDELAY|LOG_PID,LOG_DAEMON);
+      break;
+    case 1      :       //binding on channel
+      if (soc_openbinding(socptr)==false) {
+        (void) rou_alert(0,"%s Aborting binding (config?)",OPEP);
+        (void) sleep(5);        //to avoid avalanche
+        phase=999;
+        }
+      break;
+    case 2      :       //waiting
+      (void) rou_alert(0,"JMPDBG waiter starting");
+      (void) setready(socptr);
+      (void) rou_alert(0,"JMPDBG waiter exiting");
+      break;
+    case 3      :       //stop binding
+      (void) soc_closebinding(socptr);
+      break;
+    default     :       //SAFE Guard
+      (void) closelog();
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      Waiting and handling smtp request               */
+/*                                                     */
+/********************************************************/
+PUBLIC void rec_handlesmtp(int argc,char *argv[])
+
+{
+#define OPEP    "modrec.c:rec_handlesmtp"
+#define RELAX   5       //Relax time between process checking
+
+unsigned long cycle;
+pid_t *childs;
+char **confargv;
+int nbrbind;
+_Bool allbusy;
+SOCPTR **bindings;
+int phase;
+_Bool proceed;
+
+cycle=0;
+childs=(pid_t)0;
+confargv=getconfargv();
+nbrbind=0;
+allbusy=false;
+bindings=(SOCPTR **)0;
+if (argc==0) {
+  argc=rou_nbrlist((void **)confargv);
+  argv=confargv;
+  }
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //preparing iteration
+      if ((nbrbind=prepbinding(&bindings,argc,argv))==0) {
+        (void) rou_alert(0,"%s, No listening IP found (config?)",OPEP);
+        phase=999;
+        }
+      childs=(pid_t *)calloc(nbrbind,sizeof(pid_t));
+      break;
+    case 1      :       //Detecting signal
+      cycle++;
+      (void) prc_settitle("%s:%s, sleeping mode (cycle=%08d)",
+                          APPNAME,appname,cycle);
+      if ((hangup==true)||(reload==true)) {
+        (void) rou_alert(0,"%s got hangup or reload signal",OPEP);
+        (void) prc_killchilds(childs,nbrbind,10);
+        phase=999;      //No need to have sub process;
+        }
+      break;
+    case 2      :       //Opening ALL channels
+      allbusy=true;
+      for (int i=0;i<nbrbind;i++) {
+        (void) prc_nozombie();
+        if (childs[i]==(pid_t)0) {
+          allbusy=false;
+          continue;
+          }
+        if (prc_checkprocess(childs[i])==false) {
+          childs[i]=(pid_t)0;
+          allbusy=false;
+          }
+        }
+      if (allbusy==true) {      //all serveur up and running?
+        (void) sleep(RELAX);    //yes! then relax 
+        phase=0;                //Lets check again
+        }
+      break;
+    case 3      :       //starting one process only
+      if (foreground==true) {
+        (void) startwaiter(bindings[0]);
+        phase++;        //no multiple fork
+        }
+      break;
+    case 4      :       //starting/restarting all waiting process
+      for (int i=0;i<nbrbind;i++) {
+        if (childs[i]!=(pid_t)0)
+          continue;
+        childs[i]=fork();
+        switch (childs[i]) {
+          case  -1      :       //trouble trouble to fork?
+            (void) rou_alert(0,"%s Unable to fork smtp handler (error=<%s>)",
+                                OPEP,strerror(errno));
+            childs[i]=(pid_t)0;
+            (void) sleep(2);    //Weathering the storm
+            break;
+          case   0      :       //Child process itself
+            (void) startwaiter(bindings[i]);
+            (void) exit(0);
+            break;
+          default       :       //Main process
+            (void) usleep(10000);
+            break;
+          }
+        }
+      break;
+    case 5      :       //lets continue within process loop
+      if (foreground==false) 
+        phase=0;
+      break;
+    default     :       //SAFE Guard
+      if (childs!=(pid_t *)0)
+        (void) free(childs);
+      bindings=soc_freebindinf(bindings);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+confargv=(char **)rou_freelist((void **)confargv,(genfree_t)rou_freestr);
+#undef  RELAX
+#undef  OPEP
+}
diff --git a/lib/modrec.d b/lib/modrec.d
new file mode 100644 (file)
index 0000000..6ab4ab3
--- /dev/null
@@ -0,0 +1,121 @@
+modrec.o modrec.d : modrec.c /usr/include/stdc-predef.h /usr/include/errno.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/mcheck.h \
+ /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/__fpos_t.h \
+ /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/stdlib.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/syslog.h /usr/include/sys/syslog.h \
+ /usr/include/bits/syslog-path.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h uniprc.h unieml.h subafn.h \
+ /usr/include/netdb.h /usr/include/netinet/in.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h /usr/include/bits/socket.h \
+ /usr/include/bits/socket_type.h /usr/include/bits/sockaddr.h \
+ /usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h unisig.h \
+ /usr/include/signal.h /usr/include/bits/signum-generic.h \
+ /usr/include/bits/signum-arch.h /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/types/stack_t.h \
+ /usr/include/sys/ucontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigstksz.h /usr/include/bits/ss_flags.h \
+ /usr/include/bits/types/struct_sigstack.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h devsoc.h unitls.h \
+ /usr/include/openssl/ssl.h /usr/include/openssl/macros.h \
+ /usr/include/openssl/opensslconf.h /usr/include/openssl/configuration.h \
+ /usr/include/openssl/opensslv.h /usr/include/openssl/e_os2.h \
+ /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-least.h /usr/include/openssl/e_ostime.h \
+ /usr/include/sys/time.h /usr/include/openssl/comp.h \
+ /usr/include/openssl/crypto.h /usr/include/openssl/safestack.h \
+ /usr/include/openssl/stack.h /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h /usr/include/openssl/core.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/comperr.h /usr/include/openssl/bio.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/x509.h \
+ /usr/include/openssl/buffer.h /usr/include/openssl/buffererr.h \
+ /usr/include/openssl/evp.h /usr/include/openssl/core_dispatch.h \
+ /usr/include/openssl/indicator.h /usr/include/openssl/params.h \
+ /usr/include/openssl/bn.h /usr/include/openssl/bnerr.h \
+ /usr/include/openssl/evperr.h /usr/include/openssl/objects.h \
+ /usr/include/openssl/obj_mac.h /usr/include/openssl/asn1.h \
+ /usr/include/openssl/asn1err.h /usr/include/openssl/objectserr.h \
+ /usr/include/openssl/ec.h /usr/include/openssl/ecerr.h \
+ /usr/include/openssl/rsa.h /usr/include/openssl/rsaerr.h \
+ /usr/include/openssl/dsa.h /usr/include/openssl/dh.h \
+ /usr/include/openssl/dherr.h /usr/include/openssl/dsaerr.h \
+ /usr/include/openssl/sha.h /usr/include/openssl/x509err.h \
+ /usr/include/openssl/x509_vfy.h /usr/include/openssl/lhash.h \
+ /usr/include/openssl/pkcs7.h /usr/include/openssl/pkcs7err.h \
+ /usr/include/openssl/http.h /usr/include/openssl/conf.h \
+ /usr/include/openssl/conferr.h /usr/include/openssl/conftypes.h \
+ /usr/include/openssl/pem.h /usr/include/openssl/pemerr.h \
+ /usr/include/openssl/hmac.h /usr/include/openssl/async.h \
+ /usr/include/openssl/asyncerr.h /usr/include/openssl/ct.h \
+ /usr/include/openssl/cterr.h /usr/include/openssl/sslerr.h \
+ /usr/include/openssl/sslerr_legacy.h /usr/include/openssl/prov_ssl.h \
+ /usr/include/openssl/ssl2.h /usr/include/openssl/ssl3.h \
+ /usr/include/openssl/tls1.h /usr/include/openssl/dtls1.h \
+ /usr/include/openssl/srtp.h /usr/include/openssl/quic.h gessql.h \
+ devsql.h unisql.h gestcp.h unidns.h devlog.h lvleml.h gesspf.h geseml.h \
+ modrec.h
diff --git a/lib/modrec.h b/lib/modrec.h
new file mode 100644 (file)
index 0000000..e13e747
--- /dev/null
@@ -0,0 +1,15 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/************************************************/
+/*                                             */
+/*     Module to handle all incoming email     */
+/*                                             */
+/************************************************/
+#ifndef        MODREC
+#define MODREC
+
+#include        <stdbool.h>
+
+//procedure to receive email from outside
+extern void rec_handlesmtp(int argc,char *argv[]);
+
+#endif
diff --git a/lib/numver.h b/lib/numver.h
new file mode 100644 (file)
index 0000000..5a07df1
--- /dev/null
@@ -0,0 +1,3 @@
+#define VERSION        "0.19"
+#define RELEASE        "11"
+#define BRANCH "dvl"
diff --git a/lib/subafn.c b/lib/subafn.c
new file mode 100644 (file)
index 0000000..5ab94c4
--- /dev/null
@@ -0,0 +1,678 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Sub kevel procedure to manage IP number.        */
+/*                                                     */
+/********************************************************/
+#include        <ifaddrs.h>
+#include        <malloc.h>
+#include        <stdbool.h>
+#include        <stdio.h>
+#include        <string.h>
+#include        <arpa/inet.h>
+#include        <errno.h>
+#include        <sys/types.h>
+
+#include       "subafn.h"
+
+#define        AFT     sizeof(struct in6_addr)
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     procedure to build a list of local server IP    */
+/*                                                      */
+/********************************************************/
+static char *getlocalipstr()
+
+{
+#define OPEP    "subafn.c:getlocalipstr,"
+
+char *localip;
+struct ifaddrs *ifaddr;
+int phase;
+_Bool proceed;
+
+localip=strdup("");
+ifaddr=(struct ifaddrs *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Get list of interface
+      if (getifaddrs(&ifaddr)<0) {
+        (void) fprintf(stderr,"%s Unable to get local IP (error=<%s> system?)\n",
+                               OPEP,strerror(errno));
+        phase=999;      //No need to go further;
+        }
+      break;
+    case 1      :       //cans list of interface
+      for (struct ifaddrs *ifa=ifaddr;ifa!=(struct ifaddrs *)0;ifa=ifa->ifa_next) {
+        int family;
+        int taille;
+        int er;
+        char *newloc;
+        char host[NI_MAXHOST];
+
+        if (ifa->ifa_addr==(struct sockaddr *)0)
+          continue;
+        family=ifa->ifa_addr->sa_family;
+        switch (family) {
+          case AF_INET  :
+            taille=sizeof(struct sockaddr_in);
+            break;
+          case AF_INET6 :
+            taille=sizeof(struct sockaddr_in6);
+            continue;   //No scanning for IPV6 Number (Jun 2025)
+            break;
+          default       :
+            continue;
+            break;
+          }
+        er=getnameinfo(ifa->ifa_addr,taille,host,NI_MAXHOST,NULL,0,NI_NUMERICHOST);
+        if (er!=0) {
+          (void) fprintf(stderr,"%s getnameinfo() failed: (error=<%s> system?)\n",
+                               OPEP,gai_strerror(er));
+          continue;
+          }
+        newloc=(char *)calloc(strlen(localip)+strlen(host)+3,sizeof(char));
+        if (strlen(localip)>0) {
+          (void) strcpy(newloc,localip);
+          (void) strcat(newloc,",");
+          }
+        (void) strcat(newloc,host);
+        (void) free(localip);
+        localip=newloc;
+        }
+      break;
+    case 2      :       //free memory
+      (void) freeifaddrs(ifaddr);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return localip;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Routine to compare 2 IP num, can be             */
+/*     IPV4 or IPV6 IP number.                         */
+/*      return true if match within mask.               */
+/*                                                      */
+/********************************************************/
+static _Bool cmpip(AFNTYP *ipnum1,AFNTYP *ipnum2,int mask)
+
+{
+_Bool areequal;
+int maskbyt;
+
+areequal=false;
+if (mask>128)
+  mask=128;
+maskbyt=mask/8;
+if (memcmp(ipnum1->ip,ipnum2->ip,maskbyt)==0) {
+  areequal=true;
+  mask%=8;
+  if (mask!=0) {
+    int maskbit;
+    int b1;
+    int b2;
+
+    maskbit=(~0U<<(8-mask)); 
+    b1=((int)ipnum1->ip[maskbyt])&maskbit;
+    b2=((int)ipnum2->ip[maskbyt])&maskbit;
+    if (b1!=b2)
+      areequal=false;
+    }
+  }
+return areequal;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Routine to fee memory used by an AFNTYP         */
+/*                                                      */
+/********************************************************/
+PUBLIC AFNTYP *afn_freeipnum(AFNTYP *afnnum)
+
+{
+if (afnnum!=(AFNTYP *)0) {
+  if (afnnum->strnumip!=(char *)0)
+    (void) free(afnnum->strnumip);
+  (void) free(afnnum);
+  afnnum=(AFNTYP *)0;
+  }
+return afnnum;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to set IPNUM mask.                    */
+/*                                                      */
+/********************************************************/
+PUBLIC AFNTYP *afn_maskip(AFNTYP *afnnum,int cidr)
+
+{
+if (afnnum!=(AFNTYP *)0) {
+  unsigned int num;
+  int mxcidr;
+
+  mxcidr=32;
+  if (afnnum->afntype==AF_INET6)
+    mxcidr=128;
+  if (cidr>mxcidr)
+    cidr=mxcidr;
+  afnnum->afnmask=cidr;
+  num=cidr/8;
+  cidr%=8;
+  if (cidr!=0) {
+    afnnum->ip[num]&=(~0U<<(8-cidr));
+    num++;
+    }
+  if (num<AFT)
+    (void) memset(afnnum->ip+num,'\000',AFT-num);
+  }
+return afnnum;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return a string with               */
+/*     ipnum address and MASK in clear format          */
+/*     Address need to freed.                          */
+/*                                                      */
+/********************************************************/
+PUBLIC char *afn_stripnum(AFNTYP *num)
+
+{
+#define OPEP    "subafn.c:afn_stripnumm,"
+
+char *strip;
+const char *markr;
+char host[NI_MAXHOST];
+int phase;
+_Bool proceed;
+
+strip=(char *)0;
+markr="";
+(void) strcpy(host,"");
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Do we have an IP number
+      if (num==(AFNTYP *)0) {
+        (void) fprintf(stderr,"%s No ip provided (bug?)\n",OPEP);
+        (void) snprintf(host,sizeof(host),"[IP not defined] (Bug?)");
+        phase=999;
+        }
+      break;
+    case 1      :       //extracting IP
+      switch (num->afntype) {
+        case AF_INET6  :
+          markr=STRIPV6;
+          //NO break
+        case AF_INET   :
+          if (inet_ntop(num->afntype,&(num->ip),host,sizeof(host))==(char *)0) 
+            (void) snprintf(host,sizeof(host),"[afn_stripnum error=<%s>] (Bug?)",
+                                              strerror(errno));
+          break;
+        default         :
+          (void) snprintf(host,sizeof(host),"[ipvx:0.%d] (Bug?)",num->afntype);
+          break;
+        }
+      break;
+    default     :       //SAFE Guard
+      if (strlen(host)>0) {      //always
+        int taille;
+
+        taille=strlen(markr)+strlen(host)+10;
+        strip=(char *)calloc(taille,sizeof(char));
+        (void) snprintf(strip,taille,"%s%s",markr,host);
+        }
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return strip;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Routine to convert a addrinfo struct to an      */
+/*     IP number in AFNTYP structure.                  */
+/*                                                      */
+/********************************************************/
+PUBLIC AFNTYP *afn_getaddrinfo(struct addrinfo *rp)
+
+{
+AFNTYP *afnnum;
+
+afnnum=(AFNTYP *)0;
+if (rp!=(struct addrinfo *)0) {
+  u_char *ptr;
+
+  ptr=(u_char *)0;
+  afnnum=calloc(1,sizeof(AFNTYP));
+  afnnum->afntype=rp->ai_family;
+  switch (rp->ai_family) {
+    case AF_INET       :
+      afnnum->afnmask=32;
+      ptr=(u_char *)&(((struct sockaddr_in *)rp->ai_addr)->sin_addr.s_addr);
+      break;
+    case AF_INET6      :
+      afnnum->afnmask=128;
+      ptr=(u_char *)&(((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr.s6_addr);
+      break;
+    default            :       /*trouble trouble       */
+      afnnum=afn_freeipnum(afnnum);
+      break;
+    }
+  if (ptr!=(u_char *)0)
+    (void) memmove(afnnum->ip,ptr,afnnum->afnmask/8);
+  afnnum->strnumip=afn_getstrip(afnnum);
+  }
+return afnnum;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to transforme ONE clean IP to an AFN  */
+/*      structure.                                      */
+/*                                                      */
+/********************************************************/
+PUBLIC AFNTYP *afn_getoneipnum(char *oneipstr)
+
+{
+#define OPEP    "subafn.c:afn_getoneipnum,"
+#define        ZIPV4   "0.0.0.0"
+
+AFNTYP *afnnum;
+int phase;
+int proceed;
+
+afnnum=(AFNTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) fprintf(stderr,"%s JMPDBG phase='%d'\n",OPEP,phase);
+  switch (phase) {
+    case 0     :       /*lets say ip format ok */
+      afnnum=(AFNTYP *)calloc(1,sizeof(AFNTYP));
+      afnnum->afntype=AF_INET;
+      afnnum->afnmask=32;
+      if (strchr(oneipstr,':')==(char *)0) 
+       phase++;        //This is an IPV4 number
+      break;
+    case 1     :       //IP is an IPV6 format
+      if (strcasestr(oneipstr,STRIPV6)==oneipstr) 
+       oneipstr+=strlen(STRIPV6);
+      afnnum->afntype=AF_INET6;
+      afnnum->afnmask=128;
+      break;
+    case 2     :       //lets convert it
+      switch (inet_pton(afnnum->afntype,oneipstr,afnnum->ip)) {
+        case  1         :       //success
+          break;
+        case  0         :       //Invalid IP number
+          (void) fprintf(stderr,"Unable to convert IP <%s> (Invalid IP number)\n",
+                                oneipstr);
+         afnnum=afn_freeipnum(afnnum);
+          phase=999;            //no need to go further
+          break;
+        case -1         :       //Invalid IP number
+          (void) fprintf(stderr,"Unable to convert IP <%s> (error=<%s>)\n",
+                                oneipstr,strerror(errno));
+         afnnum=afn_freeipnum(afnnum);
+          phase=999;            //no need to go further
+          break;
+        default         :       //Unexpected Result
+          (void) fprintf(stderr,"Unexpected status to convert IP <%s>  "
+                                "(error=<%s> BUG!?)\n",
+                                oneipstr,strerror(errno));
+         afnnum=afn_freeipnum(afnnum);
+          phase=999;            //no need to go further
+        }
+      break;
+    case 3     :       //lets convert it
+      afnnum->strnumip=afn_getstrip(afnnum);
+      //(void) printf("JMPDBG <%s> -> to <%s>\n",oneipstr,afnnum->strnumip);
+      break;
+    default    :       /*SAFE guard            */
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return afnnum;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Routine to convert clean string list to an      */
+/*     IP number in AFNTYP list.                       */
+/*                                                      */
+/********************************************************/
+PUBLIC AFNTYP **afn_getipnums(char *seqipstr)
+
+{
+#define OPEP    "subafn.c:afn_getipnums"
+#define        ZIPV4   "0.0.0.0"
+
+AFNTYP **afnlist;
+char *iplist;
+int phase;
+int proceed;
+
+afnlist=(AFNTYP **)calloc(1,sizeof(AFNTYP *));
+iplist=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Do we have an IP string
+      if (seqipstr==(char *)0) {
+        (void) fprintf(stderr,"%s No ip provided (bug?)\n",OPEP);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 1     :       //checking '0' situation
+      if (strcmp(seqipstr,"0")==0) 
+        seqipstr=ZIPV4;
+      break;
+    case 2     :       //checking '0' situation
+      iplist=strdup(seqipstr);
+      if (strcmp(iplist,ZIPV4)==0) {
+        (void) free(iplist);
+        iplist=getlocalipstr();
+        }
+      if (strlen(iplist)==0)
+        phase++;        //No need to scan  
+      break;
+    case 3     :       //scanning the iplist
+      char *ptr;
+      int num;
+
+      ptr=iplist;
+      num=0;
+      while (ptr!=(char *)0) {
+        char *next;
+        AFNTYP *afnloc;
+
+        if ((next=strchr(ptr,','))!=(char *)0) {
+          *next='\000';
+          next++;
+          } 
+        if ((afnloc=afn_getoneipnum(ptr))!=(AFNTYP *)0) {
+          afnlist=(AFNTYP **)realloc(afnlist,(num+2)*sizeof(AFNTYP *));
+          afnlist[num]=afnloc;
+          num++;
+          afnlist[num]=(AFNTYP *)0;
+          }
+        ptr=next;
+        }
+      break;
+    case 4      :       //free memory used
+      (void) free(iplist);
+      break;
+    default    :       /*SAFE guard            */
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return afnlist;
+
+#undef  ZIPV4
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to normalize IP number format         */
+/*     Memory allocated need to be freed later         */
+/*     on.                                             */
+/*                                                      */
+/*                                                      */
+/********************************************************/
+char *afn_getstrip(AFNTYP *afnnum)
+
+{
+#define OPEP    "subafn.c:afn_getstrip"
+
+char *ipnorm;
+char buffer[300];
+
+ipnorm=(char *)0;
+(void) strcpy(buffer,"");
+if (afnnum!=(AFNTYP *)0) {
+  switch (afnnum->afntype) {
+    case AF_INET       :
+      (void) snprintf(buffer,sizeof(buffer),"%d.%d.%d.%d",
+                                            afnnum->ip[0],afnnum->ip[1],
+                                            afnnum->ip[2],afnnum->ip[3]);
+      break;
+    case AF_INET6      :
+      (void) snprintf(buffer,sizeof(buffer),"%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
+                                            "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+                                           afnnum->ip[0],afnnum->ip[1],
+                                           afnnum->ip[2],afnnum->ip[3],
+                                           afnnum->ip[4],afnnum->ip[5],
+                                           afnnum->ip[6],afnnum->ip[7],
+                                           afnnum->ip[8],afnnum->ip[9],
+                                           afnnum->ip[10],afnnum->ip[11],
+                                           afnnum->ip[12],afnnum->ip[13],
+                                           afnnum->ip[14],afnnum->ip[15]);
+      break;
+    default            :
+      (void) fprintf(stderr,"%s, unable to find IP type (bug?)",OPEP);
+    } 
+  }
+if (strlen(buffer)>0)
+  ipnorm=strdup(buffer);
+return ipnorm;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return a revers address as         */
+/*      dynamically alocated memory.                    */
+/*                                                      */
+/********************************************************/
+char *afn_reversipnum(AFNTYP *afnnum)
+
+{
+char * reversip;
+
+reversip=(char *)0;
+if (afnnum!=(AFNTYP *)0) {
+  int i;
+  char revers[200];
+
+  (void) memset(revers,'\000',sizeof(revers));
+  switch (afnnum->afntype) {
+    case AF_INET6       :
+      for (i=16;i>0;i--) {
+        char local[20];
+
+        (void) snprintf(local,sizeof(local),"%x.%x.",
+                                             afnnum->ip[i-1]&0xf,
+                                             afnnum->ip[i-1]>>4);
+        (void) strncat(revers,local,sizeof((revers)-1)-strlen(revers));
+        }
+      break;
+    case AF_INET        :
+      for (i=4;i>0;i--) {
+        char local[10];
+
+        (void) snprintf(local,sizeof(local),"%d.",afnnum->ip[i-1]);
+        (void) strncat(revers,local,(sizeof(revers)-1)-strlen(revers));
+        }
+      break;
+    default             :
+      (void) snprintf(revers,sizeof(revers),"%d.0.0.0.0.ukn.",afnnum->afntype);
+      break;
+    }
+  revers[strlen(revers)-1]='\000';
+  reversip=strdup(revers);
+  }
+return reversip;
+}
+                 
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Routine to compare two ipnum by taking          */
+/*     mask ip_ipbits, return true if ipbits           */
+/*     are identical, false otherwise.                 */
+/*     return -1 if unexpected type                    */
+/*                                                      */
+/********************************************************/
+PUBLIC int afn_cmpipnum(AFNTYP *afnnum1,AFNTYP *afnnum2,int mask)
+
+{
+int areequal;
+int phase;
+int proceed;
+
+areequal=0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0     :       /*two ipnum really      */
+      if ((afnnum1==(AFNTYP *)0)||(afnnum2==(AFNTYP *)0))
+       proceed=false;  /*no need to go further */
+      break;
+    case 1     :       /*is mask 0, all IP ok  */
+      if (mask==0) {
+       areequal=1;
+       proceed=false;
+       }
+      break;
+    case 2     :       /*two ipnum same type   */
+      if (afnnum1->afntype!=afnnum2->afntype)
+       proceed=false;  /*no need to go further */
+      break;
+    case 3     :       /*lets compare ip       */
+      if (mask>128)
+       mask=128;
+      switch(afnnum1->afntype) {
+       case AF_INET    :
+         if (mask>32)
+           mask=32;
+          // fall through
+       case AF_INET6   :
+         areequal=(int)cmpip(afnnum1,afnnum2,mask);
+         break;
+       default         :
+         errno=EAFNOSUPPORT;
+         areequal=-1;
+         break;
+       }
+      break;
+    default    :       /*SAFE guard            */
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return areequal;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check if a remote IP is part of    */
+/*      acceptable to be relayed list.                  */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool afn_is_ip_relayable(char *iptocheck,AFNTYP **list)
+
+{
+#define OPEP    "subafn.c:afn_ip_inlist,"
+
+_Bool isok;
+AFNTYP *afn;
+int phase;
+_Bool proceed;
+
+isok=false;
+afn=(AFNTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //converting IP
+      if ((afn=afn_getoneipnum(iptocheck))==(AFNTYP *)0) {
+        (void) fprintf(stderr,"%s, Unable to convert IP=<%s> (bug?)",
+                              OPEP,iptocheck);
+        phase=999;
+        }
+      break;
+    case 1      :       //do we have a list
+      if (list==(AFNTYP **)0) 
+        phase=999;      //No need to go further
+      break;
+    case 2      :       //do we have a list
+      while ((isok==false)&&(*list!=(AFNTYP *)0)) {
+        switch (afn_cmpipnum(*list,afn,(*list)->afnmask)) {
+          case -1       :       //trouble!
+            (void) fprintf(stderr,"%s, Unable to check IP=<%s> (error=<%s> bug?)",
+                                  OPEP,iptocheck,strerror(errno));
+            break;
+          case  0       :       //no match, nothing to do
+            break;
+          case  1       :       //found a match
+            isok=true;
+            break;
+          }
+        list++;
+        }
+      break;
+    default     :       //SAFE Guard
+      afn=afn_freeipnum(afn);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return isok;
+
+#undef  OPEP
+}
diff --git a/lib/subafn.d b/lib/subafn.d
new file mode 100644 (file)
index 0000000..40a1321
--- /dev/null
@@ -0,0 +1,48 @@
+subafn.o subafn.d : subafn.c /usr/include/stdc-predef.h /usr/include/ifaddrs.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/sys/socket.h /usr/include/bits/types/struct_iovec.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/socket.h /usr/include/sys/types.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/socket_type.h \
+ /usr/include/bits/sockaddr.h /usr/include/asm/socket.h \
+ /usr/include/asm-generic/socket.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/malloc.h \
+ /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/string.h /usr/include/bits/types/locale_t.h \
+ /usr/include/bits/types/__locale_t.h /usr/include/strings.h \
+ /usr/include/arpa/inet.h /usr/include/netinet/in.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/bits/in.h \
+ /usr/include/errno.h /usr/include/bits/errno.h \
+ /usr/include/linux/errno.h /usr/include/asm/errno.h \
+ /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
+ subafn.h /usr/include/netdb.h /usr/include/rpc/netdb.h \
+ /usr/include/bits/netdb.h
diff --git a/lib/subafn.h b/lib/subafn.h
new file mode 100644 (file)
index 0000000..15a93c9
--- /dev/null
@@ -0,0 +1,66 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/************************************************/
+/*                                             */
+/*     Define all routine to manage all        */
+/*      procedure to manage IP number conversion*/
+/*                                             */
+/************************************************/
+#ifndef        SUBAFN
+#define SUBAFN
+
+#include       <netdb.h>
+
+//IPV6 ip number marker
+#define STRIPV6 "ipv6:"
+
+//space to store (at least) IPV6 number
+#define        AFT sizeof(struct in6_addr)
+
+//Need to have GNU_SOURCE define within CFLAGS
+#ifdef  AI_ALL
+#define HINTFLG AI_ALL|AI_CANONNAME|AI_CANONIDN
+#else /*AI_ALL is not defined in linux <= rh9   */
+#define HINTFLG AI_CANONNAME
+#endif
+
+typedef        struct  {
+        char *strnumip; //IP in string format   
+       int afntype;    //IP type
+       int afnmask;    //IP Mask
+       u_char ip[AFT]; //IP number
+       }AFNTYP;
+
+//procedure to free memory used by an AFNTYP structure
+extern AFNTYP *afn_freeipnum(AFNTYP *afnnum);
+
+//procedure to set IPNUM mask
+extern AFNTYP *afn_maskip(AFNTYP *afnnum,int cidr);
+
+//procedure to convert a AFNTYP structure to an standardised
+//IP number
+extern char *afn_stripnum(AFNTYP *afnnum);
+
+//procedure to extract a dynamic string with the
+//clean IP number from a AFNTYP struct.
+extern char *afn_getstrip(AFNTYP *afnnum);
+
+//procedure to convert a addrinfo struct to an 
+//IP number in AFNTYP structure.
+extern AFNTYP *afn_getaddrinfo(struct addrinfo *rp);
+
+//procedure to convert on IP seen as a string to an AFNTYP structure
+extern AFNTYP *afn_getoneipnum(char *oneipstr);
+
+//procedure to convert multiple IP seen as a string to an AFNTYP list
+extern AFNTYP **afn_getipnums(char *listipstr);
+
+//procedure to 'compute' a reverss-addres from the IP
+extern char *afn_reversipnum(AFNTYP *afnnum);
+
+//procedure to compare 2 same class IP according a mask value
+extern int afn_cmpipnum(AFNTYP *afnnum1,AFNTYP *afnnum2,int mask);
+
+//procedure to check if a remote ip is part of the relayable list
+extern _Bool afn_is_ip_relayable(char *iptocheck,AFNTYP **list);
+
+#endif
diff --git a/lib/subcnv.c b/lib/subcnv.c
new file mode 100644 (file)
index 0000000..17c8204
--- /dev/null
@@ -0,0 +1,223 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Define all routine to manage language string    */
+/*      conversion.                                     */
+/*                                                     */
+/********************************************************/
+#include        <ctype.h>
+#include        <stdbool.h>
+#include        <string.h>
+#include        <openssl/bio.h>
+#include        <openssl/buffer.h>
+#include        <openssl/evp.h>
+
+#include       "subcnv.h"
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Routine to check if all char within a B64       */
+/*      string are indeed Base-64 character.            */
+/*                                                      */
+/********************************************************/
+static int checkb64(char *b64)
+
+{
+int ok;
+
+ok=true;
+while ((*b64!='\000')&&(ok==true)) {
+  switch (*b64)        {
+    case '+'   :
+    case '/'   :
+    case '='   :
+      break;
+    default    :
+      if (isalnum(*b64)==0) {
+       ok=false;
+       *b64='\000';
+       }
+      break;
+    }
+  b64++;
+  }
+return ok;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to convert an ASCII B64 sequence to a */
+/*      plain ASCII sequence.                           */
+/*                                                      */
+/********************************************************/
+PUBLIC char *cnv_getb64(char *b64)
+
+{
+#define        OPEP    "subcnv.c:cnv_getb64,"
+
+char *decoded;
+BIO *mbio;
+BIO *bbio;
+int retour;
+
+(void) checkb64(b64);
+decoded=(char *)calloc(strlen(b64)+3,sizeof(char));
+mbio=BIO_new(BIO_s_mem());
+bbio=BIO_new(BIO_f_base64());
+BIO_set_flags(bbio,BIO_FLAGS_BASE64_NO_NL);
+bbio=BIO_push(bbio,mbio);
+(void) BIO_write(mbio,b64,strlen(b64));
+(void) BIO_flush(mbio);
+switch(retour=BIO_read(bbio,decoded,strlen(b64))) {
+  case -2      :
+    (void) fprintf(stderr,"%s Can't proceed! decoding <%s> (bug?)",OPEP,b64);
+    break;
+  case -1      :
+  case  0      :
+    (void) fprintf(stderr,"%s Need a retry! decoding <%s> (bug?)",OPEP,b64);
+    break;
+  default      :
+    if (retour>0) {    /*always                */
+      register int scan;
+      register int skip;
+
+      scan=retour;
+      skip=strlen(IOBNULL);    
+      while (scan>0) {
+        scan--;
+        if (decoded[scan]=='\000') {
+          register int taille;
+         char *n_deco;
+
+          taille=retour+skip+1;
+         if ((n_deco=(char *)realloc(decoded,taille*sizeof(char)))!=(char *)0) {
+           decoded=n_deco;
+           (void) memmove(decoded+scan+skip,decoded+scan+1,(retour-scan)+1);
+            (void) memmove(decoded+scan,IOBNULL,skip);
+           }
+         retour+=skip;
+         }
+       }
+      }
+    break;
+  }
+if (retour<=0) {
+  (void) free(decoded);
+  decoded=(char *)0;
+  }
+(void) BIO_free_all(bbio);
+return decoded;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to convert a plain ASCII sequence to a*/
+/*      B64 ASCII sequence.                             */
+/*                                                      */
+/********************************************************/
+PUBLIC char *cnv_setb64(const char *str)
+
+{
+#define        OPEP    "unicnv.c:tls_setb64,"
+
+register int skip;
+
+char *coded;
+BIO *bmem;
+BIO *b64;
+BUF_MEM *bptr;
+int taille;
+char *buffer;
+char *ptr;
+
+skip=strlen(IOBNULL)-1;
+coded=(char *)0;
+b64=BIO_new(BIO_f_base64());
+BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
+bmem=BIO_new(BIO_s_mem());
+b64=BIO_push(b64,bmem);
+buffer=strdup(str);
+taille=strlen(buffer);
+ptr=buffer;
+while ((ptr=strstr(ptr,IOBNULL))!=(char *)0) {
+  *ptr='\000';
+  ptr++;
+  (void) memmove(ptr,ptr+skip,strlen(ptr+skip)+1);
+  taille-=skip;
+  }
+(void) BIO_write(b64,buffer,taille);
+(void) BIO_flush(b64);
+BIO_get_mem_ptr(b64,&bptr);
+(void) free(buffer);
+coded=(char *)calloc(bptr->length+1,sizeof(char));
+(void) memcpy(coded,bptr->data,bptr->length);
+(void) BIO_free_all(b64);
+return coded;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to convert a string of character as   */
+/*      an hexadecimale string sequence                 */
+/*                                                      */
+/********************************************************/
+PUBLIC char *cnv_tohexa(const char *str,int taille)
+
+{
+char *hexa;
+
+hexa=(char *)0;
+if ((str!=(char *)0)&&(taille>0)) {
+  hexa=(char *)calloc((taille*2)+1,sizeof(char));
+  for (int i=0;i<taille;i++) {
+    char convert[5];
+  
+    (void) snprintf(convert,sizeof(convert),"%02hhx",str[i]);
+    (void) strcat(hexa,convert);
+    }
+  }
+return hexa;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to generate a random string with a    */
+/*      specific lenght.                                */
+/*                                                      */
+/********************************************************/
+PUBLIC char *cnv_getrndstr(size_t length)
+
+{
+char charset[]  =       "0123456789"
+                        "abcdefghijklmnopqrstuvwxyz"
+                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+char *rndstr;
+
+rndstr=(char *)0;
+if (length>0) {
+  rndstr=(char *)calloc(length+1,sizeof(char));
+  for (size_t t=0;t<length;t++) {
+    size_t index;
+
+    index=rand()%sizeof(charset);
+    rndstr[t]=charset[index];
+    }
+  }
+return rndstr;
+}
diff --git a/lib/subcnv.d b/lib/subcnv.d
new file mode 100644 (file)
index 0000000..5a11dc5
--- /dev/null
@@ -0,0 +1,67 @@
+subcnv.o subcnv.d : subcnv.c /usr/include/stdc-predef.h /usr/include/ctype.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/types/locale_t.h \
+ /usr/include/bits/types/__locale_t.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/string.h /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/strings.h /usr/include/openssl/bio.h \
+ /usr/include/openssl/macros.h /usr/include/openssl/opensslconf.h \
+ /usr/include/openssl/configuration.h /usr/include/openssl/opensslv.h \
+ /usr/include/openssl/e_os2.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/bits/stdint-least.h \
+ /usr/include/stdio.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/openssl/crypto.h \
+ /usr/include/stdlib.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/time.h \
+ /usr/include/bits/time.h /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h \
+ /usr/include/openssl/safestack.h /usr/include/openssl/stack.h \
+ /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h /usr/include/openssl/core.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/buffer.h \
+ /usr/include/openssl/buffererr.h /usr/include/openssl/evp.h \
+ /usr/include/openssl/core_dispatch.h /usr/include/openssl/indicator.h \
+ /usr/include/openssl/params.h /usr/include/openssl/bn.h \
+ /usr/include/openssl/bnerr.h /usr/include/openssl/evperr.h \
+ /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h \
+ /usr/include/openssl/asn1.h /usr/include/openssl/asn1err.h \
+ /usr/include/openssl/objectserr.h subcnv.h
diff --git a/lib/subcnv.h b/lib/subcnv.h
new file mode 100644 (file)
index 0000000..b16dec3
--- /dev/null
@@ -0,0 +1,28 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/************************************************/
+/*                                             */
+/*      Define all routine to manage language   */
+/*      string conversion.                      */
+/*                                             */
+/************************************************/
+#ifndef        SUBCNV
+#define SUBCNV
+
+//base64 char 0 coding
+#define IOBNULL "\\0"
+
+//Procedure to convert a plain ASCII B64 sequence
+//to a plain ASCII sequence
+extern char *cnv_getb64(char *b64);
+
+//Procedure to convert a plain ASCII sequence
+//to an ASCII B64 sequence
+extern char *cnv_setb64(const char *str);
+
+//Procedure to convert a string of character as an HEXA string
+extern char *cnv_tohexa(const char *str,int taille);
+
+//procedure to generate a random string
+extern char *cnv_getrndstr(size_t length);
+
+#endif
diff --git a/lib/subrou.c b/lib/subrou.c
new file mode 100644 (file)
index 0000000..675e73e
--- /dev/null
@@ -0,0 +1,941 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Module for low level subroutine                 */
+/*                                                     */
+/********************************************************/
+#include        <sys/stat.h>
+#include        <sys/time.h>
+#include        <dirent.h>
+#include        <errno.h>
+#include        <math.h>
+#include        <signal.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <syslog.h>
+#include        <time.h>
+#include        <unistd.h>
+
+#include       "subrou.h"
+#include       "numver.h"
+
+
+//Public variables
+PUBLIC  int debug=0;            //debug level
+PUBLIC  char *dbglive;          //procedure reference to be debugged live
+
+PUBLIC  _Bool foreground=false; //process is always started in background mode
+PUBLIC  char *rootdir=(char *)0;//working directory (debugging)
+PUBLIC  char  *appname;         //application "official" name
+
+//application binary name
+PUBLIC  char  execname[20]=APPNAME;
+
+//static variables
+static  _Bool modopen;          //boolean module open/close
+
+//Time offset (for debug purpose only)
+static time_t off64_time=(time_t)0;
+static time_t off_date=(time_t)0;
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to find out a possible drop zone      */
+/*      for abort() core memory dump.                   */
+/*                                                     */
+/********************************************************/
+static char *get_dropzone()
+
+{
+char *dropzone;
+char command[100];
+
+(void) strcpy(command,"/tmp/");
+(void) strcat(command,APPNAME);
+(void) strcat(command,"-crash");
+dropzone=rou_apppath(command);
+(void) rou_do_mkpdir(dropzone);
+return dropzone;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     This procedure is working tandem with libleak   */
+/*      to detect memory leak within specfic part of    */
+/*      code.                                           */
+/*     Tools as valgrind can not be used as most of    */
+/*      the application is forked in daemon state.      */
+/*      memory leak must be detected on a "long" run    */
+/*      forgetting about parent process apparent leak.  */
+/*      (forked process are created with parent memory  */
+/*      copy)                                           */
+/*                                                      */
+/********************************************************/
+PUBLIC void rou_checkleak(_Bool onoff)
+
+{
+#define ENABLE  "/tmp/libleak.enabled"
+
+static  _Bool current=false;
+
+if ((debug>2)&&(current!=onoff)) {
+  int status;
+  char cmd[200];
+
+  switch (onoff) {
+    case true   :
+      (void) snprintf(cmd,sizeof(cmd),"echo %d >> %s",getpid(),ENABLE);
+      break;
+    case false  :
+      (void) snprintf(cmd,sizeof(cmd),"sed -i '/%d/d' %s",getpid(),ENABLE);
+      break;
+    }
+  if ((status=system(cmd))!=0) {
+    (void) rou_alert(0,"status '%d' to memleak command <%s> (bug?)",status,cmd);
+    }
+  current=onoff;
+ }
+#undef  ENABLE
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to assign enough memory to format     */
+/*      a string with va_list parameter.                */
+/*      Known as vasprintf in GNU_SOURCE.               */
+/*                                                      */
+/********************************************************/
+PUBLIC int rou_vasprintf(char **str,const char *fmt,va_list ap)
+
+{
+int taille;
+va_list sup;
+char loc[10];
+
+taille=0;
+(void) va_copy(sup,ap);
+if (strlen(fmt)==0) 
+  *str=strdup("");
+else {
+  if ((taille=vsnprintf(loc,4,fmt,ap))>0) {
+    *str=calloc(taille+2,sizeof(char));
+    taille=vsnprintf(*str,taille+1,fmt,sup);
+    }
+  }
+return taille;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return the current application     */
+/*      working realm                                   */
+/*                                                      */
+/********************************************************/
+PUBLIC const char *rou_getrealm()
+
+{
+const char *realm;
+
+if ((realm=getenv("REALM"))==(const char *)0)
+   realm=REALM;
+return realm; 
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to assign enough memory to format     */
+/*      a string.                                       */
+/*      Known as asprintf in GNU_SOURCE.                */
+/*                                                      */
+/********************************************************/
+PUBLIC int rou_asprintf(char **str,const char *fmt,...)
+
+{
+int taille;
+
+taille=0;
+if (strlen(fmt)==0)
+  *str=strdup("");
+else {
+  va_list args;
+  va_start(args,fmt);
+  char loc[10];
+
+  if ((taille=vsnprintf(loc,4,fmt,args))>0) {
+    va_list argssup;
+  
+    va_start(argssup,fmt);
+    *str=calloc(taille+2,sizeof(char));
+    taille=vsnprintf(*str,taille+1,fmt,argssup);
+    va_end(argssup);
+    }
+  va_end(args);
+  }
+return taille;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to create directory with all up       */
+/*      directories if they are missing.                */
+/*      Return true if successfull.                     */ 
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool rou_do_mkpdir(char *dirpath)
+
+{
+#define OPEP    "subrou.c:rou_do_mkpdir,"
+_Bool done;
+int taille;
+char *ptr;
+char locdir[PATH_MAX];
+int phase;;
+_Bool proceed;
+
+done=false;
+(void) strcpy(locdir,dirpath);
+ptr=(char *)0;
+taille=strlen(locdir);
+phase=0;
+proceed=(taille>0);
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //make sur directory is starting with /
+      if (locdir[0]!='/') {
+        (void) rou_alert(0,"%s, <%s> is not a directory name (Bug?)",OPEP,locdir);
+        phase=999;      //Trouble No need to go further!
+        }
+      break;
+    case 1      :       //cleaning directory ending buy '/'
+      taille--;
+      if ((taille>1)&&(locdir[taille]=='/'))
+        locdir[taille]='\000';
+      break;
+    case 2      :       //is directory existing?
+      DIR *dirp;
+
+      if ((dirp=opendir(locdir))!=(DIR *)0) {
+        (void) closedir(dirp);
+        done=true;
+        phase=999;      //No need to go further
+        }
+      break;
+    case 3      :       //is directory up existing?
+      if ((ptr=strrchr(locdir,'/'))!=(char *)0) {
+        *ptr='\000';
+        if (rou_do_mkpdir(locdir)==false) 
+          phase=999;    //unable to create up directory!
+        }
+      break;
+    case 4      :       //creating the requested directory
+      if (mkdir(dirpath,0755)<0) {
+        (void) rou_alert(0,"%s, unable to create directory <%s> (error=<%s>)",
+                              OPEP,dirpath,strerror(errno));
+        phase=999;    //unable to create directory!
+        }
+      break;
+    case 5      :       //everything fine
+      done=true;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return done;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return the time difference between */
+/*     current real-time at nano-second level and      */
+/*      time reference.                                 */
+/*     time difference unit is millisec.               */
+/*                                                      */
+/********************************************************/
+PUBLIC unsigned int rou_getdifftime(TIMESPEC *start)
+
+{
+unsigned int result;
+
+result=0;
+if (start!=(TIMESPEC *)0) {
+  TIMESPEC current;
+
+  (void) clock_gettime(CLOCK_REALTIME,&current);
+  result=(current.tv_sec-start->tv_sec)*1000;
+  result+=(current.tv_nsec-start->tv_nsec)/1000000;
+  }
+return result;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return the current time            */
+/*     expressed with a millisecond precision starting */
+/*     from the firt time it was called.               */
+/*                                                      */
+/********************************************************/
+PUBLIC unsigned int rou_getmillisec()
+
+{
+struct timeval newtime;
+(void) gettimeofday(&newtime,(struct timezone *)0);
+return (unsigned int)((newtime.tv_usec/1000));
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to transform the local system time in */
+/*     ASCII time stamp.                               */
+/*     Stored in STATIC memory area.                   */
+/*                                                      */
+/********************************************************/
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to transform the local system time in */
+/*     ASCII time stamp.                               */
+/*     Stored in STATIC memory area.                   */
+/*                                                      */
+/********************************************************/
+PUBLIC char *rou_ascsysstamp(time_t curtime)
+
+{
+#define TSTAMP "%a, %d %b %Y %T %z"
+
+static char ascstamp[100];
+
+(void) strftime(ascstamp,sizeof(ascstamp),TSTAMP,localtime(&curtime));
+return ascstamp;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*      Returne the version number and application      */
+/*      name.                                           */
+/*                                                      */
+/********************************************************/
+PUBLIC const char *rou_getversion()
+
+{
+char *vers;
+
+vers=VERSION"."RELEASE;
+if (strlen(BRANCH)>0)
+  vers=VERSION"."RELEASE"-"BRANCH;
+return vers;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to free memory used by a              */
+/*     string, do not proceed if pointer is NULL.       */
+/*                                                      */
+/********************************************************/
+PUBLIC char *rou_freestr(register char *str)
+
+{
+if (str!=(char *)0) {
+  (void) free(str);
+  str=(char *)0;
+  }
+return str;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to get rid of any \r or \n at the end */
+/*     of a string.                                    */
+/*                                                     */
+/********************************************************/
+char *rou_stripcrlf(char *line)
+
+{
+if (line!=(char *)0)
+  line[strcspn(line,"\r\n")]='\000';
+return line;
+}
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Subroutine to count the number of items within  */
+/*     a list of pointer.                              */
+/*                                                     */
+/********************************************************/
+PUBLIC int rou_nbrlist(void **list)
+
+
+{
+int num;
+
+num=0;
+if (list!=(void **)0) {
+  while (*list!=(void *)0) {
+    num++;
+    list++;
+    }
+  }
+return num;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Subroutine to add a pointer (not null)          */
+/*     to a list of pointer.                           */
+/*                                                     */
+/********************************************************/
+PUBLIC void **rou_addlist(void **list,void *entry)
+
+{
+if (entry!=(void *)0) {
+  register int num;
+
+  num=0;
+  if (list==(void  **)0)
+    list=(void **)calloc(num+2,sizeof(void *));
+  else {
+    register void **ptr;
+
+    ptr=list;
+    while (*ptr!=(void *)0) {
+      num++;
+      ptr++;
+      }
+    list=realloc((void *)list,(num+2)*sizeof(void *));
+    }
+  list[num]=entry;
+  num++;
+  list[num]=(void *)0;
+  }
+return list;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to free one entry part of list        */
+/*      return true if found, false otherwize.          */
+/*                                                     */
+/********************************************************/
+PUBLIC _Bool rou_rmlist(void **list,void *entry,genfree_t handler)
+
+{
+_Bool found;
+
+found=false;
+if ((list!=(void **)0)&&(entry!=(void **)0)) {
+  void **ptr;
+
+  ptr=list;
+  while (*ptr!=(void *)0) {
+    if (*ptr==entry) {
+      void **nxt;
+    
+      nxt=ptr+1;
+      if (handler!=(genfree_t)0)
+        (void) handler(*ptr);
+      while (*ptr!=(void *)0) {
+        *ptr=*nxt;
+        ptr++;
+        nxt++;
+        }
+      found=true;
+      break;
+      }
+    ptr++;
+    }
+  }
+return found;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to free all list entries, return a    */
+/*      NULL pointer.                                   */
+/*                                                     */
+/********************************************************/
+PUBLIC void **rou_freelist(void **list,genfree_t handler)
+
+{
+if (list!=(void **)0) {
+  void **ptr;
+
+  ptr=list;
+  while (*ptr!=(void *)0) {
+    (void) handler(*ptr);
+    ptr++;
+    }
+  (void) free(list);
+  list=(void **)0;
+  }
+return list;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*      Returne set a new application name to be used in*/
+/*      logs.                                           */
+/*                                                      */
+/********************************************************/
+PUBLIC char *rou_setappname(const char *newname)
+
+{
+char *name;
+
+name=(char *)0;
+if (newname!=(char *)0) {
+  if (appname!=(char *)0) 
+    (void) free(appname);
+  appname=strdup(newname);
+  name=appname;
+  }
+return name;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*      Returne set a new exec/binary name.             */
+/*      logs.                                           */
+/*                                                      */
+/********************************************************/
+PUBLIC const char *rou_setexecname(const char *newexecname)
+
+{
+(void) memset(execname,'\000',sizeof(execname));
+(void) strncpy(execname,newexecname,sizeof(execname)-1);
+return execname;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     procedure to allocate a path within the root    */
+/*      directory if needed                             */
+/*                                                     */
+/********************************************************/
+PUBLIC char *rou_apppath(const char *path)
+
+{
+char *root;
+char *newpath;
+
+root="";
+newpath=(char *)0;
+if (rootdir!=(char *)0)
+  root=rootdir;
+(void) rou_asprintf(&newpath,"%s%s",root,path);
+return newpath;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Subroutine to log event on syslog               */
+/*                                                     */
+/********************************************************/
+PUBLIC void rou_valert(const int dlevel,const char *fmt,va_list ap)
+
+#define        DEBMAX  140
+
+{
+char lvl[10];
+char strloc[10000];
+
+(void) snprintf(lvl,sizeof(lvl),"(dl=%02d) ",dlevel);
+(void) memset(strloc,'\000',sizeof(strloc));
+(void) vsnprintf(strloc,sizeof(strloc)-1,fmt,ap);
+if (foreground==true)
+  (void) fprintf(stderr,"%s%s\n",lvl,strloc);
+else {
+  char *ptr;
+  ptr=strloc;
+  while (strlen(ptr)>DEBMAX) {
+    char locline[DEBMAX+10];
+
+    (void) strncpy(locline,ptr,DEBMAX);
+    locline[DEBMAX]='\000';
+    (void) syslog(LOG_INFO,"%s%s",lvl,locline);
+    ptr +=DEBMAX;
+    (void) strcpy(lvl,"");
+    } 
+  if (strlen(ptr)>0)
+    (void) syslog(LOG_INFO,"%s%s",lvl,ptr);
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Subroutine to log event on syslog               */
+/*                                                     */
+/********************************************************/
+PUBLIC void rou_alert(const int dlevel,const char *fmt,...)
+
+{
+if (debug>=dlevel) {
+  va_list args;
+
+  va_start(args,fmt);
+  (void) rou_valert(dlevel,fmt,args);
+  va_end(args);
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Subroutine to do live debug on a specific       */
+/*      function.                                       */
+/*                                                     */
+/********************************************************/
+PUBLIC void rou_dbglive(const int dlevel,const char *prcref,const char *fmt,...)
+
+{
+if ((debug>=dlevel)&&(dbglive!=(char *)0)) {
+  if (strstr(dbglive,prcref)!=(char *)0) {
+    char *nstr;
+    va_list args;
+
+    va_start(args,fmt);
+    (void) rou_asprintf(&nstr,"%s: %s",prcref,fmt);
+    (void) rou_valert(dlevel,nstr,args);
+    va_end(args);
+    (void) free(nstr);
+  }
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Subroutine to CORE-DUMP the application         */
+/*                                                     */
+/********************************************************/
+PUBLIC void rou_crash(const char *fmt,...)
+
+{
+#define        RELAX   5
+va_list args;
+char strloc[10000];
+
+va_start(args,fmt);
+(void) vsnprintf(strloc,sizeof(strloc),fmt,args);
+(void) rou_alert(0,"Crashed on purpose:");
+(void) rou_alert(0,"\t--> '%s'",strloc);
+(void) rou_alert(0,"Crash delayed by '%d' second",RELAX);
+(void) sleep(RELAX);   //To avoid immediat restart
+va_end(args);
+(void) kill(getpid(),SIGSEGV);
+(void) exit(-1);       //unlikely to reach here
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to display an core_dump               */
+/*     message and terminate application               */
+/*                                                     */
+/********************************************************/
+PUBLIC void rou_core_dump(const char *fmt,...)
+
+{
+#define MAXCRASH        10      //maximun crash file within drop zone
+#define COREDELAY        5      //number of second for core delay
+
+va_list args;
+char *crashdir;
+_Bool doabort;
+_Bool proceed;
+int phase;
+
+va_start(args,fmt);
+crashdir=(char *)0;
+doabort=false;
+phase=0;
+proceed=true;
+(void) rou_valert(0,fmt,args);
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG core phase='%d'",phase);
+  switch (phase) {
+    case 0      :               //find out if crashdir is existing
+      if ((crashdir=get_dropzone())==(char *)0)
+        phase=999;              //no need to go further.
+      break;
+    case 1      :               //can we access the crash dir
+      if (chdir(crashdir)<0) {    //Unable to access crashdir
+        (void) free(crashdir);
+        phase=999;              //no need to go further.
+        }
+      break;
+    case 2      :               //no too much crash so fare?
+      DIR *dirp;
+
+      (void) rou_alert(0,"Trying to store core-dump within directory <%s>",
+                         crashdir);
+      if ((dirp=opendir("."))!=(DIR *)0) {
+        int nument;
+        struct dirent *entry;
+
+        nument=0;
+        while ((entry=readdir(dirp))!=(struct dirent *)0) {
+          if (entry->d_type==DT_REG)
+            nument++;
+          }
+        (void) closedir(dirp);
+        if (nument>MAXCRASH) {
+          (void) rou_alert(0,"Aborting Coredump file generation!");
+          (void) rou_alert(0,"Too many crash files already within <%s>",
+                              crashdir);
+          phase=999;          //No allowed to do abort
+          }
+        }
+      else
+        (void) rou_alert(0,"Unable to open crashdir <%s> PWD=%s (error=<%s>",
+                            crashdir,get_current_dir_name(),strerror(errno));
+      (void) free(crashdir);
+      break;
+    case 3      :               //Ok we can call abort
+      doabort=true;
+      break;
+    default     :
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+(void) sleep(COREDELAY);       /*to avoid crash avalanche      */
+va_end(args);
+if (doabort==true) {
+  (void) abort();      /*doing to do the abort         */
+  }
+(void) rou_alert(0,"Unable to dump core memory");
+(void) exit(-1);       //Theoriticaly unreachabe if
+                        //abort was allowed
+#undef  MAXCRASH
+#undef  COREDELAY
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to clean a line form a configuration  */
+/*     line, removing comment part and end of space    */
+/*      within the line.                                */
+/*                                                     */
+/********************************************************/
+PUBLIC char *rou_clean_conf_line(char *line)
+
+{
+if (line!=(char *)0) {
+  char *ptr;
+  int taille;
+
+  if ((ptr=strchr(line,'#'))!=(char *)0)
+    *ptr='\000';
+  taille=strlen(line);
+  while (taille>0) {
+    taille--;
+    ptr=line+taille;
+    switch (*ptr) {
+      case ' '  :       //Space characteres
+      case '\t' :
+      case '\r' :
+      case '\n' :
+        *ptr='\000';
+        break;
+      default   :       //end of search
+        taille=0;
+        break;  
+      }
+    }
+  }
+return line;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to load the configuartion file        */
+/*     message and terminate application               */
+/*                                                     */
+/********************************************************/
+PUBLIC void rou_loadconfig(char *conffile,_Bool load)
+
+{
+#define OPEP    "subrou.c:rou_loadconfig,"
+
+FILE *fichier;
+char line[300];
+char *name;
+char *value;
+char *ptr;
+int phase;
+_Bool proceed;
+
+fichier=(FILE *)0;
+(void) memset(line,'\000',sizeof(line));
+name=(char *)0;
+value=(char *)0;
+ptr=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Opening the config file
+      if ((fichier=fopen(conffile,"r"))==(FILE *)0) {
+        char cwd[PATH_MAX];
+
+        (void) getcwd(cwd, sizeof(cwd));
+        (void) fprintf(stdout,"%s exiting!, Unable to open file <%s> "
+                              "within <%s> (error=<%s>)\n",
+                              OPEP,conffile,cwd,strerror(errno));
+        (void) exit(-1);//Big Trouble
+        }
+      break;
+    case 1      :       //scanning file
+      (void) memset(line,'\000',sizeof(line));
+      if ((ptr=fgets(line,sizeof(line)-1,fichier))==(char *)0)
+        phase=999;      //scan terminated 
+      break;
+    case 2      :       //trimming line
+      (void) rou_clean_conf_line(line);
+      //check remaining clean line
+      if (strlen(line)==0)
+        phase=0;        //next line
+      break;
+    case 3      :       //Spliting data
+      if ((ptr=strchr(line,'='))==(char *)0)
+        phase=0;        //line without '='
+      break;
+    case 4      :       //set name and value
+      name=line;
+      value=ptr+1;
+      *ptr='\000';
+      while ((ptr=strrchr(name,' '))!=(char *)0)
+        *ptr='\000';
+      while ((ptr=strrchr(name,'\t'))!=(char *)0)
+        *ptr='\000';
+      while ((value[0]==' ')||(value[0]=='\t'))
+        value++;
+      //removing quote if present
+      if (value[0]=='"')
+        value++;
+      if ((ptr=strrchr(value,'"'))!=(char *)0)
+        *ptr='\000';
+      //make sure we have data to set
+      if ((strlen(name)==0)||(strlen(value)==0))
+        phase=0;        //No name or value??
+      break;
+    case 5      :       //set name and value
+      if (load==true) 
+        (void) setenv(name,value,true);
+      else
+        (void) unsetenv(name);
+      phase=0;
+      break;
+    default     :       //SAFE Guard
+      (void) fclose(fichier);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to "open/close" module and do         */
+/*      homework purpose                                */
+/*      return zero if everything right                 */
+/*                                                      */
+/********************************************************/
+PUBLIC int rou_modesubrou(_Bool mode)
+
+{
+#define OPEP    "subrou.c:rou_modesubrou"
+
+int status;
+
+status=0;
+if (mode!=modopen) {
+  switch ((int)mode) {
+    case true     :
+      off64_time=(time_t)0;
+      off_date=(time_t)0;
+      (void) srand(getpid()*355);
+      if (foreground==true)
+        (void) srand((int)(M_PI*100000000));
+      if (appname==(char *)0)
+        appname=strdup(APPNAME);
+      (void) openlog(appname,LOG_NDELAY|LOG_PID,LOG_DAEMON);
+      break;
+    case false    :
+      (void) closelog();
+      appname=rou_freestr(appname);
+      rootdir=rou_freestr(rootdir);
+      break;
+    default       :
+      (void) fprintf(stderr,"Calling %s with wrong mode='%d' (Bug?!):",
+                            OPEP,(int)mode);
+      status=-1;
+      break;
+    }
+  modopen=mode;
+  }
+return status;
+#undef  OPEP
+}
diff --git a/lib/subrou.d b/lib/subrou.d
new file mode 100644 (file)
index 0000000..25829e7
--- /dev/null
@@ -0,0 +1,72 @@
+subrou.o subrou.d : subrou.c /usr/include/stdc-predef.h /usr/include/sys/stat.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/endian.h /usr/include/bits/endianness.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/stat.h \
+ /usr/include/bits/struct_stat.h /usr/include/sys/time.h \
+ /usr/include/bits/types/struct_timeval.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h /usr/include/dirent.h \
+ /usr/include/bits/dirent.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/dirent_ext.h /usr/include/errno.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/math.h \
+ /usr/include/bits/libc-header-start.h /usr/include/bits/math-vector.h \
+ /usr/include/bits/libm-simd-decl-stubs.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/bits/flt-eval-method.h \
+ /usr/include/bits/fp-logb.h /usr/include/bits/fp-fast.h \
+ /usr/include/bits/mathcalls-macros.h \
+ /usr/include/bits/mathcalls-helper-functions.h \
+ /usr/include/bits/mathcalls.h /usr/include/signal.h \
+ /usr/include/bits/signum-generic.h /usr/include/bits/signum-arch.h \
+ /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/types/stack_t.h \
+ /usr/include/sys/ucontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigstksz.h /usr/include/bits/ss_flags.h \
+ /usr/include/bits/types/struct_sigstack.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/stdio.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/stdlib.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/alloca.h /usr/include/bits/stdlib-float.h \
+ /usr/include/string.h /usr/include/bits/types/locale_t.h \
+ /usr/include/bits/types/__locale_t.h /usr/include/strings.h \
+ /usr/include/syslog.h /usr/include/sys/syslog.h \
+ /usr/include/bits/syslog-path.h /usr/include/time.h \
+ /usr/include/bits/time.h /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h numver.h
diff --git a/lib/subrou.h b/lib/subrou.h
new file mode 100644 (file)
index 0000000..c7318a2
--- /dev/null
@@ -0,0 +1,129 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/************************************************/
+/*                                             */
+/*     Low level subroutine declaration        */
+/*                                             */
+/************************************************/
+#ifndef        SUBROU
+#define SUBROU
+
+#include        <linux/types.h>
+#include        <stdbool.h>
+#include       <stdarg.h>
+#include        <time.h>
+
+
+#define APPNAME "mailleur"      //application name
+#define REALM   APPNAME"-email" //application REALM (DIGEST-MD5)
+#define ITSOK           0       //to check errno against no error
+
+//defining database #define
+#define USE_NODB        0       //No DB TYPE defined
+#define USE_POSTGRESQL  1
+#define USE_MYSQL       2
+
+#define SQLENV          5       //number of ENV parameters for 
+                                //SQL definition
+                        
+typedef void (*genfree_t)(void *);
+
+typedef struct timespec TIMESPEC;
+
+//---   Variables defined within subrou.c       ---------
+extern int             debug;  //application debug mode
+extern _Bool       foreground;  //process is in foreground mode
+
+extern char          *dbglive;  //string to check for live debugging
+extern char          *rootdir;  //application root directory
+extern char          *appname;  //application "official" name
+extern char        execname[];  //Application exec/binary name
+
+//---   Routines implemented within subrou.c    ---------
+
+//open/close memory leak detector.
+extern void rou_checkleak(_Bool onoff);
+
+//procedure to assign memory according a format and va list
+extern int rou_vasprintf(char **str,const char *fmt,va_list ap);
+
+//procedure to assign memory according a format and parameter list
+extern int rou_asprintf(char **str,const char *fmt,...);
+
+//procedure to return the application working realm
+extern const char *rou_getrealm();
+
+//procedure to create needed subdirectory
+extern _Bool rou_do_mkpdir(char *dirpath);
+
+//procedure to get the time difference between the current
+//time and a TIMESPEC starttime
+extern unsigned rou_getdifftime(TIMESPEC *start);
+
+//procedure to return the current number of milli-second
+extern unsigned int rou_getmillisec();
+
+//transform local system time in ASCII stamp.
+extern char *rou_ascsysstamp(time_t curtime);
+
+//return program version
+extern const char *rou_getversion();
+
+//Procedure to free a string pointer and return 
+//a NULL pointer
+extern char *rou_freestr(char *str);
+
+//procedure to remove '\r' or '\n' at end of a line
+extern char *rou_stripcrlf(char *line);
+
+//to return the number of element within a list
+extern int rou_nbrlist(void **list);
+
+//to add a not null pointer to a list of pointer
+extern void **rou_addlist(void **list,void *entry);
+
+//Procedure to free one entry part of list
+//return true if found, false otherwise. 
+extern _Bool rou_rmlist(void **list,void *entry,genfree_t handler);
+
+//Procedure to free all entries of list
+extern void **rou_freelist(void **list,genfree_t handler);
+
+//change the application name
+extern char *rou_setappname(const char *newname);
+
+//change the execname name
+extern const char *rou_setexecname(const char *newexecname);
+
+//to compute an application path with the root directory
+extern char *rou_apppath(const char *path);
+
+//to display message on console (verbose mode) or
+//via syslog (LOG_INFO) using variable argument list macros
+void rou_valert(const int dlevel,const char *fmt,va_list ap);
+
+//return  a difference between 2 chrono
+//to display message on console (verbose mode) or
+//via syslog (LOG_DAEMON)
+extern void rou_alert(const int dlevel,const char *fmt,...);
+
+//procedure to debug live daemon process
+extern void rou_dbglive(const int dlevel,const char *prcref,const char *fmt,...);
+
+//To do an on purpose crash the application with an
+//explication message
+extern void rou_crash(const char *fmt,...);
+
+//To do an on purpose application memory core dump
+//with an explication message
+extern void rou_core_dump(const char *fmt,...);
+
+//clean configuration file line
+extern char *rou_clean_conf_line(char *line);
+
+//load/unload environement configuration
+extern void rou_loadconfig(char *conffile,_Bool load);
+
+//homework to be done before starting/stoping module.
+extern int rou_modesubrou(_Bool mode);
+
+#endif
diff --git a/lib/toremake b/lib/toremake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/lib/unidig.c b/lib/unidig.c
new file mode 100644 (file)
index 0000000..e746563
--- /dev/null
@@ -0,0 +1,501 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Implement all procedure to use digest-md5       */
+/*     authentication exchange.                        */
+/*                                                     */
+/********************************************************/
+#include        <openssl/evp.h>
+#include        <openssl/hmac.h>
+#include        <stdio.h>
+#include        <string.h>
+
+#include       "subrou.h"
+#include       "subcnv.h"
+#include       "unidig.h"
+
+#define ALGO    "md5-sess"
+#define MAXBUF  2000
+#define CHARSET "utf-8"
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to scan data and take care of the     */
+/*      data format "abc\"def" to become abc\"def       */
+/*                                                      */
+/********************************************************/
+static void scanliteral(char *data)
+
+{
+if (*data=='"') {
+  char *ptr;
+
+  (void) memmove(data,data+1,strlen(data+1)+1);
+  while ((ptr=strchr(data,'"'))!=(char *)0) {
+    if (strlen(data)>strlen(ptr)) {
+      if (*(ptr-1)=='\\') {
+        data=ptr+1;
+        continue;
+        }
+      }
+    *ptr='\000';
+    }
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to scan ONE entry from the challenge  */
+/*      response.                                       */
+/*                                                      */
+/********************************************************/
+static void scanfield(RSPTYP *resp,char *entry)
+
+{
+static char *voc[]={
+        "charset",
+        "cnonce",
+        "digest-uri",
+        "nc",
+        "nonce",
+        "qop",
+        "realm",
+        "response",
+        "username",
+        (char*)0
+        };
+
+if (strlen(entry)>0) {
+  char *ptr;
+
+  if ((ptr=strchr(entry,'='))!=(char *)0) {
+    int num;
+
+    *ptr='\000';
+    ptr++;
+    (void) scanliteral(ptr);
+    for (num=0;voc[num]!=(char *)0;num++) {
+      if (strcasecmp(voc[num],entry)!=0)
+        continue;
+      switch (num) {
+        case 0  :       //charset
+          resp->charset=rou_freestr(resp->charset);
+          resp->charset=strdup(ptr);
+          break;
+        case 1  :       //cnonce
+          resp->cnonce=rou_freestr(resp->cnonce);
+          resp->cnonce=strdup(ptr);
+          break;
+        case 2  :       //digest-uri
+          resp->digesturi=rou_freestr(resp->digesturi);
+          resp->digesturi=strdup(ptr);
+          break;
+        case 3  :       //nc (unsigned long)
+          (void) sscanf(ptr,"%lx",&(resp->nc));
+          break;
+        case 4  :       //nonce
+          resp->nonce=rou_freestr(resp->nonce);
+          resp->nonce=strdup(ptr);
+          break;
+        case 5  :       //qop
+          resp->qop=rou_freestr(resp->qop);
+          resp->qop=strdup(ptr);
+          break;
+        case 6  :       //realm
+          resp->realm=rou_freestr(resp->realm);
+          resp->realm=strdup(ptr);
+          break;
+        case 7  :       //response
+          resp->response=rou_freestr(resp->response);
+          resp->response=strdup(ptr);
+          break;
+        case 8  :       //username
+          resp->username=rou_freestr(resp->username);
+          resp->username=strdup(ptr);
+          break;
+        default :
+          (void) rou_alert(0,"name=<%s> field=<%s>",entry,ptr);
+          break;
+        }
+      }
+    }
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to free all memroy used by a response */
+/*      type structure.                                 */
+/*                                                      */
+/********************************************************/
+PUBLIC RSPTYP *dig_freeresp(RSPTYP *resp)
+
+{
+if (resp!=(RSPTYP *)0) {
+  resp->response=rou_freestr(resp->response);
+  resp->charset=rou_freestr(resp->charset);
+  resp->qop=rou_freestr(resp->qop);
+  resp->cnonce=rou_freestr(resp->cnonce);
+  resp->nonce=rou_freestr(resp->nonce);
+  resp->digesturi=rou_freestr(resp->digesturi);
+  resp->realm=rou_freestr(resp->realm);
+  resp->username=rou_freestr(resp->username);
+  (void) free(resp);
+  resp=(RSPTYP *)0;
+  }
+return resp;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to parse the challenge structure and  */
+/*      return an RSPTYP pointer structure              */
+/*                                                      */
+/********************************************************/
+PUBLIC RSPTYP *dig_parseresp(char *response)
+
+{
+#define OPEP    "unidig.c:dig_parseresp,"
+
+RSPTYP *resp;
+
+resp=(RSPTYP *)0;
+if ((response!=(char *)0)&&(strlen(response)>0)) {
+  char *cpy;
+  char *next;
+
+  resp=calloc(1,sizeof(RSPTYP));
+  cpy=strdup(response);
+  next=cpy;
+  while (next!=(char *)0) {
+    char *ptr;
+
+    if ((ptr=strchr(next,','))!=(char *)0) {
+      *ptr='\000';
+      ptr++;
+      }
+    (void) scanfield(resp,next);
+    next=ptr;
+    }
+  cpy=rou_freestr(cpy);
+  }
+return resp;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to hash a string with MD5 hash        */
+/*      an return a 16+1 Bytes.                         */
+/*                                                      */
+/********************************************************/
+PUBLIC MD5TYP *dig_hashmd5(unsigned char *seq,unsigned int length)
+
+{
+#define OPEP    "subdig.c:dig_hashmd5,"
+
+MD5TYP *md5;
+EVP_MD_CTX *mdctx;
+int phase;
+_Bool proceed;
+
+md5=(MD5TYP *)0;
+mdctx=(EVP_MD_CTX *)0;
+phase=1;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //do we hae a sequence?
+      if ((seq==(unsigned char *)0)&&(length==0)) 
+        phase=999;      //no need to go further
+      break;
+    case 1      :       //getting md5 context
+      if ((mdctx=EVP_MD_CTX_new())==(EVP_MD_CTX *)0) {
+        (void) rou_alert(0,"%s Unable to open MD5 context (System?)",OPEP);
+        phase=999;      //no need to go further
+        }
+      break;
+    case 2      :       //doing hashing
+      (void) EVP_DigestInit_ex(mdctx,EVP_md5(),(ENGINE *)0);
+      (void) EVP_DigestUpdate(mdctx,seq,length);
+      break;
+    case 3      :   {   //assign memory
+      unsigned int s;
+
+      md5=calloc(1,sizeof(MD5TYP));
+      (void) EVP_DigestFinal_ex(mdctx,(unsigned char *)md5,&s);
+      EVP_MD_CTX_free(mdctx);
+      }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return md5;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to crypt a string with MD5 hash       */
+/*      function.                                       */
+/*                                                      */
+/********************************************************/
+PUBLIC char *dig_cryptmd5(const void *key,unsigned char *seq)
+
+{
+#define OPEP    "subdig.c:dig_hashmd5,"
+
+char *hashmd5;          //function result
+unsigned char *d5;      //md5 hash result
+u_int reslen;
+int phase;
+int proceed;
+
+hashmd5=(char *)0;
+d5=(unsigned char *)0;
+reslen=-1;
+phase=1;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //do ve have good parameters
+      if ((key==(const void  *)0)||(seq==(unsigned char *)0)) {
+        (void) rou_alert(0,"%s key or seq undefined (Bug?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //computing sequence
+      d5=HMAC(EVP_md5(),key,strlen(key),seq,strlen((const char *)seq),d5,&reslen);
+      if (d5==(unsigned char *)0) {
+        (void) rou_alert(0,"%s No MD5 for key=<%s> and seq=<%s> (Bug?)",
+                            OPEP,(char *)key,seq);
+        phase=999;
+        }
+      break;
+    case 2      :       //duplicate md5 result
+      if (reslen>0) {
+        hashmd5=(char *)calloc(reslen+1,sizeof(char));
+        (void) memmove((void *)hashmd5,(void *)d5,reslen);
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return hashmd5;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to convert an MD5TYP array in         */
+/*      hexadecimal structure to be a pure/plain MD5TYP */
+/*      array.                                          */
+/*      Return (MD5TYP *)0 if trouble.                  */
+/*                                                      */
+/********************************************************/
+PUBLIC MD5TYP *dig_get_plain_md5(char *hexa)
+
+{
+MD5TYP *plain;
+
+plain=(MD5TYP *)0;
+if (strlen(hexa)==sizeof(MD5TYP)*2) {
+  _Bool ok;
+  MD5TYP travail;
+  char tmp[3];
+
+  ok=true;
+  (void) memset(travail,'\000',sizeof(travail));
+  (void) memset(tmp,'\000',sizeof(tmp));
+  for (register int i=0;i<sizeof(MD5TYP);i++) {
+    (void) memcpy(tmp,hexa+(i*2),2);
+    if (sscanf(tmp,"%hhx",&(travail[i]))!=1) {
+      ok=false; //Wrong hexa decimal number
+      break;
+      }
+    }
+  if (ok==true) {
+    plain=calloc(sizeof(MD5TYP),sizeof(char));
+    (void) memmove(plain,travail,sizeof(MD5TYP));
+    }
+  }
+return plain;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to generate a unique DIGEST-M5        */
+/*      challenge as an B64 string.                     */
+/*                                                      */
+/********************************************************/
+PUBLIC char *dig_getchallenge()
+
+{
+#define OPEP    "unidig.c:dig_getchallenge,"
+
+static char *comp[]={
+        "realm=\"%s\"",         //the LOCAL realm
+        ",nonce=\"%s\"",
+        ",qop=%s",
+        ",algorithm=%s",
+        ",charset=%s",
+        (char*)0
+        };
+
+char *challenge;
+
+challenge=(char *)calloc(MAXBUF,sizeof(char));
+for (int num=0;comp[num]!=(char *)0;num++) {
+  char loc[MAXBUF/2];
+
+  (void) memset(loc,'\000',sizeof(loc));
+  switch (num) {
+    case 0      :       //realm
+      (void) snprintf(loc,sizeof(loc),comp[num],rou_getrealm());
+      break;
+    case 1      :  {     //nonce
+      char *nonce;
+      char *b64;
+
+      nonce=cnv_getrndstr(30);
+      b64=cnv_setb64(nonce); 
+      (void) snprintf(loc,sizeof(loc),comp[num],b64);
+      b64=rou_freestr(b64);
+      nonce=rou_freestr(nonce);
+      }
+      break;
+    case 2      :       //qop
+      (void) snprintf(loc,sizeof(loc),comp[num],"auth");
+      break;
+    case 3      :       //algorithm
+      (void) snprintf(loc,sizeof(loc),comp[num],ALGO);
+      break;
+    case 4      :       //charset
+      (void) snprintf(loc,sizeof(loc),comp[num],CHARSET);
+      break;
+    default     :       //none expect field BUG!
+      (void) rou_alert(0,"%s field[%d]=%s not treated (BUG!)",OPEP,num,comp[num]);
+      challenge=rou_freestr(challenge);
+      break;
+    }
+  if (strlen(loc)>0)
+    (void) strcat(challenge,loc); 
+  }
+return challenge;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to compute local response to challenge*/
+/*      and check if the compupted answer is the same as*/
+/*      reponse available within RSPTYP record.         */
+/*                                                      */
+/********************************************************/
+PUBLIC char *dig_hashresp(RSPTYP *resp,char *mode,char *hash)
+
+{
+#define OPEP    "unidig.c:dig_hashresp,"
+
+char *HA0;
+char *HA1;
+char *HA2;
+MD5TYP *A1;
+char seq[400];
+int phase;
+_Bool proceed;
+
+
+HA0=(char *)0;
+HA1=(char *)0;
+HA2=(char *)0;
+(void) memset(seq,'\000',sizeof(seq));
+phase=0;
+proceed=(resp!=(RSPTYP *)0);
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //computing A1 from data-base contents
+      A1=dig_get_plain_md5(hash);
+      if (A1==(MD5TYP *)0) {
+        (void) rou_alert(0,"%s Unable to convert hash=<%s>",OPEP,hash);
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 1      :  {    //computing hash HA1
+      char *ptr;
+      int max;
+
+      //starting algorithm value is "MD5-sess"
+      (void) memset(seq,'\000',sizeof(seq));
+      (void) memmove(seq,(char *)A1,sizeof(MD5TYP));
+      (void) free(A1);
+      ptr=seq+sizeof(MD5TYP);
+      max=sizeof(seq)-sizeof(MD5TYP);
+      (void) snprintf(ptr,max,":%s:%s",resp->nonce,resp->cnonce);
+      A1=dig_hashmd5((unsigned char *)seq,sizeof(MD5TYP)+strlen(ptr));
+      //ending algorithm value is "MD5-sess"
+      HA1=cnv_tohexa((char *)A1,sizeof(MD5TYP));
+      (void) free(A1);
+      }
+      break;
+    case 2      :  {    //computing HA2
+      MD5TYP *A2;
+
+      (void) snprintf(seq,sizeof(seq),"%s:%s",mode,resp->digesturi);
+      A2=dig_hashmd5((unsigned char *)seq,strlen(seq));
+      HA2=cnv_tohexa((char *)A2,sizeof(MD5TYP));
+      (void) free(A2);
+      }
+      break;
+    case 3      :  {    //computing response
+      MD5TYP *A0;
+
+      (void) snprintf(seq,sizeof(seq),"%s:%s:%08lx:%s:%s:%s",
+                                      HA1,resp->nonce,resp->nc,
+                                      resp->cnonce,resp->qop,HA2);
+      A0=dig_hashmd5((unsigned char *)seq,strlen(seq));
+      HA0=cnv_tohexa((char *)A0,sizeof(MD5TYP));
+      (void) free(A0);
+      }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+HA2=rou_freestr(HA2);
+HA1=rou_freestr(HA1);
+(void) rou_alert(0,"JMPDBG mode=<%s> HA0=<%s>",mode,HA0);
+return HA0;
+
+#undef  OPEP
+}
diff --git a/lib/unidig.d b/lib/unidig.d
new file mode 100644 (file)
index 0000000..774d5b5
--- /dev/null
@@ -0,0 +1,72 @@
+unidig.o unidig.d : unidig.c /usr/include/stdc-predef.h /usr/include/openssl/evp.h \
+ /usr/include/openssl/macros.h /usr/include/openssl/opensslconf.h \
+ /usr/include/openssl/configuration.h /usr/include/openssl/opensslv.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/__fpos_t.h \
+ /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/e_os2.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/endian.h /usr/include/bits/endianness.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/bits/stdint-least.h \
+ /usr/include/openssl/safestack.h /usr/include/openssl/stack.h \
+ /usr/include/openssl/core.h /usr/include/openssl/core_dispatch.h \
+ /usr/include/openssl/indicator.h /usr/include/openssl/params.h \
+ /usr/include/openssl/bn.h /usr/include/openssl/crypto.h \
+ /usr/include/stdlib.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/time.h \
+ /usr/include/bits/time.h /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/bnerr.h /usr/include/openssl/bio.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/evperr.h \
+ /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h \
+ /usr/include/openssl/asn1.h /usr/include/openssl/asn1err.h \
+ /usr/include/openssl/objectserr.h /usr/include/openssl/hmac.h \
+ /usr/include/string.h /usr/include/strings.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h subcnv.h \
+ unidig.h
diff --git a/lib/unidig.h b/lib/unidig.h
new file mode 100644 (file)
index 0000000..8b1856c
--- /dev/null
@@ -0,0 +1,51 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Define all procedure to use digest-md5          */
+/*      authentication exchanges.                       */
+/*                                                     */
+/********************************************************/
+#ifndef        UNIDIG
+#define UNIDIG
+
+typedef struct  {       //DIGEST-MD5 challange response structure
+        char *username; //username requesting authentication
+        char *realm;    //Challenge realm
+        char *digesturi;//Type of digest
+        char *nonce;    //server nonce
+        char *cnonce;   //client nonce
+        u_long nc;      //nonce count
+        char *qop;      //protection quality (authentication)
+        char *charset;  //used carset
+        char *response; //Challenge md5 response
+        }RSPTYP;
+
+//MD5 hashing result
+typedef unsigned char MD5TYP[16];
+
+//procedure to free the response structure
+extern RSPTYP *dig_freeresp(RSPTYP *resp);
+
+//procedure to parse a challenge answer and return
+//the structure
+extern RSPTYP *dig_parseresp(char *response);
+
+//procedure to hash (md5) a string result is a MD5 arrays
+extern MD5TYP *dig_hashmd5(unsigned char *seq,unsigned int length);
+
+//procedure to convert an MD5 string (HEXA representation)
+//to an pure MD5TYP structure
+extern MD5TYP *dig_get_plain_md5(char *hexa);
+
+//Procedure to crypt a string with MD5 hash function
+extern char *dig_cryptmd5(const void *key,unsigned char *seq);
+
+//Procedure to generate a DISGEST-MD5 challaneg as a
+//b64 string.
+extern char *dig_getchallenge();
+
+//Procedure to compute local response to challenge and
+//check if the remote session is the same
+extern char *dig_hashresp(RSPTYP *resp,char *mode,char *hash);
+
+#endif
diff --git a/lib/unidns.c b/lib/unidns.c
new file mode 100644 (file)
index 0000000..ba7dc40
--- /dev/null
@@ -0,0 +1,814 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Implement all routine to manage SMTP low level  */
+/*     exchange.                                       */
+/*                                                     */
+/********************************************************/
+#include        <arpa/inet.h>
+#include        <errno.h>
+#include        <malloc.h>
+#include        <resolv.h>
+#include        <stdbool.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <unistd.h>
+
+#include       "subrou.h"
+#include       "unidns.h"
+
+//spf1 marker within DNS record
+#define SPF1    "v=spf1 "
+
+#define         B64K   65536           //according US Cert VU#738331
+typedef        union   {
+        HEADER hdr;            //defined in resolv.h
+        u_char buf[B64K];      //according US Cert VU#738331
+       }RSPTYP;
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to compare 2 MX and return            */
+/*     a value according preference value              */
+/*                                                      */
+/********************************************************/
+static int cmpmx(const void *p1,const void *p2)
+
+{
+MXTYP *m1;
+MXTYP *m2;
+
+m1=((MXTYP **)p1)[0];
+m2=((MXTYP **)p2)[0];
+return m1->preference-m2->preference;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to query DNS information              */
+/*                                                      */
+/********************************************************/
+static int myquery(const char *dname,int class,int type,
+                   unsigned char *answer, int anslen)
+
+{
+#define        OPEP    "unidns.c:myquery,"
+
+int got;
+
+if ((got=res_query(dname,class,type,answer,anslen))<0) {
+  got=-1;                      /*trouble trouble*/
+  switch (errno) {
+    case  0            :       /* do not care  */
+      got=0;                   /*NO answer!    */
+      break;
+    case ETIMEDOUT     :       /*rqst timed out*/
+      (void) rou_alert(0,"%s Timeout on domain <%s>!",OPEP,dname);
+      break;
+    default    :
+      (void) rou_alert(0,"%s query error for domain <%s> type='%d' "
+                        "(error=<%d/%s>)",OPEP,dname,type,errno,strerror(errno));
+    }
+  }
+else {
+  if (got>anslen) {
+    (void) rou_alert(0,"%s query too by for domain <%s> type='%d' (got=%d)",
+                      OPEP,dname,type,got);
+    got=anslen;
+    }
+  }
+return got;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to extract text information           */
+/*     from DNS record.                                */
+/*                                                      */
+/********************************************************/
+static char **extracting(RSPTYP *rsp,int rsplen,const char *request,char *field)
+
+{
+#define        OPEP    "unidns.c:extracting,"
+
+char **list;
+int question;
+int answer;
+const unsigned char *msg;
+const unsigned char *eom;
+const unsigned char *cp;
+int rc;
+char buf[NS_PACKETSZ+1];
+
+list=(char **)0;
+question=ntohs(rsp->hdr.qdcount);
+answer=ntohs(rsp->hdr.ancount);
+msg=(unsigned char *)rsp;
+eom=(unsigned char *)rsp+rsplen;
+cp=(unsigned char *)rsp+sizeof(HEADER);
+while ((question-->0)&&(cp<eom)) {
+  if ((rc=dn_expand(msg,eom,cp,(char *)buf,sizeof(buf)-1))<0)
+    break;
+  cp +=rc+QFIXEDSZ;
+  }
+while((answer-->0)&&(cp<eom)) {
+  if ((rc=dn_expand(msg,eom,cp,(char *)buf,sizeof(buf)-1))<0) {
+    break;
+    }
+  if (rc>0) {
+    int type;
+    int class;
+    long ttl;
+    int dlen;
+    int collected;
+    const unsigned char *txtptr;
+    char *dynbuf;
+
+    cp+=rc;
+    GETSHORT(type,cp);
+    GETSHORT(class,cp);
+    GETLONG(ttl,cp);
+    GETSHORT(dlen,cp);
+    if (class!=1) {
+      (void) rou_alert(0,"%s unexpected class='%d' on request "
+                         "<%s> field=<%s> (ttl='%d')",
+                         OPEP,class,request,field,ttl);
+      cp+=dlen;
+      break;           /*no need to go further                 */
+      }
+    collected=0;
+    dynbuf=(char *)0;
+    switch (type) {
+      case T_MX                :
+        if ((rc=dn_expand(msg,eom,cp+2,(char *)buf,sizeof(buf)-1))>0) {
+          int pref;
+         char info[30];
+
+         pref=(((unsigned int)cp[0])<<8)|((unsigned int)cp[1]);
+         (void) snprintf(info,sizeof(info),"%d",pref);
+          list=(char **)rou_addlist((void **)list,(void *)strdup(info));
+          list=(char **)rou_addlist((void **)list,(void *)strdup(buf));
+         }
+       break;
+      case T_CNAME     :       
+       #ifdef GETCNAME_2011
+       /*we are not interested to collect CNAME for now (JMP Feb 2011) */
+        if ((rc=dn_expand(msg,eom,cp,(char *)buf,sizeof(buf)))>0) {
+          list=(char **)rou_addlist((void **)list,(void *)strdup("100"));
+          list=(char **)rou_addlist((void **)list,(void *)strdup(buf));
+         }
+       #endif
+       break;
+      case T_SPF       :
+      case T_TXT       :
+       txtptr=cp;
+        dynbuf=(char *)calloc(10,sizeof(char));
+       while (collected<dlen) {
+          u_int taille;
+
+         (void) memset(buf,'\000',sizeof(buf));
+          taille=((u_int)(txtptr[0]))&0X0FF; 
+         txtptr++;
+          (void) snprintf(buf,sizeof(buf),"%.*s",taille,txtptr);
+         dynbuf=(char *)realloc(dynbuf,strlen(dynbuf)+strlen(buf)+5);
+         (void) strcat(dynbuf,buf);
+         collected+=taille+1;
+         txtptr+=taille;
+          }
+        list=(char **)rou_addlist((void **)list,(void *)dynbuf);
+       break;
+      case T_A         :       {
+        char str[INET_ADDRSTRLEN];
+
+        (void) inet_ntop(AF_INET,cp,str,INET_ADDRSTRLEN);
+        list=(char **)rou_addlist((void **)list,(void *)strdup(str));
+       }
+       break;
+      case T_AAAA      :       
+       (void) rou_alert(0,"%s Unexpected T_AAAA field",OPEP);
+       break;
+      case T_PTR       :
+        if ((rc=dn_expand(msg,eom,cp,(char *)buf,sizeof(buf)-1))>0) {
+          list=(char **)rou_addlist((void **)list,(void *)strdup(buf));
+         }
+       break;
+      default          :
+       (void) rou_alert(0,"%s Unexpected '%d' field",OPEP,type);
+       break;
+      }
+    cp+=dlen;
+    }
+  }
+return list;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to extract a domain TXT               */
+/*     If the domain doesn't exist or domain           */
+/*     do not have a TXT, a void pointer is            */
+/*     returned.                                       */
+/*                                                      */
+/********************************************************/
+static char **gettxt(char *domain,int ttype,char *field)
+
+{
+char **inftxt;
+int answer;
+RSPTYP rsp;
+
+inftxt=(char **)0;
+(void) memset(rsp.buf,'\000',sizeof(rsp.buf));
+switch (answer=myquery(domain,C_IN,ttype,rsp.buf,sizeof(rsp.buf))) {
+  case -1      :       /*timeout               */
+    break;
+  case  0      :       /*no data available     */
+    break;
+  default      :       /*data                  */
+    inftxt=extracting(&rsp,answer,domain,field);
+    break;
+  }
+return inftxt;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Routine to extract ipaddr from addr             */
+/*                                                      */
+/********************************************************/
+static char *addrip(struct addrinfo *addinf)
+
+{
+static char straddr[INET6_ADDRSTRLEN];
+
+char *ipaddr;
+void *addr;
+
+ipaddr=straddr;
+addr=(void *)0;
+switch (addinf->ai_family) {
+  case  AF_INET6       :       /*IPV6          */
+    addr=&(((struct sockaddr_in6 *)addinf->ai_addr)->sin6_addr);
+    break;
+  case  AF_INET                :       /*IPV4          */
+  default              :
+    addr=&(((struct sockaddr_in *)addinf->ai_addr)->sin_addr);
+    break;
+  }
+if (inet_ntop(addinf->ai_family,addr,straddr,sizeof(straddr))==(char *)0) {
+  (void) rou_alert(0,"%s Unable to find IP for hostname <%s> (error=<%s>)",
+                    "unidns.c:dns_addrip,",addinf->ai_canonname,strerror(errno));
+  ipaddr=(char *)0;
+  }
+return ipaddr;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Routine to extract iplist in string format from */
+/*     addr list.                                      */
+/*                                                      */
+/********************************************************/
+static char **addriplist(struct addrinfo *addrlst)
+
+{
+char **iplist;
+struct addrinfo *pif;
+
+iplist=(char **)0;
+for (pif=addrlst;pif!=(struct addrinfo *)0;pif=pif->ai_next) {
+  char *straddr;
+
+  if ((straddr=addrip(pif))!=(char *)0)
+    iplist=(char **)rou_addlist((void **)iplist,(void *)strdup(straddr));
+  }
+return iplist;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to free memory used by BLKTYP entry   */
+/*                                                      */
+/********************************************************/
+PUBLIC BLKTYP *dns_freeblk(BLKTYP *blk)
+
+{
+if (blk!=(BLKTYP *)0) {
+  blk->sitename=rou_freestr(blk->sitename);
+  blk->targets=(char **)rou_freelist((void **)blk->targets,
+                                     (genfree_t)rou_freestr);
+  (void) free(blk);
+  blk=(BLKTYP *)0;
+  }
+return blk;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to add and entry with the list of     */
+/*      blacklist site.                                 */
+/*                                                      */
+/********************************************************/
+PUBLIC BLKTYP **dns_addblklist(BLKTYP **blklist,char *entry)
+
+
+{
+#define OPEP    "unidns.c:dns_addblklist,"
+
+if ((entry!=(char *)0)&&(strlen(entry)>0)) {
+  int delta;
+  int sofar;
+  char sitename[300];
+
+  (void) memset(sitename,'\000',sizeof(sitename));
+  sofar=0;
+  if (sscanf(entry,"%d %s%n",&delta,sitename,&sofar)==2) {
+    char **targets;
+    BLKTYP *blk;
+    char *ptr;
+
+    targets=(char **)0;
+    blk=calloc(1,sizeof(BLKTYP));
+    blk->delta=delta;
+    blk->sitename=strdup(sitename);
+    ptr=entry+sofar;
+    while ((ptr!=(char *)0)&&(strlen(ptr)>0)) {
+      char *coma;
+
+      while ((*ptr==' ')||(*ptr=='\t'))
+        ptr++;
+      if ((coma=strchr(ptr,','))!=(char *)0) {
+        *coma='\000';
+        coma++;
+        }
+      targets=(char **)rou_addlist((void **)targets,(void *)strdup(ptr)); 
+      ptr=coma;
+      }
+    blk->targets=targets;
+    blklist=(BLKTYP **)rou_addlist((void **)blklist,(void *)blk);
+    }
+  else 
+    (void) rou_alert(0,"%s, entry wrong format <%s> (config?)",OPEP,entry);
+  }
+return blklist;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to extract IPs from an hostname       */
+/*                                                      */
+/********************************************************/
+PUBLIC char **dns_get_ip_list(const char *hostname)
+
+{
+char **iplst;
+struct addrinfo hints;
+struct addrinfo *res;
+            
+iplst=(char **)0;
+(void) memset(&hints,'\000',sizeof(hints));
+hints.ai_family=PF_UNSPEC;
+hints.ai_socktype=SOCK_STREAM;
+if (getaddrinfo(hostname,(char *)0,&hints,&res)==0) {
+  iplst=addriplist(res);
+  (void) freeaddrinfo(res);
+  }
+return iplst;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to extract a hostname SPF             */
+/*     Search first for an SPF record, if not          */
+/*     implemented search for a TXT record             */
+/*     If the domain doesn't exist or domain           */
+/*     do not have a TXT or SPF, a void pointer        */
+/*     is returned.                                    */
+/*                                                      */
+/********************************************************/
+PUBLIC char *dns_getspf(char *domain)
+
+{
+char *spfrec;
+char **list;
+
+spfrec=(char *)0;
+if ((list=gettxt(domain,T_SPF,"SPF"))==(char **)0) 
+  list=gettxt(domain,T_TXT,"TXT");
+if (list!=(char **)0) {
+  char **ptr;
+
+  ptr=list;
+  while (*ptr!=(char *)0) {
+    if (strstr(*ptr,SPF1)==*ptr) {
+      spfrec=strdup(*ptr+strlen(SPF1));
+      break;
+      }
+    ptr++;
+    }
+  list=(char **)rou_freelist((void **)list,(genfree_t)rou_freestr);
+  }
+return spfrec;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to free memory used by a list of      */
+/*      MXTYP.                                          */
+/*                                                      */
+/********************************************************/
+PUBLIC MXTYP **dns_freemxlist(MXTYP **mxlist)
+
+{
+if (mxlist!=(MXTYP **)0) {
+  for (int i=0;mxlist[i]!=(MXTYP *)0;i++) {
+    mxlist[i]->mxip=(char **)rou_freelist((void **)mxlist[i]->mxip,
+                                          (genfree_t)rou_freestr);
+    mxlist[i]->mxname=rou_freestr(mxlist[i]->mxname);
+    (void) free(mxlist[i]);
+    }
+  (void) free(mxlist);
+  mxlist=(MXTYP **)0;
+  }
+return mxlist;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to extract a domain MX.               */
+/*     If the domain doesn't exist or domain           */
+/*     do not have a MX, a void pointer is             */
+/*     returned.                                       */
+/*     Retuned list is ordered according               */
+/*     MX preference (low preference first)            */
+/*                                                      */
+/********************************************************/
+PUBLIC MXTYP **dns_getmx(const char *domain)
+
+{
+#define OPEP    "unidns.c:dns_getmx"
+
+MXTYP **mxlist;
+char **list;
+int answer;
+RSPTYP rsp;
+int phase;
+_Bool proceed;
+
+mxlist=(MXTYP **)0;
+list=(char **)0;
+answer=0;
+(void) memset(&rsp,'\000',sizeof(rsp)); 
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Are the parameters available
+      if ((domain==(char *)0)||(strlen(domain)==0)) {
+        (void) rou_alert(0,"%s missing domainname",OPEP);
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 1      :       //cleaning domain
+      if (domain!=(char *)0) {  //always
+        char *ptr;
+
+        if ((ptr=strchr(domain,'@'))!=(char *)0) {
+          ptr++;
+          if (strlen(ptr)==0) {
+            (void) rou_alert(0,"%s Incorrect domainname <%s>",OPEP,domain);
+            phase=999;      //trouble trouble
+            }
+          domain=ptr;
+          }
+        }
+      break;
+    case 2      :       //Requesting MX for domain
+      answer=myquery(domain,C_IN,T_MX,rsp.buf,sizeof(rsp.buf));
+      if (answer<=0) 
+        phase=999;      //Trouble trouble to get MX
+      break;
+    case 3      :       //extracting result
+      if (answer>=sizeof(rsp.buf))
+        answer=sizeof(rsp.buf)-1;
+      list=extracting(&rsp,answer,domain,"MX");
+      if (list==(char **)0)
+        phase=999;      //list empty?
+      break;
+    case 4      :       //ordering list
+      answer=0;
+      for (int i=0;list[i]!=(char *)0;i+=2) {
+       if ((list[i+1]!=(char *)0)&&(strlen(list[i+1])>0)) {
+         MXTYP *mx;
+
+         mx=(MXTYP *)calloc(1,sizeof(MXTYP));
+         mx->preference=atoi(list[i]);
+         mx->mxname=strdup(list[i+1]);
+          mx->mxip=dns_get_ip_list(mx->mxname);
+          //(void) rou_alert(0,"%s JMPDBG MX[%d]=<%s>",OPEP,i,mx->mxname);
+         mxlist=(MXTYP **)rou_addlist((void **)mxlist,(void *)mx);
+          answer++;
+         }
+        }
+      if (answer>0)
+        (void) qsort((void *)mxlist,answer,sizeof(MXTYP *),cmpmx);
+      list=(char **)rou_freelist((void **)list,(genfree_t)rou_freestr);
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return mxlist;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to check if an IP (Origin IP) is      */
+/*     part of domain A record list                    */
+/*                                                     */
+/********************************************************/
+_Bool dns_matchiprec(char *hostname,AFNTYP *afnnum,int mask)
+
+{
+#define        OPEP    "unidns.c:dns_matchiprec,"
+
+_Bool match;
+struct addrinfo hints;
+struct addrinfo *res;
+int status;
+
+match=false;   
+(void) memset(&hints, 0, sizeof(hints));
+hints.ai_family=PF_UNSPEC;
+hints.ai_socktype=SOCK_STREAM;
+hints.ai_flags=HINTFLG;
+if ((status=getaddrinfo(hostname,"",&hints,&res))==0) {
+  match=true;
+  if (mask!=0) {/*do not compare IP with 0/0,::0*/
+    int proceed;
+    struct addrinfo *rp;
+
+    proceed=true;
+    match=false;
+    for (rp=res;rp!=(struct addrinfo *)0;rp=rp->ai_next) {
+      AFNTYP *addrnum;
+  
+      if ((addrnum=afn_getaddrinfo(rp))==(AFNTYP *)0) {
+       (void) rou_alert(0,"%s Unable to resolve addr struct for "
+                           "hostname <%s> (bug?)",
+                          OPEP,hostname,strerror(errno));
+       continue;       
+       }
+      switch (afn_cmpipnum(addrnum,afnnum,mask)) {
+       case false      :/*no equal continue    */
+         break;
+       case true       :/*found it equal!      */
+         match=true;
+         proceed=false;
+         break;
+       case -1         :/*trouble trouble      */
+         (void) rou_alert(0,"%s Unable to compare IP for hostname "
+                             "<%s> (error=<%s>)",
+                            OPEP,hostname,strerror(errno));
+         proceed=false;
+         break;
+       }
+      addrnum=afn_freeipnum(addrnum);
+      if (proceed==false)
+        break;
+      }
+    }
+  (void) freeaddrinfo(res);
+  }
+else {
+  (void) rou_alert(0,"%s Unable to get '%s' host IP (error='%s')",
+                    OPEP,hostname,gai_strerror(status));
+  }
+return match;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to check if a domain is handle by the */
+/*     local interface (serveur is primary MX).        */
+/*                                                     */
+/********************************************************/
+PUBLIC LOCTYP dns_is_domain_local(const char *domain,AFNTYP **afns)
+
+
+{
+#define OPEP    "unidns.c:dns_is_domain_local,"
+
+LOCTYP status;
+MXTYP **dns;
+int phase;
+_Bool proceed;
+
+status=dns_nomx;
+dns=(MXTYP **)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Do we have a domain
+      if ((domain==(char *)0)||(strlen(domain)==0))
+        phase=999;      //No domain (Bug?)
+      break;
+    case 1      :       //getting MX list
+      if ((dns=dns_getmx(domain))!=(MXTYP **)0)
+        phase++;        //Ok, we have an MX list
+      break;
+    case 2      :   {   //what is the domain IP
+      char **ipl;
+
+      if ((ipl=dns_get_ip_list(domain))!=(char **)0) {
+        MXTYP *mx0;
+
+        ipl=(char **)rou_freelist((void **)ipl,(genfree_t)rou_freestr);
+        mx0=(MXTYP *)calloc(1,sizeof(MXTYP));
+        mx0->preference=0;
+        mx0->mxname=strdup(domain);
+        dns=(MXTYP **)rou_addlist((void **)dns,(void *)mx0);
+        }        
+      else
+        phase=999;      //no MX, no IP!
+      }
+      break;
+    case 3      :       //establishing MX IP
+      status=dns_remote;
+      if (afns==(AFNTYP **)0)     //No local IP?
+        phase=999;
+      break;
+    case 4      :       //checking best MX with local interface
+      while (*afns!=(AFNTYP *)0) {
+        _Bool ismx;
+
+        ismx=dns_matchiprec((*dns)->mxname,*afns,32);
+        if (ismx==true) {
+          status=dns_local;
+          break;
+          }
+        afns++;
+        }
+      break;
+    default     :       //SAFE Guard
+      dns=dns_freemxlist(dns);  //cleaning MX
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to probe one black list site about the*/
+/*     remote IP and return a TXT if it is blacklisted.*/
+/*                                                     */
+/********************************************************/
+PUBLIC char *dns_is_blacklisted(BLKTYP *blk,char *reversip)
+
+{
+#define OPEP    "unidns.c:dns_is_blacklisted,"
+
+char *listed;
+char **txt;
+char dnsquest[300];
+int phase;
+_Bool proceed;
+
+listed=(char *)0;
+phase=0;
+proceed=(blk->targets!=(char **)0);
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Building the request
+      (void) snprintf(dnsquest,sizeof(dnsquest),"%s.%s",reversip,blk->sitename);
+      break;
+    case 1      :       //do we have a list of IP
+      if ((txt=gettxt(dnsquest,T_A,"TXT"))==(char **)0)
+        phase=999;      //No answer no need to go further
+      break;
+    case 2      :  {    //chek is the list of IP ia amongs targets
+      char *foundit;
+      char **ptr;
+    
+      foundit=(char *)0; 
+      ptr=txt;
+      while ((*ptr!=(char *)0)&&(foundit==(char *)0)) {
+        char **tgt;
+
+        tgt=blk->targets;
+        while (*tgt!=(char *)0) {
+          if (strcmp(*tgt,*ptr)==0) {
+            foundit=*tgt;
+            break;
+            }
+          tgt++;
+          }
+        ptr++;
+        } 
+      txt=(char **)rou_freelist((void **)txt,(genfree_t)rou_freestr);
+      if (foundit==(char *)0)
+        phase=999;
+      }
+      break;
+    case 3      :       //get the explainantion
+      if ((txt=gettxt(dnsquest,T_TXT,"TXT"))!=(char **)0) {
+        (void) rou_asprintf(&listed,"%s -> %s",blk->sitename,txt[0]);
+        txt=(char **)rou_freelist((void **)txt,(genfree_t)rou_freestr);
+        phase=999;      //every thing found
+        }
+      break;
+    case 4      :       //blakc list but no explaination?
+      (void) rou_alert(0,"%s <%s> blacklisted but no explainantion!",
+                          OPEP,dnsquest);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return listed;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to extract the IP reverse address     */
+/*      Return "No-reverse" if unknown.                 */
+/*                                                     */
+/********************************************************/
+PUBLIC char *dns_get_reverse_addr(char *reversip)
+
+{
+#define OPEP    "unidns.c:dns_get_reverse_addr,"
+
+char *reverse;
+char **txt;
+char dnsquest[300];
+
+reverse=(char *)0;
+(void) snprintf(dnsquest,sizeof(dnsquest),"%s.%s",reversip,"in-addr.arpa");
+//(void) rou_alert(0,"%s JMPDBG checking <%s>",OPEP,dnsquest);
+if ((txt=gettxt(dnsquest,T_PTR,"PTR"))!=(char **)0) {
+  (void) rou_asprintf(&reverse,"%s",txt[0]);
+  txt=(char **)rou_freelist((void **)txt,(genfree_t)rou_freestr);
+  }
+if (reverse==(char *)0)
+  reverse=strdup(NORVERS);
+return reverse;
+
+#undef  OPEP
+}
diff --git a/lib/unidns.d b/lib/unidns.d
new file mode 100644 (file)
index 0000000..df2c0d2
--- /dev/null
@@ -0,0 +1,82 @@
+unidns.o unidns.d : unidns.c /usr/include/stdc-predef.h /usr/include/arpa/inet.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/netinet/in.h /usr/include/bits/stdint-uintn.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/socket.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/endian.h /usr/include/bits/endianness.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/socket_type.h \
+ /usr/include/bits/sockaddr.h /usr/include/asm/socket.h \
+ /usr/include/asm-generic/socket.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/errno.h /usr/include/bits/errno.h \
+ /usr/include/linux/errno.h /usr/include/asm/errno.h \
+ /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
+ /usr/include/malloc.h /usr/include/stdio.h \
+ /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/resolv.h \
+ /usr/include/sys/param.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/signal.h /usr/include/bits/signum-generic.h \
+ /usr/include/bits/signum-arch.h /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/types/stack_t.h \
+ /usr/include/sys/ucontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigstksz.h /usr/include/bits/ss_flags.h \
+ /usr/include/bits/types/struct_sigstack.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/bits/param.h \
+ /usr/include/linux/param.h /usr/include/asm/param.h \
+ /usr/include/asm-generic/param.h /usr/include/arpa/nameser.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-least.h /usr/include/arpa/nameser_compat.h \
+ /usr/include/bits/types/res_state.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/stdlib.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h unidns.h subafn.h \
+ /usr/include/netdb.h /usr/include/rpc/netdb.h /usr/include/bits/netdb.h
diff --git a/lib/unidns.h b/lib/unidns.h
new file mode 100644 (file)
index 0000000..3a7860c
--- /dev/null
@@ -0,0 +1,68 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Define all procedure to check SPF entry within  */
+/*      DNS record.                                     */
+/*                                                     */
+/********************************************************/
+#ifndef        UNIDNS
+#define UNIDNS
+
+#include       "subafn.h"
+
+#define NORVERS "No.Reverse"
+
+//defining an MX structure
+typedef        struct  {
+       int preference; //MX preference
+       char *mxname;   //MX hostname
+        char **mxip;    //MX IPNUM list
+       }MXTYP;
+
+//defining a blacklist entrry
+typedef struct  {
+        int delta;      //black list delta value
+        char *sitename; //black lister domain entry
+        char **targets; //listing entry;
+        }BLKTYP;
+
+typedef enum    {       //domain MX status
+        dns_nomx,       //Domain without MX
+        dns_local,      //Domain is local
+        dns_remote,     //Domain is remote
+        dns_unknown     //Unknow domain status.
+        }LOCTYP;
+
+//procedure to free on blklist entry
+extern BLKTYP *dns_freeblk(BLKTYP *blk);
+
+extern BLKTYP **dns_addblklist(BLKTYP **blklist,char *entry);
+
+//Procedure to get a list of IP related to a hostname
+extern char **dns_get_ip_list(const char *hostname);
+
+//Procedure to get ONE SPF entry within domain dns
+extern char *dns_getspf(char *domain);
+
+//procedure to free a list of MX related to domain
+extern MXTYP **dns_freemxlist(MXTYP **mxlist);
+
+//procedure to get a list of MX IP releated to a specific
+//domain.
+extern MXTYP **dns_getmx(const char *domain);
+
+//Procedure to check if an IP (Origin IP) is part
+//of domain A record list
+extern _Bool dns_matchiprec(char *hostname,AFNTYP *afnnum,int mask);
+
+//procedure to detect if a domain is served by a local
+//smtp server
+extern LOCTYP dns_is_domain_local(const char *domain,AFNTYP **afns);
+
+//procedure returning a string if remote IP si blacklisted
+extern char *dns_is_blacklisted(BLKTYP *blk,char *reversip);
+
+//procedure returning the IP reverss address as a string 
+extern char *dns_get_reverse_addr(char *reversip);
+
+#endif
diff --git a/lib/unieml.c b/lib/unieml.c
new file mode 100644 (file)
index 0000000..5aedee3
--- /dev/null
@@ -0,0 +1,1079 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Implement all routine to manage SMTP low level  */
+/*     exchange.                                       */
+/*                                                     */
+/********************************************************/
+#include        <arpa/inet.h>
+#include        <sys/file.h>
+#include        <dirent.h>
+#include        <errno.h>
+#include        <fcntl.h>
+#include        <stdbool.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <unistd.h>
+#include        <uuid/uuid.h>
+
+#include       "subafn.h"
+#include       "subrou.h"
+#include       "unieml.h"
+
+//defining queued email repository
+#define QDIR    "/var/spool/"APPNAME"/queue"
+
+typedef struct  {
+        CODTYP code;            //keyword code
+        const char *key;        //keyword itself
+        }VOCTYP;
+
+//this list order by key length
+static VOCTYP vocsmtp[]={
+                {c_auth,"AUTH"},
+                {c_data,"DATA"},
+                {c_ehlo,"EHLO"},
+                {c_helo,"HELO"},
+                {c_help,"HELP"},
+                {c_mail,"MAIL FROM:"},
+                {c_noop,"NOOP"},
+#if   MODEDEBUG
+                {c_orgn,"ORGN:"},
+#endif
+                {c_quit,"QUIT"},
+                {c_rcpt,"RCPT TO:"},
+                {c_rset,"RSET"},
+                {c_starttls,"STARTTLS"},
+                {c_unknown,(const char *)0}
+                };
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to free memory used by a list of      */
+/*      recipient.                                      */
+/*                                                      */
+/********************************************************/
+PUBLIC RCPTYP *eml_freerecipient(RCPTYP *info)
+
+{
+if (info!=(RCPTYP *)0) {
+  info->userid=rou_freestr(info->userid);
+  info->domain=rou_freestr(info->domain);
+  (void) free(info);
+  info=(RCPTYP *)0;
+  }
+return info;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to free memory used by the TRATYP     */
+/*      response part.                                  */
+/*                                                      */
+/********************************************************/
+PUBLIC void eml_freetra_resp(TRATYP *tra)
+
+{
+if (tra!=(TRATYP *)0) {
+  tra->resp=(char **)rou_freelist((void **)tra->resp,(genfree_t)rou_freestr);
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to free memory used by a TRATYP record*/
+/*                                                      */
+/********************************************************/
+PUBLIC TRATYP *eml_freetra(TRATYP *tra)
+
+{
+if (tra!=(TRATYP *)0) {
+  (void) eml_freetra_resp(tra);
+  tra->hfrom=rou_freestr(tra->hfrom);
+  tra->hsubject=rou_freestr(tra->hsubject);
+  tra->reverse=rou_freestr(tra->reverse);
+  tra->remoteip=rou_freestr(tra->remoteip);
+  tra->rcptto=rou_freestr(tra->rcptto);
+  tra->sessid=rou_freestr(tra->sessid);
+  tra->sfrom=rou_freestr(tra->sfrom);
+  (void) free(tra);
+  tra=(TRATYP *)0;
+  }
+return tra;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to free memory used by a tra list     */
+/*                                                      */
+/********************************************************/
+PUBLIC TRATYP **eml_freeall_tra(TRATYP **tra)
+
+{
+return (TRATYP **)rou_freelist((void **)tra,(genfree_t)eml_freetra);
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to store an recipient information     */
+/*      within the contact list.                        */
+/*      retuurn RCPTYP ** if successful, NULL otherwise.*/
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_addrecipient(RCPTYP ***list,RCPTYP *rcpt)
+
+{
+_Bool todo;
+
+todo=true;
+if (*list!=(RCPTYP **)0) {
+  RCPTYP **ptr;
+
+  ptr=*list;
+  while (*ptr!=(RCPTYP *)0) {
+    if ((strcasecmp((*ptr)->userid,rcpt->userid)==0) &&
+        (strcasecmp((*ptr)->domain,rcpt->domain)==0)) {
+      todo=false;
+      break;    //already within list
+      }
+    ptr++;
+    }
+  }
+if (todo==true) {
+  *list=(RCPTYP **)rou_addlist((void **)(*list),(void *)rcpt);
+  }
+return todo;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to format and return a dynamicaly     */
+/*      allocated char array filled with uniq session ID*/
+/*                                                      */
+/********************************************************/
+PUBLIC char *eml_getmainsesid()
+
+{
+#define UFTIME  "%Y%m%d%H%M%S"
+#define UNIQUE  "%05d-%s-%04d"
+
+char *sesid;
+time_t curtime;
+char asctemps[100];
+
+sesid=(char *)0;
+curtime=time((time_t)0);
+(void) strftime(asctemps,sizeof(asctemps),UFTIME,localtime(&curtime));
+(void) rou_asprintf(&sesid,UNIQUE,getpid(),asctemps,rou_getmillisec());
+return sesid;
+#undef  UNIQUE
+#undef  UFTIME
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to format and return a dynamicaly     */
+/*      allocated char array with the current seesion ID*/
+/*                                                      */
+/********************************************************/
+PUBLIC char *eml_getcursesid(char *mainsesid,int numreset)
+
+{
+char *sesid;
+
+sesid=(char *)0;
+(void) rou_asprintf(&sesid,"%s-%04d",mainsesid,numreset);
+return sesid;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return a protocol keywork code     */
+/*                                                      */
+/********************************************************/
+PUBLIC CODTYP eml_getcode(char *keyword)
+
+{
+#define OPEP    "unieml.c:eml_getcode"
+
+CODTYP code;
+VOCTYP *ptr;
+
+code=c_unknown;
+for (ptr=vocsmtp;ptr->key!=(char *)0;ptr++) {
+  if (strncasecmp(ptr->key,keyword,strlen(ptr->key))==0) {
+    char *par;
+
+    code=ptr->code;
+    par=keyword+strlen(ptr->key);
+    *keyword='\000';     //lets say no parameters
+    if (*par=='\000')
+      break;            //no parameters
+    //removing unneeded space
+    while ((*par==' ')||(*par=='\t'))
+      par++;
+    (void) memmove(keyword,par,strlen(par)+1);
+    break;
+    }
+  }
+return code;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to remove crlf at the end of the      */
+/*      return true if CRLF was removed                 */
+/*                                                      */
+/********************************************************/
+PUBLIC int eml_removecrlf(char *string)
+
+{
+_Bool done;
+
+done=false;
+if (string!=(char *)0) {
+  register int taille;
+  register char *ptr;
+  
+  taille=strlen(string)-1;
+  ptr=string+taille;     
+  while ((*ptr=='\n') || (*ptr=='\r')) {
+    *ptr='\000';
+    taille--;
+    done=true;
+    if (taille<0)
+      break;
+    ptr--;
+    }
+  }
+return done;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to change qfile extension             */
+/*      directory.                                      */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_renameqfile(char *qfilename,const char *oldext,const char *newext)
+
+{
+#define OPEP    "unieml.c:eml_renameqfile,"
+
+_Bool status;
+char *filename;
+char fold[300];
+char fnew[300];
+
+status=true;
+filename=rou_apppath(QDIR);
+(void) snprintf(fold,sizeof(fold),"%s/%s.%s",filename,qfilename,oldext);
+(void) snprintf(fnew,sizeof(fnew),"%s/%s.%s",filename,qfilename,newext);
+if (rename(fold,fnew)<0) {
+  char *cwd;
+
+  cwd=get_current_dir_name();
+  (void) rou_alert(0,"%s Unable to rename file <%s>  to <%s> (error=<%s>)",
+                           OPEP,fold,fnew,strerror(errno));
+  (void) rou_alert(0,"%s current working dir=<%s>",OPEP,cwd);
+  cwd=rou_freestr(cwd);
+  status=false;
+  }
+filename=rou_freestr(filename);
+return status;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to remove a file from the queue       */
+/*      directory.                                      */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_deleteqfile(char *qfilename)
+
+{
+#define OPEP    "unieml.c:eml_deleteqfile,"
+
+_Bool done;
+char *filename;
+char fpath[300];
+
+done=true;
+filename=rou_apppath(QDIR);
+(void) snprintf(fpath,sizeof(fpath),"%s/%s",filename,qfilename);
+if (unlink(fpath)<0) {
+  (void) rou_alert(0,"%s Unable to delete file <%s> (error=<%s>) (Bug?)",
+                           OPEP,fpath,strerror(errno));
+  done=false;
+  }
+filename=rou_freestr(filename);
+return done;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to open a file within the queue       */
+/*      directory.                                      */
+/*                                                      */
+/********************************************************/
+PUBLIC FILE *eml_createqfile(char *qfilename,const char *ext)
+
+{
+#define OPEP    "unieml.c:eml_createqfile,"
+
+FILE *qfile;
+int handle;
+char *filename;
+int phase;
+int proceed;
+
+qfile=(FILE *)0;
+handle=-1;
+filename=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Do we have a session ID
+      if ((qfilename==(char *)0)||(strlen(qfilename)==0)) {
+        (void) rou_core_dump("%s qfilename is not set (bug?)",OPEP);
+        phase=999;      //never reached
+        }
+      break;
+    case 1      :       //"Computing" the file new queue filename
+      filename=rou_apppath(QDIR);
+      filename=(char *)realloc(filename,strlen(filename)+strlen(qfilename)+10);
+      (void) strcat(filename,"/");
+      (void) strcat(filename,qfilename);
+      if ((ext!=(char *)0)&&(strlen(ext)>0)) {
+        filename=(char *)realloc(filename,strlen(filename)+strlen(ext)+10);
+        (void) strcat(filename,".");
+        (void) strcat(filename,ext);
+        }
+      break;
+    case 2      :       //creating and opening the file
+      if ((handle=open(filename,O_EXCL|O_CREAT|O_WRONLY|O_TRUNC,0640))<0) {
+        (void) rou_alert(0,"%s Unable to open file <%s> (error=<%s>)",
+                           OPEP,filename,strerror(errno));
+        phase=999;      //Trouble trouble
+        }
+      break;
+    case 3      :       //convert handle to FILE *
+      if ((qfile=fdopen(handle,"w"))==(FILE *)0) {
+        (void) rou_alert(0,"%s Unable to fdopen file <%s> (error=<%s>)",
+                           OPEP,filename,strerror(errno));
+        }
+      break;
+    default     :       //SAFE guard
+      filename=rou_freestr(filename);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return qfile;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to close the file to the email stored */
+/*      stored within queue.                            */
+/*                                                      */
+/********************************************************/
+PUBLIC FILE *eml_closeqfile(FILE *qfile)
+
+{
+#define OPEP    "unieml.c:eml_closeqfile,"
+
+if (qfile!=(FILE *)0) {
+  if (fclose(qfile)<0) {
+      (void) rou_alert(0,"%s Unable to close qfile (error=<%s>)",
+                             OPEP,strerror(errno));
+    }
+  else
+    qfile=(FILE *)0;
+  }
+return qfile;
+#undef OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to load a list of IP from which email */
+/*      to a NONE local domain can be accepted and      */
+/*      relayed.                                        */
+/*                                                      */
+/********************************************************/
+PUBLIC AFNTYP **eml_load_relayed(char *relayedfile)
+
+{
+#define OPEP    "unieml.c:eml_load_relayed,"
+
+AFNTYP **list;
+char *filename;
+FILE *relays;
+int phase;
+_Bool proceed;
+
+list=(AFNTYP **)0;
+filename=(char *)0;
+relays=(FILE *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Check if we have a ffilename
+      if ((relayedfile==(char *)0)||(strlen(relayedfile)==0)) {
+        (void) rou_alert(0,"%s No file name config?)",OPEP);
+        phase=999;      //*no need to go further
+        }
+      break;
+    case 1      :       //let open the relayable file
+      filename=rou_apppath(relayedfile);
+      if ((relays=fopen(filename,"r"))==(FILE *)0) {
+        (void) rou_alert(0,"%s Unable to open file <%s> (error=<%s>, config?)",
+                            OPEP,filename,strerror(errno));
+        phase=999;      //No need to go further
+        }
+      break;
+    case 2      :  {    //scanning relayable contents
+      int num;
+      char format[40];
+      char ip[80];
+      char line[300];
+    
+      num=0; 
+      snprintf(format,sizeof(format),"%%%ld[^/]/%%d",sizeof(ip));
+      while (fgets(line,sizeof(line)-1,relays)!=(char *)0) {
+        AFNTYP *afn;
+        int mask;
+
+        num++;
+        (void) rou_clean_conf_line(line);
+        if (strlen(line)==0)
+          continue;
+        (void) memset(ip,'\000',sizeof(ip));
+        if (sscanf(line,format,ip,&mask)<1)
+          continue;
+        if ((afn=afn_getoneipnum(ip))==(AFNTYP *)0) {
+          (void) rou_alert(0,"%s line='%03d' <%s> not a valid IP "
+                             "(error=<%s>, config?)",
+                             OPEP,num,line,strerror(errno));
+          continue;
+          }
+        (void) afn_maskip(afn,mask);
+        list=(AFNTYP **)rou_addlist((void **)list,(void *)afn);
+        }
+      }
+      break;
+    case 3      :       //closing file
+      (void) fclose(relays);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+filename=rou_freestr(filename);
+return list;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to list all file with a specific      */
+/*      extension with the queue directory.             */
+/*      Return a list or NULL if no file.               */
+/*                                                      */
+/********************************************************/
+PUBLIC char **eml_getqfilelist(char **dnames,const char *ext)
+
+{
+#define OPEP    "unieml.c:eml_getqfilelist"
+
+DIR *dir;
+int phase;
+_Bool proceed;
+
+dir=(DIR *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :        //lets open the queue directory
+      char *dirname;
+
+      dirname=rou_apppath(QDIR);
+      if ((dir=opendir(dirname))==(DIR *)0) {
+        (void) rou_alert(0,"%s Unable to open director <%s> (error=<%s>, config?)",
+                            OPEP,dirname,strerror(errno));
+        phase=999;      //No need to go further
+        }
+      dirname=rou_freestr(dirname);
+      break;
+    case 1      :       //scanning directory contents
+      struct dirent *data;
+      register int taille;
+
+      taille=0;
+      if (ext!=(char *)0)
+        taille=strlen(ext);
+      while ((data=readdir(dir))!=(struct dirent *)0) {
+        register char *ptr;
+  
+        ptr=data->d_name;
+        if (*ptr=='.')
+          continue;             //hidden file
+        if (taille>0)
+          ptr=strstr(data->d_name,ext);
+        if (ptr==(char *)0)
+          continue;             //extension not found
+        if ((strlen(ptr)!=taille)&&(*(ptr-1)!='.')) 
+          continue;             //Not the right extension
+        (void) rou_alert(6,"%s got <%s>",OPEP,data->d_name);
+        dnames=(char **)rou_addlist((void **)dnames,(void *)strdup(data->d_name));
+        }
+      break;
+    case 2      :       //closing directory
+      if (closedir(dir)<0) {
+        (void) rou_alert(0,"%s Unable to close queue directory (error=<%s>, bug?)",
+                            OPEP,strerror(errno));
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return dnames;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to open a fine within the MTA queue   */
+/*      directory.                                      */
+/*                                                      */
+/********************************************************/
+PUBLIC FILE *eml_openqfile(const char *qfilename,const char *ext)
+
+{
+#define OPEP    "unieml.c:eml_openqfile,"
+
+FILE *qfile;
+char *dirname;
+char fullname[300];
+
+qfile=(FILE *)0;
+dirname=rou_apppath(QDIR);
+(void) snprintf(fullname,sizeof(fullname),"%s/%s",dirname,qfilename);
+if ((ext!=(const char *)0)&&((strlen(fullname)+strlen(ext)+3)<sizeof(fullname))) {
+  (void) strcat(fullname,".");
+  (void) strcat(fullname,ext);
+  }
+if ((qfile=fopen(fullname,"r+"))==(FILE *)0) {
+  (void) rou_alert(0,"%s Unable to open file <%s> (error-<%s> bug?)",
+                     OPEP,fullname,strerror(errno));
+  }
+dirname=rou_freestr(dirname);
+return qfile;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to store an email to a list of email  */
+/*      return true if successful, false otherwise.     */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_addemail(char ***emails,char *email)
+
+{
+_Bool status;
+char **ptr;
+
+status=true;
+ptr=*emails;
+if (ptr!=(char **)0) {
+  while (*ptr!=(char *)0) {
+    if (strcasecmp(*ptr,email)==0) {
+      status=false;
+      break;    //already within list
+      }
+    ptr++;
+    }
+  }
+if (status==true)
+  *emails=(char **)rou_addlist((void **)*emails,strdup(email));
+return status;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to duplication a sessid file (email)  */
+/*      to user directory.                              */
+/*      Return true if successful, false otherwise      */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool eml_dupqfile(char *qfilename,const char *dest)
+
+{
+#define OPEP    "unieml.c:eml_dupqfile,"
+_Bool status;
+char *filename;
+FILE *fin;
+FILE *fout;
+int phase;
+_Bool proceed;
+
+status=false;
+filename=rou_apppath(QDIR);
+fin=(FILE *)0;
+fout=(FILE *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :        //lets open the queue directory
+      if ((qfilename==(char *)0)||(dest==(char *)0)) {
+        (void) rou_alert(0,"%s filename=<%s> or dest=<%s> missing (Bug?)",
+                            OPEP,qfilename,dest);
+        phase=999;      //Big trouble
+        }
+      break;
+    case 1      :       //opening origin file
+      if (qfilename!=(char *)0) {       //always
+        char name[300];
+
+        (void) snprintf(name,sizeof(name),"%s/%s",filename,qfilename);
+        if ((fin=fopen(name,"r"))==(FILE *)0) {
+          (void) rou_alert(0,"%s unable to open source <%s> (error=<%s>)",
+                             OPEP,name,strerror(errno));
+          phase=999;    //Big trouble
+          }
+        }
+      break;
+    case 2      :       //opening destination file
+      if (fin!=(FILE *)0) {     //always
+        char name[300];
+
+        (void) snprintf(name,sizeof(name),"%s/%s",dest,qfilename);
+        if ((fout=fopen(name,"w"))==(FILE *)0) {
+          (void) rou_alert(0,"%s unable to open destination <%s> (error=<%s>)",
+                             OPEP,name,strerror(errno));
+          (void) fclose(fin);
+          phase=999;    //Big trouble
+          }
+        }
+      break;
+    case 3      :       //lock the fout file
+      if (flock(fileno(fout),LOCK_EX)<0) {
+        (void) rou_alert(0,"%s unable to lock destination file <%s> "
+                           "(error=<%s> (Bug?))",
+                            OPEP,qfilename,strerror(errno));
+        (void) fclose(fout);
+        (void) fclose(fin);
+        phase=999;    //Big trouble
+        }
+      break;
+    case 4      :       //duplicating file
+      if (fout!=(FILE *)0) {
+        int count;
+        char buffer[2048];
+
+        while ((count=fread(buffer,sizeof(char),sizeof(buffer),fin))>0) {
+          if (fwrite(buffer,sizeof(char),count,fout)!=count) {
+            (void) rou_alert(0,"%s unable to fully write destination file "
+                               "<%s> (error=<%s>)",
+                               OPEP,qfilename,strerror(errno));
+            phase=999;
+            }
+          }
+        (void) flock(fileno(fout),LOCK_UN);
+        (void) fclose(fout);
+        (void) fclose(fin);
+        }
+      break;
+    case 5      :       //duplication is a success
+      status=true;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+filename=rou_freestr(filename);
+return status;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to increase or decrase the "count"    */
+/*      session id file.                                */
+/*      return 0 or a positive count number.            */
+/*      return -1 if trouble.                           */
+/*                                                      */
+/********************************************************/
+PUBLIC int eml_countqfile(char *qfilename,int count)
+
+{
+#define OPEP    "unieml.c:eml_countqfile,"
+
+int result;
+FILE *fcount;
+char *filename;
+int phase;
+_Bool proceed;
+
+result=-1;
+fcount=(FILE *)0;
+filename=rou_apppath(QDIR);
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+   case 0      :        //lets open the queue directory
+      if (qfilename==(char *)0) {
+        (void) rou_alert(0,"%s filename=<%s> missing (Bug?)",OPEP,qfilename);
+        phase=999;      //Big trouble
+        }
+      break;
+    case 1      :       //opening count file
+      char name[300];
+
+      (void) snprintf(name,sizeof(name),"%s/%s.%s",filename,qfilename,EXTCNT);
+      if ((fcount=fopen(name,"r+"))==(FILE *)0) {
+        (void) rou_alert(0,"%s unable to open count file <%s> (error=<%s>)",
+                             OPEP,name,strerror(errno));
+        phase=999;    //Big trouble
+        }
+      break;
+    case 2      :       //locking access
+      int try;
+
+      try=100;          //wait 1 sec total
+      while (try>0) {
+        if (flock(fileno(fcount),LOCK_EX|LOCK_NB)==0) 
+          break;        //We have the lock
+        else {
+          switch (errno) {
+            case (EWOULDBLOCK)  :
+              try--;
+              if (try>0)
+                (void) usleep(10000);   //Wait 10 millisec
+              else
+                (void) rou_alert(0,"%s unable to lock count file <%s> in time",
+                                  OPEP,name);
+              break;
+            default             :
+              (void) rou_alert(0,"%s unable to lock count file <%s> (error=<%s>)",
+                                  OPEP,name,strerror(errno));
+              (void) fclose(fcount);
+              phase=999;// trouble trouble
+              try=0;
+              break;
+            }
+          }
+        }
+      break;
+    case 3      :       //reading count
+      if (fscanf(fcount,"%d",&result)!=1) {
+        (void) rou_alert(0,"%s unable to read file <%s> contents (error=<%s>)",
+                             OPEP,name,strerror(errno));
+        (void) fclose(fcount);
+        phase=999;      //trouble trouble
+        } 
+      break;
+    case 4      :       //doing count
+      result+=count;
+      if (result<0)
+        result=-1;
+      (void) rewind(fcount);
+      if (fprintf(fcount,"%d\n",result)<0) {
+        (void) rou_alert(0,"%s unable to write file <%s> count value (error=<%s>)",
+                             OPEP,name,strerror(errno));
+        }
+      (void) fclose(fcount);
+      break;
+    case 5      :       //removing file count if result is zero
+      if (result==0)
+        (void) eml_deleteqfile(qfilename);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+filename=rou_freestr(filename);
+return result;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check if email address is          */
+/*      acceptable.                                     */
+/*                                                      */
+/********************************************************/
+PUBLIC RCPTYP *eml_isemailok(char *email,char **report)
+
+{
+#define OPEP    "unieml.c:eml_isemailok"
+
+RCPTYP *info;
+char *localpart;
+char *domain;
+int phase;
+_Bool proceed;
+
+info=(RCPTYP *)0;
+*report=(char *)0;
+localpart=(char *)0;
+domain=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"JMPDBG %s phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //do we have an email
+      if ((email==(char *)0)||strlen(email)==0) {
+        (void) rou_alert(0,"%s Email missing! (bug?)",OPEP);
+        proceed=false;
+        }
+      break;
+    case 1      :       //splitting local domain part
+      if ((domain=strchr(email,'@'))==(char *)0) {
+        *report=strdup("Missing domain part");
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 2      :       //double checking domain part
+      domain++;
+      if (strchr(domain,'@')!=(char *)0) {
+        *report=strdup("malformed domain part");
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 3      :       //duplicating email to localpart
+      localpart=strdup(email);
+      *(strchr(localpart,'@'))='\000';  //removing domain
+      if (strlen(localpart)==0) {
+        *report=strdup("email address is empty");
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 4      :       //checking localpart email
+      if (strlen(localpart)>0) {        //always
+        char *ptr;
+        char cmt[200];
+
+        ptr=localpart;
+        while (*ptr!='\000') {
+          switch (*ptr) {
+            case ' '  :
+            case '<'  :
+            case '>'  :
+            case '('  :
+            case '['  :
+            case ']'  :
+            case ';'  :
+            case ':'  :
+              (void) snprintf(cmt,sizeof(cmt),"'%c' not allowed in email",*ptr);
+              *report=strdup(cmt);
+              *(ptr+1)='\000';  //exiting;
+              phase=999;        //No need to go further
+              break;
+            }
+          ptr++;
+          } 
+        }
+      break;
+    case 5      :       //everythin fine
+      info=(RCPTYP *)calloc(1,sizeof(RCPTYP));
+      info->code='N';           //Not assigned code
+      info->domain=strdup(domain);
+      info->userid=strdup(localpart);
+      break;
+    default     :       //SAFE guard
+      localpart=rou_freestr(localpart);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return info;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to attach a part of text to a file    */
+/*      acceptable.                                     */
+/*                                                      */
+/********************************************************/
+PUBLIC int eml_attache(FILE *qout,char *toaddfile,char **explain,int nbrlines)
+
+{
+#define OPEP    "unieml.c:eml_attache,"
+#define CNT     "Content-"
+
+
+int success;
+FILE *qfile;
+uuid_t id;
+char marker[40];
+int phase;
+_Bool proceed;
+
+success=false;
+qfile=(FILE *)0;
+(void) uuid_generate(id);
+(void) uuid_unparse(id,marker);
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //Open the file
+      if ((qfile=eml_openqfile(toaddfile,(char *)0))==(FILE *)0) {
+        (void) rou_alert(0,"%s unable to open file <%s> to be attached",
+                           OPEP,toaddfile);
+        phase=999;      //No need to go further
+        }
+      break;
+    case 1      :       //mime starting part
+      (void) fprintf(qout,"MIME-Version: 1.0\n");
+      (void) fprintf(qout,"%sType: multipart/mixed; boundary=\"%s\"\n",CNT,marker);
+      (void) fprintf(qout,"\nThis is a multi-part message in MIME format.\n\n");
+      (void) fprintf(qout,"--%s\n",marker);
+      (void) fprintf(qout,"%sType: text/plain; charset=\"iso-8859-1\"\n",CNT);
+      (void) fprintf(qout,"%sTransfer-Encoding: 8bit\n\n",CNT);
+      break;
+    case 2      :       //mime starting part
+      (void) fprintf(qout,"Sending Email was not successful\n");
+      (void) fprintf(qout,"here is the status returned by remote serveur:\n");
+      (void) fprintf(qout,"--->\n");
+      if (explain!=(char **)0) {
+        while (*explain!=(char *)0) {
+          (void) fprintf(qout,"\t%s\n",*explain);
+          explain++;
+          }
+        }
+      (void) fprintf(qout,"---<\n");
+      break;
+    case 3      :       //mime starting part
+      (void) fprintf(qout,"--%s%s",marker,CRLF);
+      (void) fprintf(qout,"%sType: message/rfc822;\n\tname=\"%s\"\n",
+                          CNT,toaddfile);
+      (void) fprintf(qout,"%sTransfer-Encoding: 8bit\n",CNT);
+      (void) fprintf(qout,"%sDisposition: attachment;\n\tfilename=\"%s\"\n\n",
+                          CNT,toaddfile);
+      break;
+    case 4      :       //appending addfile
+      _Bool count;      //reading at most nbrlines
+      char line[300]; 
+
+      count=false;
+      while (fgets(line,sizeof(line),qfile)!=(char *)0) {
+        if (count==true) 
+          nbrlines--;
+        (void) fprintf(qout,"%s",line);
+        if (strlen(line)<1)
+          count=true;
+        if (nbrlines==0)
+          break;
+        }
+      break;
+    case 5      :       //end attachement marker
+      (void) fprintf(qout,"\n--%s--\n\n",marker);
+      qfile=eml_closeqfile(qfile);
+      success=true;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return success;
+
+#undef  CNT
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to format a string with remote server */
+/*      credit report.                                  */
+/*                                                      */
+/********************************************************/
+PUBLIC char *eml_showcredit(char *rmtip,char *reverse,int delta,int credit)
+
+{
+static const char *cmt="%s\tRVS='%s'\tdelta='%3d' -> new credit='%3d'";
+
+char *fmt;
+
+fmt=(char *)0;
+(void) rou_asprintf(&fmt,cmt,rmtip,reverse,delta,credit);
+return fmt;
+}
diff --git a/lib/unieml.d b/lib/unieml.d
new file mode 100644 (file)
index 0000000..8df0219
--- /dev/null
@@ -0,0 +1,65 @@
+unieml.o unieml.d : unieml.c /usr/include/stdc-predef.h /usr/include/arpa/inet.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/netinet/in.h /usr/include/bits/stdint-uintn.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/sys/socket.h \
+ /usr/include/bits/types/struct_iovec.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/socket.h /usr/include/sys/types.h \
+ /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/endian.h /usr/include/bits/endianness.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/socket_type.h \
+ /usr/include/bits/sockaddr.h /usr/include/asm/socket.h \
+ /usr/include/asm-generic/socket.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/bits/in.h \
+ /usr/include/sys/file.h /usr/include/fcntl.h /usr/include/bits/fcntl.h \
+ /usr/include/bits/fcntl-linux.h /usr/include/bits/stat.h \
+ /usr/include/bits/struct_stat.h /usr/include/dirent.h \
+ /usr/include/bits/dirent.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/dirent_ext.h \
+ /usr/include/errno.h /usr/include/bits/errno.h \
+ /usr/include/linux/errno.h /usr/include/asm/errno.h \
+ /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/stdlib.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/alloca.h /usr/include/bits/stdlib-float.h \
+ /usr/include/string.h /usr/include/bits/types/locale_t.h \
+ /usr/include/bits/types/__locale_t.h /usr/include/strings.h \
+ /usr/include/unistd.h /usr/include/bits/posix_opt.h \
+ /usr/include/bits/environments.h /usr/include/bits/confname.h \
+ /usr/include/bits/getopt_posix.h /usr/include/bits/getopt_core.h \
+ /usr/include/bits/unistd_ext.h /usr/include/uuid/uuid.h \
+ /usr/include/sys/time.h /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h subafn.h \
+ /usr/include/netdb.h /usr/include/rpc/netdb.h /usr/include/bits/netdb.h \
+ subrou.h /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ unieml.h
diff --git a/lib/unieml.h b/lib/unieml.h
new file mode 100644 (file)
index 0000000..45623e9
--- /dev/null
@@ -0,0 +1,178 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Define all routine to manage SMTP low level     */
+/*     exchange.                                       */
+/*                                                     */
+/********************************************************/
+#ifndef        UNIEML
+#define UNIEML
+
+#include        <stdio.h>
+
+#include        "subafn.h"     
+
+#define HFROM   "From: "        //Then email header header from
+#define HTITLE  "Subject: "     //Then email subject entry
+
+#define MXMSIZE "52428800"      //52 Megabytes
+#define CRLF    "\r\n"          //EOL within SMTP protocol
+#define        GOTHELP 214             //Help message (A response to the HELP command)
+#define        SIGNON  220             //signon information
+#define        QUITOK  221             //status on quit
+#define IDOK    235             //Authentication ID OK
+#define        CMDOK   250             //Everything OK
+#define SENDB64 334             //send a B64 sequence
+#define        DATAOK  354             //Ready to accept EMAIL data
+#define        ERRPROC 451             //local processing error
+#define ALREADY 456             //remote server already connected
+#define BADSITE 457             //Site credential is too low
+#define NOTNOW  459             //Site already connected
+#define NOANSWR 460             //no answer from remote
+#define BADPAR  501             //error in parameters
+#define CMDBAD  502             //command not implemented
+#define BADSEQ  503             //out of sequence command
+#define BADAUTH 504             //Posting user Authentication not successful
+#define DATRJC  521             //Data Rejected
+#define EXPIRED 536             //Contract date expired
+#define REJECT  550             //E-mail/domain rejected
+#define UKNUSER 551             //Unknown user
+#define MALABRT 552             //email data rejected
+#define NOTEML  553             //Not an email address
+#define FAILED  554             //transaction failed
+#define NORELAY 555             //we do not relay email
+#define BLCKLST 556             //remote site Black listed 
+#define MISSMX  563             //NO MX found for recipient
+
+//defining extensions
+#define EXTODO  "todo"          //todo qfile extension
+#define EXDONE  "done"          //todo qfile processing completed
+#define EXDOING "doing"         //todo qfile in sending mode
+#define EXTRANS "trans"         //trans qfile extension
+#define EXTCNT  "count"         //count sessid by email extension
+#define EXTOBE  "tobedone"      //in progess qfile extension
+
+
+#define SBINDIR "/usr/sbin/"    //appliaction installation directory
+#define SENDER  "sender"        //application email sender
+#define SORTER  "sorter"        //application email sorter
+
+#define EMLPORT "25"            //Default SMTP email port
+#define WAITRMT 60              //how long to wait answer from remote SMTP
+
+typedef enum    {               //list of email relayable status
+        rel_authentic,          //connection/user is authenticated
+        rel_isrelay,            //remote IP is relayable
+        rel_plain,              //remote IP is a "plain" stranger
+        rel_unknown             //Wrong status
+        }RELTYP;
+
+//list of keyword
+typedef enum    {               //list of SMTP protocol keyword
+        c_auth,                 //Requesting authentication
+        c_data,                 //DATA email contents transfer request
+        c_ehlo,                 //EHLO command
+        c_helo,                 //Basic Helo command
+        c_help,                 //HELP command
+        c_mail,                 //'mail from:' sequence detected
+        c_noop,                 //No Operation request
+#ifdef  MODEDEBUG
+//command available ONLY if compiled in debug mode
+        c_orgn,                 //to force remote IP to a specific value
+#endif
+        c_quit,                 //quit exchange
+        c_rcpt,                 //'rcpt to:' sequence detected
+        c_rset,                 //resetting session
+        c_starttls,             //Starting a TLS crypted link
+        c_unknown               //key word unknown
+        }CODTYP;
+
+typedef struct  {               //*definition of recipient
+        char code;              //'L' rcipient local, 'R' recipient remote
+        char *domain;           //Recipient domain
+        char *userid;           //recipient email userid
+        }RCPTYP;
+
+//structure to define an email transport directive
+typedef struct  {
+        char code;      //Transaction code
+        time_t date;    //Transaction date
+        u_int delay;    //Transaction execution delay
+        char *hfrom;    //Email HEADER originator
+        char *hsubject; //Email HEADER subject
+        char *remoteip; //remote server pure IP
+        char *reverse;  //remote server reverse-address
+        char *rcptto;   //Email Recipient
+        char *sessid;   //session id
+        char *sfrom;    //Email SMTP Originator
+        int sendcode;   //Email sending status;
+        char **resp;    //Transfer response status
+        }TRATYP;
+
+//procedure to Free one recipient info
+extern RCPTYP *eml_freerecipient(RCPTYP *info);
+
+//procedure to fee memory used by the resp record within TRATYP structure
+extern void eml_freetra_resp(TRATYP *tra);
+
+//procedure to Free one transaction info
+extern TRATYP *eml_freetra(TRATYP *tra);
+
+//procedure to Free all transaction inf
+extern TRATYP **eml_freeall_tra(TRATYP **tra);
+
+//procedure to add recipient to a recipient list
+extern _Bool eml_addrecipient(RCPTYP ***list,RCPTYP *rcpt);
+
+//get a session unique id
+extern char *eml_getmainsesid();
+
+//get the current session ID
+extern char *eml_getcursesid(char *mainid,int numreset);
+
+//convert SMTP keyword to CODTYP
+extern CODTYP eml_getcode(char *keyword);
+
+//remove CRLF from string
+extern int eml_removecrlf(char *string);
+
+//procedure to delete a file within the queue
+extern _Bool eml_deleteqfile(char *qfilename);
+
+//procedure to change qfile extension
+extern _Bool eml_renameqfile(char *qfilename,const char *oldext,const char *newext);
+
+//procedure to open a file within queue directory
+extern FILE *eml_createqfile(char *qfilename,const char *ext);
+
+//procedure to close a file within the queue directory
+extern FILE *eml_closeqfile(FILE *qfile);
+
+//Procedure to load the list of IP/Network from
+//which incoming email can be relayed to another domain MX
+extern AFNTYP **eml_load_relayed(char *relayedfile);
+
+//Procedure to list all file within queue directory
+//and with a specific extenstion
+extern char **eml_getqfilelist(char **dnames,const char *ext);
+
+//procedure to open a specific qfile
+extern FILE *eml_openqfile(const char *qfilename,const char *ext);
+
+//procedure to duplicate a specific qfile to another file
+extern _Bool eml_dupqfile(char *qfilename,const char *dest);
+
+//procedure to increase or decrease the "count" file contents
+extern int eml_countqfile(char *qfilename,int count);
+
+//procedure to check email address format
+//of an email address
+extern RCPTYP *eml_isemailok(char *email,char **report);
+
+//procedure to create an attachement for email data part.
+extern int eml_attache(FILE *qout,char *toaddfile,char **explain,int nbrlines);
+
+//Procedure to format a remote server credit report
+extern char *eml_showcredit(char *rmtip,char *reverse,int delta,int credit);
+
+#endif
diff --git a/lib/unimar.c b/lib/unimar.c
new file mode 100644 (file)
index 0000000..e817329
--- /dev/null
@@ -0,0 +1,558 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Low level subroutine implementation             */
+/*     to handle POSTGRES SQL request                  */
+/*                                                     */
+/********************************************************/
+#include        <pwd.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <unistd.h>
+#include        <mysql/mysql.h>
+#include        <sys/types.h>
+
+#include        "subrou.h"
+#include        "unimar.h"
+
+//checking if MYSQL database need to compiled
+#if DATABASE==USE_MYSQL
+  #define DB_MYSQL
+#endif
+
+#ifdef  DB_MYSQL
+char *marenv[SQLENV]= {
+                      "MYSQL",
+                      "localhost",
+                      "3306",
+                      "mailleur",
+                      (char *)0
+                      };
+#else
+char *marenv[SQLENV]= {
+                      (char *)0,
+                      (char *)0,
+                      (char *)0,
+                      (char *)0,
+                      (char *)0
+                };
+#endif
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to detect and 'clean' any single quote*/
+/*     within a string, this is mandatory for string   */
+/*      variable in the database.                       */
+/*     Single quote and double quote are translated to */
+/*      '\'' and '\"'                                   */
+/*                                                     */
+/********************************************************/
+char *mar_cleanquote(char *dstr)
+
+{
+char *cleanstr;
+int taille;
+char *ptr;
+
+taille=3;
+cleanstr=calloc(3,sizeof(char));
+cleanstr[0]='\'';
+if (dstr!=(char *)0) {
+  char *nptr;
+
+  ptr=dstr;
+  while (((nptr=strchr(ptr,'\''))!=(char *)0)||((nptr=strchr(ptr,'\"'))!=(char *)0)) {
+    char lit[3];
+    char *newclean;
+
+    (void) strcpy(lit,"\\'");
+    if (*nptr=='"')
+      lit[1]='"';
+    *nptr='\000';      /*to segment string     */
+    taille+=strlen(ptr)+2;
+    if ((newclean=(char *)realloc(cleanstr,taille))!=(char *)0) {
+      cleanstr=newclean;
+      (void) strcat(cleanstr,ptr);
+      (void) strcat(cleanstr,lit);
+      *nptr=lit[1];    /*to glue back string   */
+      ptr=nptr+1;
+      }
+    }
+  if (*ptr!='\000') {
+    char *newclean;
+
+    taille+=strlen(ptr);
+    if ((newclean=(char *)realloc(cleanstr,taille))!=(char *)0) {
+      cleanstr=newclean;
+      (void) strcat(cleanstr,ptr);
+      }
+    }
+  }
+(void) strcat(cleanstr,"'");
+return cleanstr;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to return a dynamicaly allocated      */
+/*      char * with an MYSQL date computation sequenc   */
+/*                                                      */
+/********************************************************/
+PUBLIC char *mar_caldate(char *expression,int second)
+
+{
+char *seq;
+
+seq=(char *)0;
+(void) rou_asprintf(&seq,"(addtime(%s,%d))",expression,second);
+return seq;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to establish a link with the          */
+/*      postgresq SQL server.                           */
+/*                                                      */
+/********************************************************/
+MARPTR *mar_opensql(const char *host,const char *sqlport,const char *dbname)
+
+{
+#define OPEP    "unimar.c:mar_opensql,"
+
+MARPTR *marptr;
+
+marptr=(MARPTR *)0;
+#ifdef  DB_MYSQL
+  {
+  struct passwd *pw;
+  MYSQL *mysql;
+  int port;
+
+  if ((pw=getpwuid(geteuid()))==(struct passwd *)0) {
+    (void) rou_core_dump("%s is unable to find name for uid '%s'",
+                          OPEP,geteuid());
+    //never ever REACHED
+    }
+  mysql=mysql_init((MYSQL *)0);
+  port=0;
+  if (sqlport!=(char *)0)
+    port=atoi(sqlport);
+  if (mysql_real_connect(mysql,host,pw->pw_name,(char *)0,dbname,port,
+                         (char *)0,CLIENT_FOUND_ROWS)==(MYSQL *)0) {
+    (void) rou_alert(0,"%s Connection to database '%s' failed, cause '%s'",
+                       OPEP,dbname,mysql_error(mysql));
+    (void) free(mysql);
+    mysql=(MYSQL *)0; 
+    }
+  marptr=(MARPTR *)mysql;
+  }
+#else
+  (void) fprintf(stderr,"%s not compiled to be used with mysql\n",OPEP);
+#endif
+return marptr;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to close the link with the designated */
+/*      postgresql SQL server.                          */
+/*                                                      */
+/********************************************************/
+MARPTR *mar_closesql(MARPTR *marptr)
+
+{
+#define OPEP    "sqlmar.c:mar_closesql,"
+
+#ifdef  DB_MYSQL
+  (void) mysql_close((MYSQL *)marptr);
+#else
+  (void) fprintf(stderr,"%s not compiled to be used with mysql\n",OPEP);
+#endif
+marptr=(MARPTR *)0;
+return marptr;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return a unix time in acsii        */
+/*      format (data-base compatible).                  */
+/*                                                     */
+/********************************************************/
+PUBLIC const char *mar_fromunixtime(time_t timestamp)
+
+{
+#define TSTAMP   "%Y-%m-%d %H:%M:%S"
+
+static char unixdate[50];
+
+(void) memset(unixdate,'\000',sizeof(unixdate));
+
+#ifdef  DB_MYSQL
+/*Note: 
+ *syntax MYSQL ("FROM_UNIXTIME(%ld)",timestamp)
+ */
+  {
+  struct tm *tminfo;
+
+  tminfo=localtime(&timestamp);
+ (void) strftime(unixdate,sizeof(unixdate),TSTAMP,tminfo);
+ }
+#endif
+
+return unixdate;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return a unix time_t  from an ASCII*/
+/*      string (data-base compatible).                  */
+/*                                                     */
+/********************************************************/
+PUBLIC time_t mar_tounixtime(const char *date)
+
+{
+#define OPEP    "unimar.c:mar_tounixtime,"
+
+time_t datetime;
+
+datetime=(time_t)0;
+#ifdef  DB_MYSQL
+  {
+  static const char *stampeon="0000-00-00 00:00:00";
+
+  if ((date!=(char *)0)&&(strcmp(date,stampeon)!=0))  {
+    static const char *dbtounix="%Y-%m-%d %H:%M:%S";
+
+    char *ptr;
+    struct tm tm;
+
+    if ((ptr=strchr(date,'.'))!=(char *)0) {
+      *ptr='\000';
+      }
+    (void) memset(&tm,'\000',sizeof(struct tm));
+    tm.tm_isdst=-1;
+    if (strptime(date,dbtounix,&tm)!=(char *)0) {
+      datetime=mktime(&tm);
+      }
+    else {
+      (void) rou_alert(0," Unable to convert <%s> to time_t (Bug?)",OPEP,date);
+      }
+    }
+  }
+#endif
+
+return datetime;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to lock access to a specific table    */
+/*      within database.                                */
+/*                                                     */
+/********************************************************/
+PUBLIC _Bool mar_lock(MARPTR *marptr,char *tablename)
+
+{
+#define OPEP    "unimar.c:mar_lock,"
+
+_Bool locked;
+
+locked=false;
+#ifdef  DB_MYSQL
+  {
+  static const char *cmd="LOCK TABLE %s WRITE";
+
+  char fullcmd[300];
+  int phase;
+  _Bool proceed;
+
+  (void) snprintf(fullcmd,sizeof(fullcmd),cmd,tablename);
+  phase=0;
+  proceed=true;
+  while (proceed==true) {
+    //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+    switch (phase) {
+      case 0    :       //Starting lock
+        if (mar_request(marptr,"BEGIN")<0) {
+          (void) rou_alert(0,"%s Unable to BEGIN to lock table <%s>",
+                              OPEP,tablename);
+          phase=999;    //no need to go further
+          }
+        break;
+      case 1    :       //lock table itself
+        if (mar_request(marptr,fullcmd)<0) {
+          (void) rou_alert(0,"%s Unable to lock table <%s>",
+                              OPEP,tablename);
+          phase=999;    //no need to go further
+          }
+        break;
+      case 2    :       //lock done
+        locked=true;
+        break;
+      default   :       //SAFE Guard
+        proceed=false;
+        break;
+      }
+    phase++;    
+    }
+  }
+#endif
+return locked;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to unlock access to a previously      */
+/*      locked table within database.                   */
+/*                                                     */
+/********************************************************/
+PUBLIC _Bool mar_unlock(MARPTR *marptr,_Bool commit)
+
+{
+#define OPEP    "unimar.c:mar_unlock,"
+
+_Bool unlocked;
+
+unlocked=false;
+#ifdef  DB_MYSQL
+  {
+  char *cmd;
+
+  unlocked=true;
+  cmd="ROLLBACK";
+  if (commit==true)
+    cmd="COMMIT";
+  if (mar_request(marptr,cmd)<0) {
+    unlocked=false;
+    (void) rou_alert(0,"%s Unable to commit/rollback table (Bug?) ",OPEP);
+    }
+  if (mar_request(marptr,"UNLOCK TABLE")<0) {
+    unlocked=false;
+    (void) rou_alert(0,"%s Unable to unlock table (Bug?) ",OPEP);
+    }
+  }
+#endif
+return unlocked;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to drop/free all result information   */
+/*                                                     */
+/********************************************************/
+PUBLIC MARRES *mar_dropresult(MARRES *rs)
+
+{
+#ifdef  DB_MYSQL
+(void) mysql_free_result((MYSQL_RES *)rs);
+#endif
+return (MARRES *)0;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to retrieve specific field value in   */
+/*      tupple under fieldname.                         */
+/*                                                     */
+/********************************************************/
+PUBLIC const char *mar_getvalue(MARRES *rs,int tuple,const char *fieldname)
+
+{
+#define OPEP    "unimar.c:pos_getfield,"
+register char *got;
+
+got=(char *)0;
+#ifdef  DB_MYSQL
+  {
+  register int position;
+
+  register uint numfields;
+  MYSQL_FIELD *fields;
+
+  position=-1;
+  numfields=mysql_num_fields((MYSQL_RES *)rs);
+  fields=mysql_fetch_fields((MYSQL_RES *)rs);
+  for (int i=0;i<numfields;i++) {
+    if (strcmp(fields[i].name,fieldname)==0) {
+      position=i;
+      break;
+      }
+    }
+  if (position<0) 
+    (void) rou_alert(0,"%s field '%s' unknown (Bug?)",OPEP,fieldname);
+  else {
+    register MYSQL_ROW row;
+
+    (void) mysql_data_seek((MYSQL_RES *)rs,tuple);
+    if ((row=mysql_fetch_row((MYSQL_RES *)rs))!=(MYSQL_ROW)0) 
+      got=row[position];
+    }
+  }
+#endif
+return got;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to extract data from the database.    */
+/*      Return POSRES status pointer (can be NULL is not*/
+/*      successfull.                                    */
+/*                                                     */
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to retrieve specific field in specific*/
+/*      row, accessed with the column number.           */
+/*                                                     */
+/********************************************************/
+PUBLIC char *mar_getfield(MARRES *rs,int tuple,int position)
+
+{
+register char *got;
+
+got=(char *)0;
+#ifdef  DB_MYSQL
+  {
+  MYSQL_ROW row;
+
+  (void) mysql_data_seek((MYSQL_RES *)rs,tuple);
+  if ((row=mysql_fetch_row((MYSQL_RES *)rs))!=(MYSQL_ROW)0) {
+    got=row[position];
+    }
+  }
+#endif
+return got;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to extract data from the database.    */
+/*      Return POSRES status pointer (can be NULL is not*/
+/*      successfull.                                    */
+/*                                                     */
+/********************************************************/
+PUBLIC MARRES *mar_gettupple(MARPTR *marptr,char *command)
+
+{
+#define OPEP    "unimar.c:mar_gettupple"
+
+register MARRES *marres;
+
+marres=(MARRES *)0;
+#ifdef  DB_MYSQL
+  {
+  MYSQL_RES *mysstatut;
+  register MYSQL *ms;
+
+  mysstatut=(MYSQL_RES *)0;
+  ms=(MYSQL *)marptr;
+  if (mysql_query(ms,command)==0) {
+    if ((mysstatut=mysql_store_result(ms))==(MYSQL_RES *)0)
+      (void) rou_alert(3,"%s result empty for cmd=<%s>",OPEP,command);
+    }
+  else {
+    (void) rou_alert(0,"%s pid='%05d' Unable to carry cmd=<%s>, error=<%s>",
+                        OPEP,getpid(),command,mysql_error((MYSQL *)ms));
+    }
+  marres=(MARRES *)mysstatut;
+  }
+#endif
+return marres;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to return the number of tupple related*/
+/*      related to a previous search.                   */
+/*                                                     */
+/********************************************************/
+PUBLIC int mar_nbrtupple(MARRES *marres)
+
+{
+#define OPEP    "unimar.c:mar_nbrtupple,"
+
+register int numrow;
+
+numrow=0;
+#ifdef  DB_MYSQL
+  numrow=mysql_num_rows((MYSQL_RES *)marres);
+#endif
+return numrow;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to request an action (insert,update   */
+/*      delete) to the database.                        */
+/*      return the number of tupple affected by the     */
+/*      command.                                        */
+/*                                                     */
+/********************************************************/
+PUBLIC int mar_request(MARPTR *marptr,char *command)
+
+{
+#define OPEP    "unipos.c:pos_action,"
+
+int number;
+
+number=-1;
+#ifdef  DB_MYSQL
+  {
+  register MYSQL *ms;
+
+  ms=(MYSQL *)marptr;
+  if (mysql_query(ms,command)==0) 
+    number=mysql_affected_rows(ms);
+  else
+    (void) rou_alert(0,"%s pid='%05d' Unable to carry cmd=<%s>, error=<%s>",
+                        OPEP,getpid(),command,mysql_error(ms));
+  }
+#endif
+return number;
+}
diff --git a/lib/unimar.d b/lib/unimar.d
new file mode 100644 (file)
index 0000000..f1f3b67
--- /dev/null
@@ -0,0 +1,51 @@
+unimar.o unimar.d : unimar.c /usr/include/stdc-predef.h /usr/include/pwd.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/types/FILE.h /usr/include/stdio.h \
+ /usr/include/bits/libc-header-start.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/stdlib.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h \
+ /usr/include/mysql/mysql.h /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h \
+ /usr/include/mysql/mariadb_com.h /usr/include/mysql/mariadb_version.h \
+ /usr/include/mysql/ma_list.h /usr/include/mysql/mariadb_ctype.h \
+ /usr/include/ctype.h /usr/include/mysql/mariadb_stmt.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h unimar.h
diff --git a/lib/unimar.h b/lib/unimar.h
new file mode 100644 (file)
index 0000000..613ace9
--- /dev/null
@@ -0,0 +1,64 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Base level subroutine declaration               */
+/*     to handle MYSQL/MARIADB SQL request.            */
+/*                                                     */
+/********************************************************/
+#ifndef        UNIMAR
+#define UNIMAR
+
+//reference to a SQL pointer reference
+typedef void MARPTR;
+
+//reference to a MariaDB result
+typedef void MARRES;
+
+//Database env variable
+extern  char *marenv[SQLENV];
+
+//Procedure to detect and 'clean' any single quote within a string
+extern char *mar_cleanquote(char *sequence);
+
+//procedure to return a dynamicaly allocated string
+//to do SQL compute a date with a delta in second
+extern char *mar_caldate(char *expression,int second);
+
+//Opening a postscript database
+extern MARPTR *mar_opensql(const char *host,const char *sqlport,const char *dbname);
+
+//closing a postscript database
+extern MARPTR *mar_closesql(MARPTR *marptr);
+
+//converting a time to a database representation
+extern const char *mar_fromunixtime(time_t timestamp);
+
+//converting a database time representation to unix time
+extern time_t mar_tounixtime(const char *date);
+
+//locking database one table access
+extern _Bool mar_lock(MARPTR *marptr,char *tablename);
+
+//unlocking the current database table lock access
+extern _Bool mar_unlock(MARPTR *marptr,_Bool commit);
+
+//procedure to drop/free all result information
+extern MARRES *mar_dropresult(MARRES *rs);
+
+//procedure to extract specific field within database
+extern char *mar_getfield(MARRES *rs,int tuple,int position);
+
+//procedure to extract specific field value within database
+extern const char *mar_getvalue(MARRES *rs,int tuple,const char *fieldname);
+
+//procedure to extract data from database
+extern MARRES *mar_gettupple(MARPTR *marptr,char *command);
+
+//procedure to return the number of entry within a result
+extern int mar_nbrtupple(MARRES *marres);
+
+//procedure to submit an action (insert,update,delete)
+//to a MySAL  database
+extern int mar_request(MARPTR *marptr,char *command);
+
+#endif
diff --git a/lib/unipar.c b/lib/unipar.c
new file mode 100644 (file)
index 0000000..8c9372f
--- /dev/null
@@ -0,0 +1,231 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Low level subroutine declaration                */
+/*     to handle an argv list and extract              */
+/*     parameters.                                     */
+/*                                                     */
+/********************************************************/
+#include       <stdio.h>
+#include       <stdlib.h>
+#include       <string.h>
+#include       <unistd.h>
+
+#include       "subrou.h"
+#include       "unipar.h"
+
+//application default config file
+PUBLIC  char  config[200]="/etc/"APPNAME"/"APPNAME".conf";
+PUBLIC  char  srcip[200]="";
+
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to init the argument list             */
+/*                                                     */
+/********************************************************/
+static ARGTYP *initparams()
+
+{
+ARGTYP *params;
+
+params=(ARGTYP *)calloc(1,sizeof(ARGTYP));
+params->argv=(char **)0;
+return params;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Display aid, usage parameter                    */
+/*                                                     */
+/********************************************************/
+static void usage_aid(char *name,const char *select)
+
+{
+static char *prg[]={
+               "chkspf domain ip_number",
+               (char *)0
+               };
+
+static char *options[]={
+              "c[-c config]",
+              "d[-d debug]",
+              "D[-D dbglive]",
+              "f[-f]",
+              "h[-h]",
+              "i[-i IP]",
+              "r[-r root]",
+              "v[-v]",
+               (char *)0
+               };
+
+static char *details[]={
+              "-c config\t: set config file\n",
+              "-d level\t: debug level [1-10]\n",
+              "-D dbglive\t: Procedure name to use with live debug\n",
+              "-f\t\t: start program in foreground (CLI) mode\n",
+              "-h\t\t: print this help message\n",
+              "-i IP\t\t: IP to be used as source IP\n",
+              "-r root\t\t: root working directory\n",
+              "-v\t\t: Print program version number\n",
+              (char *)0
+              };
+
+int num;
+char *pars;
+char msg[200];
+char explain[1000];
+
+num=0;
+pars=(char *)0;
+while (prg[num]!=(char *)0) {
+  if (strstr(prg[num],name)==prg[num]) {
+    pars=strchr(prg[num],' ');
+    if (pars!=(char *)0)
+      pars++;
+    break;
+    }
+  num++;
+  }
+num=0;
+(void) sprintf(explain,"\twhere:\n");
+(void) fprintf(stderr,"usage:\n  ");
+(void) sprintf(msg,"%s\t",name);
+while (options[num]!=(char *)0) {
+  char *ptr;
+
+  ptr=options[num];
+  if (strchr(select,*ptr)!=(char *)0) {
+    ptr++; 
+    (void) strcat(msg," ");
+    (void) strcat(msg,ptr);
+    (void) strcat(explain,"\t\t");
+    (void) strcat(explain,details[num]);
+    }
+  num++;
+  }
+(void) fprintf(stderr,"%s",msg);
+if (pars!=(char *)0) 
+  (void) fprintf(stderr," %s",pars);
+(void) fprintf(stderr,"\n");
+if ((select!=(char *)0)&&(strlen(select)>0)) {
+  (void) fprintf(stderr,"%s",explain);
+  }
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to free memory used by an arg         */
+/*     list                                            */
+/*                                                     */
+/********************************************************/
+PUBLIC ARGTYP *par_freeparams(ARGTYP *params)
+
+{
+if (params!=(ARGTYP *)0) {
+  if (params->argv!=(char **)0) {
+    char **ptr;
+
+    ptr=params->argv;
+    while (*ptr!=(char *)0) {
+      (void) free(*ptr);
+      ptr++;
+      }
+    (void) free(params->argv);
+    }  
+  (void) free(params);
+  params=(ARGTYP *)0;
+  rou_freestr(dbglive);
+  dbglive=(char *)0;
+  }
+return params;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to extract argument list              */
+/*     but cherry-pick only the one allowed by         */
+/*     sequence optstring.                             */
+/*     on error return an null argtype                 */
+/*                                                     */
+/********************************************************/
+PUBLIC ARGTYP *par_getparams(int argc,char * const argv[],const char *optstring)
+
+{
+ARGTYP *params;
+char *shortname;
+int c;
+
+params=initparams();
+if ((shortname=strrchr(argv[0],'/'))==(char *)0) 
+  shortname=argv[0];
+else
+  shortname++;
+opterr=0;               //no error message from getopt library routine
+while (((c=getopt(argc,argv,optstring))!=EOF)&&(params!=(ARGTYP *)0)) {
+  switch(c) {
+    case 'c'   :       //config file
+      (void) memset(config,'\000',sizeof(config));
+      (void) strncpy(config,optarg,sizeof(config)-1);
+      break;
+    case 'd'   :       //debug level
+      debug=atoi(optarg);
+      (void) rou_alert(1,"debug level is now '%d'",debug);
+      break;
+    case 'D'   :       //debug level
+      dbglive=strdup(optarg);
+      (void) rou_alert(1,"debug live string=<%s>",dbglive);
+      break;
+    case 'f'   :       //background/daemon mode
+      foreground=true;
+      (void) fprintf(stdout,"%s-%s, foreground mode requested\n",
+                            shortname,rou_getversion());
+      break;
+    case 'h'   :       //requestion program help
+      (void) fprintf(stdout,"%s-%s\n",shortname,rou_getversion());
+      (void) usage_aid(shortname,optstring);
+      params=par_freeparams(params);    //no going further
+      break;
+    case 'i'   :       //ser the Interface source IP to be used
+      (void) memset(srcip,'\000',sizeof(srcip));
+      (void) strncpy(srcip,optarg,sizeof(srcip)-1);
+      break;
+    case 'r'   :
+      if (rootdir!=(char *)0)
+        (void) free(rootdir);
+      rootdir=strdup(optarg);
+      break;
+    case 'v'   :
+      (void) fprintf(stdout,"%s:\tVersion:%s\n",
+                            shortname,rou_getversion());
+      (void) exit(0);           //just display version
+      break;
+    default    :
+      (void) fprintf(stdout,"%s-%s\n",shortname,rou_getversion());
+      (void) fprintf(stdout,"\"%s\" unexpected argument designator\n\n",
+                            argv[optind-1]);
+      (void) usage_aid(shortname,optstring);
+      params=par_freeparams(params);
+      break;
+    }
+  }
+(void) rou_setexecname(shortname);
+if ((params!=(ARGTYP *)0)&&(argc>optind)) {
+  int i;
+
+  params->argc=argc-optind;
+  params->argv=(char **)calloc(params->argc+1,sizeof(char *));
+  for (i=0;i<params->argc;i++) {
+    params->argv[i]=strdup(argv[optind+i]);
+    }
+  }
+return params;
+}
diff --git a/lib/unipar.d b/lib/unipar.d
new file mode 100644 (file)
index 0000000..6a7f507
--- /dev/null
@@ -0,0 +1,47 @@
+unipar.o unipar.d : unipar.c /usr/include/stdc-predef.h /usr/include/stdio.h \
+ /usr/include/bits/libc-header-start.h /usr/include/features.h \
+ /usr/include/features-time64.h /usr/include/bits/wordsize.h \
+ /usr/include/bits/timesize.h /usr/include/sys/cdefs.h \
+ /usr/include/bits/long-double.h /usr/include/gnu/stubs.h \
+ /usr/include/gnu/stubs-64.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/__fpos_t.h \
+ /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/stdlib.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h unipar.h
diff --git a/lib/unipar.h b/lib/unipar.h
new file mode 100644 (file)
index 0000000..d4db92b
--- /dev/null
@@ -0,0 +1,29 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Low level subroutine declaration                */
+/*     to handle an argv list and extract              */
+/*     parameters.                                     */
+/*                                                     */
+/********************************************************/
+#ifndef        UNIPAR
+#define UNIPAR
+
+#include        <stdbool.h>
+
+//structure of argument
+typedef struct {
+       int argc;               //number of main argument
+       char **argv;            //list of argument
+       }ARGTYP;
+
+extern  char          config[]; //Application config file
+extern  char           srcip[]; //Source IP to be used
+
+//free allocated memory used by a ARGTYP structure
+extern ARGTYP *par_freeparams(ARGTYP *params);
+
+//allocated memory and parse an argment list
+extern ARGTYP *par_getparams(int argc,char * const argv[],const char *optstring);
+
+#endif
diff --git a/lib/unipos.c b/lib/unipos.c
new file mode 100644 (file)
index 0000000..42b93d6
--- /dev/null
@@ -0,0 +1,575 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Low level subroutine implementation             */
+/*     to handle POSTGRES SQL request                  */
+/*                                                     */
+/********************************************************/
+#include        <libpq-fe.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <unistd.h>
+
+#include        "subrou.h"
+#include        "unipos.h"
+
+
+
+//checking if MYSQL database need to compiled
+#if DATABASE==USE_POSTGRESQL
+  #define       DB_POSTGRESQL
+#endif
+
+#ifdef          DB_POSTGRESQL
+char *posenv[SQLENV]= {
+                      "POSTGRESQL",
+                      "localhost",
+                      "5432",
+                      "mailleur",
+                      (char *)0
+                      };
+#else
+char *posenv[SQLENV]= {
+                      (char *)0,
+                      (char *)0,
+                      (char *)0,
+                      (char *)0,
+                      (char *)0
+                };
+#endif
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to execut a database request by daemon*/
+/*                                                      */
+/********************************************************/
+#ifdef  DB_POSTGRESQL
+static PGresult *request(PGconn *pf,char *directive)
+
+{
+#define OPEP    "unipos.c:request,"
+#define RELAX   1000000
+
+PGresult *statut;
+int try;
+
+statut=(PGresult *)0;
+try=0;
+while (statut==(PGresult *)0) {
+  try++;
+  switch(PQstatus(pf)) {
+    case CONNECTION_OK  :
+      if ((statut=PQexec(pf,directive))!=(PGresult *)0)
+        break;
+      else
+        (void) rou_alert(0,"%s Command <%s> failed, (error=<%s>), retrying",
+                           OPEP,directive,PQerrorMessage(pf));
+      // fall through
+      // NO Break, want DB reset
+    default             :
+      (void) rou_alert(0,"%s reseting postgres connection (try='%d')",
+                          OPEP,try);
+      (void) usleep(RELAX/2);
+      (void) PQreset(pf);
+      (void) usleep(RELAX/2);
+      break;
+    }
+  if (try>10) {
+    (void) rou_alert(0,"%s Unable to carry command <%s> Postgres error!",
+                        OPEP,directive);
+    break;
+    }
+  }
+return statut;
+
+#undef  RELAX
+#undef  OPEP
+}
+#endif
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to detect and 'clean' any single quote*/
+/*     within a string, this is mandatory for string   */
+/*      variable in the database.                       */
+/*     Single quote "'" are doubled becoming "''"      */
+/*                                                     */
+/********************************************************/
+PUBLIC char *pos_cleanquote(register char *dstr)
+
+{
+char *cleanstr;
+int taille;
+
+cleanstr=strdup("E'");
+taille=strlen(cleanstr)+2;
+if (dstr!=(char *)0) {
+  int max;
+  int i,p;
+  char *newclean;
+
+  /*to double char ' in the string                     */
+  max=strlen((const char *)dstr);
+  taille+=max;
+  if ((newclean=(char *)realloc(cleanstr,taille))!=(char *)0) {
+    cleanstr=newclean;
+    p=strlen(cleanstr);
+    for (i=0;i<max;i++,p++,dstr++) {
+      switch (*dstr) {
+        case '\\'              :
+        case '\''              :
+          taille++;
+          if ((newclean=(char *)realloc(cleanstr,taille))!=(char *)0) {
+           cleanstr=newclean;
+            cleanstr=(char *)realloc(cleanstr,taille);
+            cleanstr[p]=*dstr;
+           }
+          p++;
+          break;
+        default                :
+         break;
+        }
+      cleanstr[p]=*dstr;
+      }
+    cleanstr[p]='\000';
+    }
+  }
+(void) strcat(cleanstr,"'");
+return cleanstr;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to return a dynamicaly allocated      */
+/*      char * with an POSTGRESQL date computation      */
+/*      sequence                                        */
+/*                                                      */
+/********************************************************/
+PUBLIC char *pos_caldate(char *expression,int second)
+
+{
+char *seq;
+
+seq=(char *)0;
+(void) rou_asprintf(&seq,"(%s + interval '%d sec')",expression,second);
+return seq;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to establish a link with the          */
+/*      postgresq SQL server.                           */
+/*                                                      */
+/********************************************************/
+POSPTR *pos_opensql(const char *host,const char *sqlport,const char *dbname)
+
+{
+#define OPEP    "unipos.c:pos_opensql,"
+POSPTR *posptr;
+
+posptr=(POSPTR *)0;
+#ifdef  DB_POSTGRESQL
+  {
+  char *z;        //parameter null
+  PGconn *pf;
+
+  z=(char *)0;
+  pf=PQsetdbLogin(host,sqlport,z,z,dbname,z,z);
+  // Check if the connection is successful
+  if (PQstatus(pf)!=CONNECTION_OK) {
+    (void) fprintf(stderr,"%s Connection to database '%s' failed, cause '%s'\n",
+                           OPEP,dbname,PQerrorMessage(pf));
+    (void) PQfinish(pf);
+    pf=(PGconn *)0;
+    }
+  posptr=(POSPTR *)pf;
+  }
+#else
+  (void) fprintf(stderr,"%s not compiled to be used with postgresql\n",OPEP);
+#endif
+return (POSPTR *)posptr;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to close the link with the designated */
+/*      postgresql SQL server.                          */
+/*                                                      */
+/********************************************************/
+POSPTR *pos_closesql(POSPTR *posptr)
+
+{
+#define OPEP    "unipos.c:pos_closesql,"
+
+#ifdef  DB_POSTGRESQL
+  {
+  PGconn *pf;
+
+  pf=(PGconn *)posptr;
+  if (pf==(PGconn *)0) {
+    (void) fprintf(stderr,"%s Database link already closedi (Bug?)\n",OPEP);
+    (void) PQfinish(pf);
+    }
+  else {
+    (void) PQfinish((PGconn *)pf);
+    pf=(PGconn *)0;
+    }
+  posptr=(POSPTR *)pf;
+  }
+#else
+  (void) fprintf(stderr,"%s not compiled to be used with postgresql\n",OPEP);
+#endif
+return posptr;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return a unix time in acsii        */
+/*      format (data-base compatible).                  */
+/*                                                     */
+/********************************************************/
+PUBLIC const char *pos_fromunixtime(time_t timestamp)
+
+{
+#define TSTAMP   "%Y-%m-%d %H:%M:%S"
+
+static char unixdate[50];
+
+(void) memset(unixdate,'\000',sizeof(unixdate));
+
+#ifdef  DB_POSTGRESQL
+  {
+  struct tm *tminfo;
+
+  tminfo=localtime(&timestamp);
+ (void) strftime(unixdate,sizeof(unixdate),TSTAMP,tminfo);
+ }
+#endif
+
+return unixdate;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to return a unix time_t  from an ASCII*/
+/*      string (data-base compatible).                  */
+/*                                                     */
+/********************************************************/
+PUBLIC time_t pos_tounixtime(const char *date)
+
+{
+#define OPEP    "unipos.c:pos_tounixtime,"
+
+time_t datetime;
+
+datetime=(time_t)0;
+#ifdef  DB_POSTGRESQL
+  {
+  if (date!=(char *)0) {
+    static const char *dbtounix="%Y-%m-%d %H:%M:%S";
+
+    char *ptr;
+    struct tm tm;
+
+    if ((ptr=strchr(date,'.'))!=(char *)0) {
+      *ptr='\000';
+      }
+    (void) memset(&tm,'\000',sizeof(struct tm));
+    tm.tm_isdst=-1;
+    if (strptime(date,dbtounix,&tm)!=(char *)0) {
+      datetime=mktime(&tm);
+      }
+    else {
+      (void) rou_alert(0," Unable to convert <%s> to time_t (Bug?)",OPEP,date);
+      }
+    }
+  }
+#endif
+
+return datetime;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to lock access to a specific table    */
+/*      within database.                                */
+/*                                                     */
+/********************************************************/
+PUBLIC _Bool pos_lock(POSPTR *posptr,char *tablename)
+
+{
+#define OPEP    "unipos.c:pos_lock,"
+
+_Bool locked;
+
+locked=false;
+#ifdef  DB_POSTGRESQL
+  {
+  static const char *cmd="LOCK TABLE %s IN SHARE ROW EXCLUSIVE MODE";
+
+  char fullcmd[300];
+  int phase;
+  _Bool proceed;
+
+  (void) snprintf(fullcmd,sizeof(fullcmd),cmd,tablename);
+  phase=0;
+  proceed=true;
+  while (proceed==true) {
+    //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+    switch (phase) {
+      case 0    :       //Starting lock
+        if (pos_request(posptr,"BEGIN")<0) {
+          (void) rou_alert(0,"%s Unable to BEGIN to lock table <%s>",
+                              OPEP,tablename);
+          phase=999;    //no need to go further
+          }
+        break;
+      case 1    :       //lock table itself
+        if (pos_request(posptr,fullcmd)<0) {
+          (void) rou_alert(0,"%s Unable to lock table <%s>",
+                              OPEP,tablename);
+          phase=999;    //no need to go further
+          }
+        break;
+      case 2    :       //lock done
+        locked=true;
+        break;
+      default   :       //SAFE Guard
+        proceed=false;
+        break;
+      }
+    phase++;    
+    }
+  }
+#endif
+return locked;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to unlock access to a previously      */
+/*      locked table within database.                   */
+/*                                                     */
+/********************************************************/
+PUBLIC _Bool pos_unlock(POSPTR *posptr,_Bool commit)
+
+{
+#define OPEP    "unipos.c:pos_unlock,"
+
+_Bool unlocked;
+
+unlocked=false;
+#ifdef  DB_POSTGRESQL
+  {
+  char *cmd;
+
+  unlocked=true;
+  cmd="ROLLBACK";
+  if (commit==true)
+    cmd="COMMIT";
+  if (pos_request(posptr,cmd)<0) {
+    unlocked=false;
+    (void) rou_alert(0,"%s Unable to unlock table (Bug?) ",OPEP);
+    }
+  }
+#endif
+return unlocked;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to drop/free all result information   */
+/*                                                     */
+/********************************************************/
+PUBLIC POSRES *pos_dropresult(POSRES *rs)
+
+{
+#ifdef  DB_POSTGRESQL
+(void) PQclear(rs);
+#endif
+return (POSRES *)0;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to retrieve specific field value in   */
+/*      tupple, accessed at position                    */
+/*                                                     */
+/********************************************************/
+PUBLIC char *pos_getfield(POSRES *rs,int tuple,int position)
+
+{
+register char *got;
+
+got=(char *)0;
+#ifdef  DB_POSTGRESQL
+got=PQgetvalue((PGresult *)rs,tuple,position);
+#endif
+return got;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      procedure to retrieve specific field value in   */
+/*      tupple under fieldname.                         */
+/*                                                     */
+/********************************************************/
+PUBLIC const char *pos_getvalue(POSRES *rs,int tuple,const char *fieldname)
+
+{
+#define OPEP    "unipos.c:pos_getfield,"
+register char *got;
+
+got=(char *)0;
+#ifdef  DB_POSTGRESQL
+  {
+  int position;
+
+  if ((position=PQfnumber((PGresult *)rs,fieldname))<0) 
+    (void) rou_alert(0,"%s field '%s' unknown (BUG?)",OPEP,fieldname);
+  else {
+    if (PQgetisnull((PGresult *)rs,tuple,position)==false)
+      got=PQgetvalue((PGresult *)rs,tuple,position);
+    }
+  }
+#endif
+return got;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to extract data from the database.    */
+/*      Return POSRES status pointer (can be NULL is not*/
+/*      successfull.                                    */
+/*                                                     */
+/********************************************************/
+PUBLIC POSRES *pos_gettupple(POSPTR *posptr,char *command)
+
+{
+#define OPEP    "basmys.c:mys_tupple,"
+
+register POSRES *posres;
+
+posres=(POSRES *)0;
+#ifdef  DB_POSTGRESQL
+  {
+  PGresult *pgstatut;
+  register PGconn *pf;
+
+  pf=(PGconn *)posptr;
+  if ((pgstatut=request(pf,command))!=(PGresult *)0) {
+    if (PQresultStatus(pgstatut)!=PGRES_TUPLES_OK) {
+      (void) rou_alert(0,"%s SQL command <%s> failed",OPEP,command);
+      (void) PQclear(pgstatut);
+      pgstatut=(PGresult *)0;
+      }
+    }
+  posres=(POSRES *)pgstatut;
+  }
+#endif
+return posres;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to return the number of tupple related*/
+/*      related to a previous search.                   */
+/*                                                     */
+/********************************************************/
+PUBLIC int pos_nbrtupple(POSRES *posres)
+
+{
+register int numrow;
+
+numrow=0;
+#ifdef  DB_POSTGRESQL
+numrow=PQntuples((PGresult *)posres);
+#endif
+return numrow;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to request an action (insert,update   */
+/*      delete) to the database.                        */
+/*      return the number of tupple affected by the     */
+/*      command.                                        */
+/*                                                     */
+/********************************************************/
+PUBLIC int pos_request(POSPTR *posptr,char *command)
+
+{
+#define OPEP    "unipos.c:pos_action,"
+
+int number;
+
+number=-1;
+#ifdef  DB_POSTGRESQL
+  {
+  register PGconn *pf;
+  PGresult *pgstat;
+
+  pf=(PGconn *)posptr;
+  if ((pgstat=request((PGconn *)pf,command))!=(PGresult *)0) {
+    switch (PQresultStatus(pgstat)) {
+      case PGRES_COMMAND_OK       :
+        number=atoi(PQcmdTuples(pgstat));
+        break;
+      default                     : 
+        (void) rou_alert(0,"%s Command <%s> failed, (error=<%s> pgstatut='%d')",
+                            OPEP,command,PQerrorMessage(pf),
+                            PQresultStatus(pgstat));
+        break;
+      }
+    (void) PQclear(pgstat);
+    }
+  }
+#endif
+return number;
+
+#undef  OPEP
+}
diff --git a/lib/unipos.d b/lib/unipos.d
new file mode 100644 (file)
index 0000000..fa7260e
--- /dev/null
@@ -0,0 +1,48 @@
+unipos.o unipos.d : unipos.c /usr/include/stdc-predef.h /usr/include/libpq-fe.h \
+ /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/__fpos_t.h \
+ /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/postgres_ext.h \
+ /usr/include/pg_config_ext.h /usr/include/stdlib.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h unipos.h
diff --git a/lib/unipos.h b/lib/unipos.h
new file mode 100644 (file)
index 0000000..5a5ffcd
--- /dev/null
@@ -0,0 +1,64 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Base level subroutine declaration               */
+/*     to handle Postgresql SQL request.               */
+/*                                                     */
+/********************************************************/
+#ifndef        UNIPOS
+#define UNIPOS
+
+//reference to a SQL pointer reference
+typedef void POSPTR;
+
+//reference to a POSTGRESQL result
+typedef void POSRES;
+
+//Database env variable
+extern  char *posenv[SQLENV];
+
+//Procedure to detect and 'clean' any single quote within a string
+extern char *pos_cleanquote(char *sequence);
+
+//procedure to return a dynamicaly allocated string
+//to do SQL compute a date with a delta in second
+extern char *pos_caldate(char *expression,int second);
+
+//Opening a postscript database
+extern POSPTR *pos_opensql(const char *host,const char *sqlport,const char *dbname);
+
+//closing a postscript database
+extern POSPTR *pos_closesql(POSPTR *posptr);
+
+//converting a time to a database representation
+extern const char *pos_fromunixtime(time_t timestamp);
+
+//converting a database time representation to unix time
+extern time_t pos_tounixtime(const char *date);
+
+//locking database one table access
+extern _Bool pos_lock(POSPTR *posptr,char *tablename);
+
+//unlocking the current database table lock access
+extern _Bool pos_unlock(POSPTR *posptr,_Bool commit);
+
+//procedure to drop/free all result information
+extern POSRES *pos_dropresult(POSRES *rs);
+
+//procedure to extract specific field within database
+extern char *pos_getfield(POSRES *rs,int tuple,int position);
+
+//procedure to extract specific field value within database
+extern const char *pos_getvalue(POSRES *rs,int tuple,const char *fieldname);
+
+//procedure to extract data from postgresql database
+extern POSRES *pos_gettupple(POSPTR *posptr,char *command);
+
+//procedure to return the number of entry within a result
+extern int pos_nbrtupple(POSRES *posres);
+
+//procedure to submit an action (insert,update,delete)
+//to a postgresql database
+extern int pos_request(POSPTR *posptr,char *command);
+
+#endif
diff --git a/lib/uniprc.c b/lib/uniprc.c
new file mode 100644 (file)
index 0000000..e23b51a
--- /dev/null
@@ -0,0 +1,531 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Process management implementation module.       */
+/*                                                     */
+/********************************************************/
+#include       <sys/prctl.h>
+#include       <sys/resource.h>
+#include       <sys/stat.h>
+#include       <sys/wait.h>
+#include       <dirent.h>
+#include       <errno.h>
+#include       <fcntl.h>
+#include       <stdarg.h>
+#include       <stdio.h>
+#include       <stdlib.h>
+#include       <string.h>
+#include       <signal.h>
+#include       <syslog.h>
+#include       <unistd.h>
+
+#include        "subrou.h"
+#include        "uniprc.h"
+
+//directory to set lock
+#define DIRLOCK                 "/var/run/"APPNAME
+
+/*Process command override structure (proc)    */
+typedef        struct  {
+       int max;        //title max size
+       char *title;    //title storage area
+       }TITLTYP;
+
+static  TITLTYP *title=(TITLTYP *)0;    //storage area for /proc
+                                        //title display
+static  _Bool modopen;                  //boolean module open/close
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to change the current ID to an        */
+/*      application working ID                          */
+/*                                                     */
+/********************************************************/
+static uid_t getappid(char *appname,_Bool group)
+
+{
+#define OPEP    "uniprc.c:getappid"
+
+uid_t id;
+char *fullname;
+struct stat bufstat;
+
+id=(uid_t)0;
+fullname=rou_apppath(appname);
+if (stat(fullname,&bufstat)<0) 
+  (void) rou_alert(0,"%s Unable to get <%s> stat (error=<%s> bug?)",
+                     OPEP,fullname,strerror(errno));
+else {
+  if (group==false)
+    id=bufstat.st_uid;
+  else
+    id=bufstat.st_gid;
+  }
+fullname=rou_freestr(fullname);
+return id;
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to allow process core DUMP            */
+/*     to trace origin of problem                      */
+/*                                                     */
+/*      NOTE:                                           */
+/*      On linux to have a working coredump, you MUST   */
+/*      add to /etc/sysctl.conf                         */
+/*          fs.suid_dumpable=1                          */
+/*          kernel.core_uses_pid=1                      */
+/*          kernel.core_pattern=./core.%e.%p            */
+/*                                                     */
+/********************************************************/
+PUBLIC void prc_allow_core_dump()
+
+{
+struct rlimit limites;
+
+if (getrlimit(RLIMIT_CORE,&limites)<0) {
+  (void) fprintf(stderr,"getrlimit error='%s'",strerror(errno));
+  }
+limites.rlim_cur=limites.rlim_max;
+if (setrlimit(RLIMIT_CORE,&limites)<0) {
+  (void) fprintf(stderr,"setrlimit error='%s'",strerror(errno));
+  }
+(void) prctl(PR_SET_DUMPABLE,1,0,0,0);/*to allow core-dump     */
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*                                                     */
+/*     Procedure to allow exited child process         */
+/*     to leave the zombie stat.                       */
+/*                                                     */
+/********************************************************/
+PUBLIC void prc_nozombie()
+
+{
+while (waitpid(-1,(int *)0,WNOHANG)>0);
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     procedure to check if a process is              */
+/*     still up and running.                           */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool prc_checkprocess(pid_t pidnum)
+
+{
+#define        SIGCHECK 0      //signal to check if process
+                       //is existing.
+                       //
+_Bool status;
+
+status=false;
+switch(pidnum) {
+  case (pid_t)0        :       /*0 means no process    */
+    status=false;
+    break;
+  case (pid_t)1        :       /*init process always OK*/
+    status=true;
+    break;
+  default      :       /*standard process      */
+    if (kill(pidnum,SIGCHECK)==0)
+      status=true;
+    break;
+  }
+return status;
+#undef SIGCHECK
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     procedure to kill a set of process within a     */
+/*      list, return return the number of process       */
+/*      still up;                                       */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool prc_killchilds(pid_t *childs,int num,int maxretry)
+
+{
+int remain;
+
+remain=num;
+while (maxretry>0) {
+  remain=0;
+  for (int i=0;i<num;i++) {
+    (void)  prc_nozombie();
+    if (childs[i]==(pid_t)0)
+      continue;
+    if (prc_checkprocess(childs[i])==false) {
+      childs[i]=(pid_t)0;
+      continue;
+      }
+    remain++;
+    (void) kill(childs[i],SIGTERM);
+    }
+  if (remain==0)
+    break;
+  (void) sleep(1);
+  maxretry--;
+  }
+return remain;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to set/unset a lock                   */
+/*     return true if successful,                      */
+/*     false otherwise.                                */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool prc_locking(const char *lockname,int lock,int tentative)
+
+{
+#define        OPEP    "uniprc.c:lck_locking,"
+
+_Bool done;
+char *fullname;
+struct stat bufstat;
+int phase;
+_Bool proceed;
+
+done=false;
+fullname=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s pahse='%d'",OPEP,phase);
+  switch (phase) {
+    case 0     :       //setting lock filename
+      if (lockname==(const char *)0) {
+       (void) rou_alert(9,"%s lockname is missing (bug?)",OPEP);
+       phase=999;      //big trouble, No need to go further
+        }
+      break;
+    case 1     :       //creating the lock directory if needed
+      DIR *dir;
+
+      fullname=rou_apppath(DIRLOCK);
+      if ((dir=opendir(fullname))==(DIR *)0) {
+        if (rou_do_mkpdir(fullname)==false) {
+         (void) rou_alert(9,"Unable to create <%s> directory (system?/bug?)",
+                              fullname);
+         phase=999;    //big trouble, No need to go further
+          }
+        }
+      else
+        (void) closedir(dir);
+      (void) free(fullname);
+      break;
+    case 2     :       //setting lock filename
+      const char *fname;
+      char *name;
+
+      if ((fname=strrchr(lockname,'/'))==(char *)0)
+        fname=lockname;
+      else
+        fname++; 
+      name=(char *)calloc(sizeof(DIRLOCK)+strlen(fname)+10,sizeof(char));
+      (void) sprintf(name,"%s/%s.lock",DIRLOCK,fname);
+      fullname=rou_apppath(name);
+      (void) free(name);
+      break;
+    case 3     :       //checking if link already exist
+      if (stat(fullname,&bufstat)<0) {
+        phase++;        //no need to check lock contents
+        }
+      break;
+    case 4     :       //making lockname
+      if (S_ISREG(bufstat.st_mode)!=0) {
+        FILE *fichier;
+
+        if ((fichier=fopen(fullname,"r"))!=(FILE *)0) {
+          pid_t pid;
+          char strloc[80];
+
+         (void) fgets(strloc,sizeof(strloc)-1,fichier);
+         (void) fclose(fichier);
+          if (sscanf(strloc,"%lu",(u_long *)(&pid))==1) {
+            (void) rou_alert(5,"Locking, check %d process active",pid);
+           if (prc_checkprocess(pid)==false) {
+              (void) rou_alert(6,"Locking, removing pid=%d unactive lock",pid);
+             (void) unlink(fullname);
+              }
+           else {
+              if (lock==LCK_LOCK) {
+                (void) rou_alert(0,"lock check, found %d process still active",pid);
+               phase=999;      //no need to go further
+                }
+             }
+           }
+         }
+       }
+      break;
+    case 5     :       //do we need to unlock ?
+      if (lock==LCK_UNLOCK) {
+       (void) rou_alert(6,"%s Request unlocking <%s>",OPEP,fullname);
+       (void) unlink(fullname);
+       done=true;
+       phase=999;      //No need to go further
+       }
+      break;
+    case 6     :       //making lockname
+      (void) rou_alert(6,"%s Request locking <%s>",OPEP,fullname);
+      while (tentative>0) {
+       int handle;
+
+        tentative--;
+        if ((handle=open(fullname,O_RDWR|O_EXCL|O_CREAT,0640))>=0) {
+          char numid[30];
+
+          (void) snprintf(numid,sizeof(numid),"%d\n",getpid());
+          (void) write(handle,numid,strlen(numid));
+          (void) close(handle);
+         done=true;
+         break;        //breaking "tentative" loop
+         }
+       else {
+          (void) rou_alert(3,"Trying one more second to lock <%s> (error=<%s>)",
+                             fullname,strerror(errno));
+          (void) sleep(1);
+         }
+       }
+      break;
+    default    :       //SAFE Guard
+      if (done==false)
+        (void) rou_alert(2,"Unable to set <%s> lock (config?)",lockname);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+fullname=rou_freestr(fullname);
+return done;
+#undef OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*                                                     */
+/*     Procedure to put a process in background        */
+/*     mode.                                           */
+/*     Return the child process id.                    */
+/*                                                      */
+/********************************************************/
+PUBLIC pid_t prc_divedivedive()
+
+{
+#define        OPEP    "uniprc:rou_divedivedive,"
+pid_t childpid;
+
+childpid=(pid_t)0;
+switch (childpid=fork()) {
+  case -1      :
+    (void) fprintf(stderr,"%s, Unable to dive! (error=<%s>)",
+                          OPEP,strerror(errno));
+    break;
+  case  0      :
+    //we are now in background mode
+    (void) setsid();
+    break;
+  default      :
+    //waiting for ballast to fill up :-}}
+    (void) sleep(1);
+    break;
+  }
+return childpid;
+#undef OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to free used by "title" struct memory.*/
+/*                                                      */
+/********************************************************/
+PUBLIC void prc_cleantitle()
+
+{
+if (title!=(TITLTYP *)0) {
+  if (environ!=(char **)0) {
+    int i;
+
+    for (i=0;environ[i]!=(char *)0;i++) {
+      (void) free(environ[i]);
+      environ[i]=(char *)0;
+      }
+    (void) free(environ);
+    environ=(char **)0;
+    }
+  (void) free(title);
+  title=(TITLTYP *)0;
+  }
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*                                                     */
+/*     Procedure to find and limit space to            */
+/*     be used as status information available         */
+/*     from proc (ps ax)                               */
+/*                                                      */
+/********************************************************/
+PUBLIC void prc_preptitle(int argc,char *argv[],char *env[])
+
+{
+char *lastend;
+
+lastend=(char *)0;
+(void) prc_cleantitle();
+title=(TITLTYP *)calloc(1,sizeof(TITLTYP));
+if (argv!=(char **)0) {
+  int i;
+
+  title->title=argv[0];
+  for (i=1;i<argc;i++) {
+    lastend=argv[i]+strlen(argv[i]);
+    argv[i]=(char *)0;
+    }
+  }
+if (env!=(char **)0) {
+  int i;
+
+  environ=(char **)0;
+  for (i=0;env[i]!=(char *)0;i++) {
+    char *valeur;
+
+    lastend=env[i]+strlen(env[i]);
+    valeur=strdup(env[i]);
+    (void) putenv(valeur);
+    }
+  }
+title->max=lastend-title->title;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to update title information.          */
+/*      Title information is avalable via the           */
+/*      CLI (commande line interface) "ps"              */
+/*                                                      */
+/********************************************************/
+PUBLIC void prc_settitle(const char *fmt,...)
+
+{
+va_list args;
+
+va_start(args,fmt);
+if ((title!=(TITLTYP *)0)&&(title->title!=(char *)0)) {
+  (void) memset(title->title,'\000',title->max);
+  (void) vsnprintf(title->title,title->max,fmt,args);
+  }
+va_end(args);
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to change the current ID (must be     */
+/*      root) to the application/program "owner:group"  */
+/*                                                      */
+/********************************************************/
+void prc_chgid(char *appnom)
+
+{
+#define        OPEP    "uniprc.c:prc_chgid,"
+
+#define        MSG \
+  "%s Unable to %s to '%d' (error=<%s> (cur u/g id='%d/%d',eid='%d/%d') bug?)"
+
+struct rlimit limites;
+
+(void) memset(&limites,'\000',sizeof(struct rlimit));
+if (getuid()==0) {     /*is changing ID possible       */
+  uid_t uid;
+  uid_t gid;
+
+  gid=getappid(appnom,true);
+  uid=getappid(appnom,false);
+  if (setegid(gid)<0)
+    (void) rou_alert(0,MSG,OPEP,"setegid",gid,
+                                strerror(errno),
+                                getuid(),getgid(),
+                                geteuid(),getegid());
+  if (seteuid(uid)<0)
+    (void) rou_alert(0,MSG,OPEP,"seteuid",uid,
+                                strerror(errno),
+                                getuid(),getgid(),
+                                geteuid(),getegid());
+  (void) rou_alert(6,"%s ID now set euid='%d', egid='%d' uid='%d' guid='%d'",
+                      OPEP,geteuid(),getegid(),getuid(),getgid());
+  }
+if (getrlimit(RLIMIT_CORE,&limites)<0) {
+  (void) rou_alert(0,"%s getrlimit error='%s'",OPEP,strerror(errno));
+  }
+limites.rlim_cur=limites.rlim_max;
+if (setrlimit(RLIMIT_CORE,&limites)<0) {
+  (void) rou_alert(0,"%s setrlimit error='%s'",OPEP,strerror(errno));
+  }
+//to allow application core-dump
+(void) prctl(PR_SET_DUMPABLE,1,0,0,0);
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to "open/close" module and do         */
+/*      homework purpose                                */
+/*      return zero if everything right                 */
+/*                                                      */
+/********************************************************/
+PUBLIC int prc_modeuniprc(_Bool mode)
+
+{
+#define OPEP    "uniprc.c:prc_modeuniprc"
+
+int status;
+
+status=0;
+if (mode!=modopen) {
+  (void) rou_modesubrou(mode);
+  switch ((int)mode) {
+    case true     :
+      (void) prc_allow_core_dump();
+      break;
+    case false    :
+      break;
+    default       :
+      (void) fprintf(stderr,"Calling %s with wrong mode='%d' (Bug?!):",
+                            OPEP,(int)mode);
+      status=-1;
+      break;
+    }
+  modopen=mode;
+  }
+return status;
+#undef  OPEP
+}
diff --git a/lib/uniprc.d b/lib/uniprc.d
new file mode 100644 (file)
index 0000000..c9ec409
--- /dev/null
@@ -0,0 +1,72 @@
+uniprc.o uniprc.d : uniprc.c /usr/include/stdc-predef.h /usr/include/sys/prctl.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/linux/prctl.h /usr/include/linux/types.h \
+ /usr/include/asm/types.h /usr/include/asm-generic/types.h \
+ /usr/include/asm-generic/int-ll64.h /usr/include/asm/bitsperlong.h \
+ /usr/include/asm-generic/bitsperlong.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/include/sys/resource.h /usr/include/bits/resource.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_rusage.h /usr/include/sys/stat.h \
+ /usr/include/bits/types/struct_timespec.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/stat.h /usr/include/bits/struct_stat.h \
+ /usr/include/sys/wait.h /usr/include/signal.h \
+ /usr/include/bits/signum-generic.h /usr/include/bits/signum-arch.h \
+ /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/types/stack_t.h /usr/include/sys/ucontext.h \
+ /usr/include/bits/sigstack.h /usr/include/bits/sigstksz.h \
+ /usr/include/bits/ss_flags.h /usr/include/bits/types/struct_sigstack.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/bits/types/idtype_t.h \
+ /usr/include/dirent.h /usr/include/bits/dirent.h \
+ /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h \
+ /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/dirent_ext.h \
+ /usr/include/errno.h /usr/include/bits/errno.h \
+ /usr/include/linux/errno.h /usr/include/asm/errno.h \
+ /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
+ /usr/include/fcntl.h /usr/include/bits/fcntl.h \
+ /usr/include/bits/fcntl-linux.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/stdlib.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/alloca.h /usr/include/bits/stdlib-float.h \
+ /usr/include/string.h /usr/include/bits/types/locale_t.h \
+ /usr/include/bits/types/__locale_t.h /usr/include/strings.h \
+ /usr/include/syslog.h /usr/include/sys/syslog.h \
+ /usr/include/bits/syslog-path.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subrou.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h uniprc.h
diff --git a/lib/uniprc.h b/lib/uniprc.h
new file mode 100644 (file)
index 0000000..ecdf612
--- /dev/null
@@ -0,0 +1,56 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/************************************************/
+/*                                             */
+/*     Unit level process management           */
+/*      declaration                             */
+/*                                             */
+/************************************************/
+#ifndef        UNIPRC
+#define UNIPRC
+
+#include        <stdbool.h>
+#include        <stdio.h>
+
+#include        "subrou.h"
+
+extern char **environ;          //context enviromenent variable
+
+#define LCK_UNLOCK   0 /*unlocking request     */
+#define        LCK_LOCK     1  /*locking requets       */
+
+//To clean the command line title area
+extern void prc_cleantitle();
+
+//To duplicate enviromenet library and used as command line data
+extern void prc_preptitle(int argc,char *argv[],char *env[]);
+
+//To insert a string in the command area
+extern void prc_settitle(const char *fmt,...);
+
+//To allow application to core dump is memory is
+//big trouble need to be investigated
+extern void prc_allow_core_dump();
+
+//routine to make sure all child process are out of
+//zombie state
+extern void prc_nozombie();
+
+//routine to check if a proces is still up and running
+extern _Bool prc_checkprocess(pid_t pidnumber);
+
+//Routine to kill a list of subprocess
+extern _Bool prc_killchilds(pid_t *childs,int num,int maxretry);
+
+//lock application (to avoid running multiple daemon)
+extern _Bool prc_locking(const char *lockname,int lock,int tentative);
+
+//procedure to put application in deamon mode
+extern pid_t prc_divedivedive();
+
+//procedure to change the current id (root) to the application "owner"
+extern void prc_chgid(char *appnom);
+
+//homework to be done before starting/stopping module.
+extern int prc_modeuniprc(_Bool mode);
+
+#endif
diff --git a/lib/unisig.c b/lib/unisig.c
new file mode 100644 (file)
index 0000000..258141d
--- /dev/null
@@ -0,0 +1,256 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Module for signal handling level                */
+/*                                                     */
+/********************************************************/
+#include        <errno.h>
+#include        <sys/wait.h>
+#include        <sys/signalfd.h>
+#include        <stdlib.h>
+#include        <stdio.h>
+#include        <string.h>
+
+#include        "unisig.h"
+
+PUBLIC  _Bool hangup;           //Hangup signal received
+PUBLIC  _Bool reload;           //reload configuration signal received
+PUBLIC  _Bool wakeup;           //application wakeup under an alarm
+PUBLIC  _Bool childout;         //application child signal
+
+static  _Bool modopen;          //boolean module open/close
+static  int sfd;                //Signal detail information
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to catch signal and do what is        */
+/*     needed.                                         */
+/*                                                     */
+/********************************************************/
+static void gotsigsegv(int sig)
+
+{
+#define        OPEP    "unisig.c:trapsigsegv,"
+
+switch (sig) {
+  case SIGSEGV   :
+    (void) rou_core_dump("Genuine memory violation (Bug?)");
+    break;
+  default        :
+    (void) rou_core_dump("%s Unexpected signal <%s> received (BUG!)",
+                     OPEP,strsignal(sig));
+    break;
+  }
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to set a signal handler in case of    */
+/*      segmentation violation.                         */
+/*                                                      */
+/********************************************************/
+static void trapsigsegv(_Bool onoff,sighandler_t trap)
+
+{
+static _Bool prvon=false;
+static struct sigaction oldsa;
+
+if (onoff==true) {
+  struct sigaction newsa;
+
+  if (prvon==true)      //in case we set signal twice
+    (void) sigaction(SIGSEGV,&oldsa,(struct sigaction *)0);
+  newsa.sa_flags=0;
+  newsa.sa_handler=trap;
+  (void) sigemptyset(&newsa.sa_mask);
+  (void) sigaction(SIGSEGV,&newsa,&oldsa);
+  }
+else {
+  (void) sigaction(SIGSEGV,&oldsa,(struct sigaction *)0);
+  }
+prvon=onoff;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to trap "critical" signal and set     */
+/*      flag accordingly;                               */
+/*                                                      */
+/********************************************************/
+PUBLIC void sig_alrm(int sig)
+
+{
+#define OPEP    "unisig.c:sig_alrm,"
+
+(void) rou_alert(11,"%s signal <%s> received",OPEP,strsignal(sig));
+switch (sig)
+  {
+  case SIGCHLD         : {
+    childout=true;
+    while (waitpid(-1,(int *)0,WNOHANG)>0);
+    }
+    break;
+  case SIGQUIT         :
+  case SIGTERM         :
+    hangup=true;
+    break;
+  case SIGINT          :
+  case SIGHUP          :
+    reload=true;
+    break;
+  case SIGALRM         :       //got an alarm, just to wakeup
+    wakeup=true;
+    break;
+  case SIGUSR1         :
+    debug++;
+    if (debug>10)
+      debug=10;
+    (void) rou_alert(0,"%s deamon, new increased debug level now set to '%d'",
+                        APPNAME,debug);
+    break;
+  case SIGUSR2         :
+    debug--;
+    if (debug<0)
+      debug=0;
+    (void) rou_alert(0,"%s deamon, new decreased debug level now set to '%d'",
+                        APPNAME,debug);
+    break;
+  case SIGPIPE         :
+    (void) rou_alert(0,"%s JMPDBG got SIGPIPE",APPNAME);
+    break;
+  default              :
+    (void) rou_alert(0,"Unexpected Signal [%d]/<%s> received",
+                        sig,strsignal(sig));
+    break;
+  }
+//able to receive next signal
+(void) signal(sig,sig_alrm);
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to trap all meaningfull signal needed */
+/*      bay application                                 */
+/*                                                      */
+/********************************************************/
+PUBLIC void sig_trapsignal(_Bool onoff,sighandler_t trap)
+
+{
+#define OPEP    "unisig.c:settrap"
+#define NUMINTR        9
+
+static struct sigaction *olds[NUMINTR];
+static _Bool alldone=false;
+
+if (onoff==alldone) {
+  switch ((int)onoff) {
+    case true  :
+      (void) rou_crash("%s signal trap already set (Bug?)",OPEP);
+      break;
+    case false :
+      (void) rou_crash("%s signal trap already UNset (Bug?)",OPEP);
+      break;
+    default    :
+      (void) rou_crash("%s unproper settrap value (very bad Bug!)",OPEP);
+      break;
+    } 
+  }
+if (onoff==true) {
+  sigset_t mask;
+  struct sigaction *newsa;
+  int i;
+
+  (void) sigemptyset(&mask);
+  (void) sigaddset(&mask,SIGCHLD);
+  newsa=(struct sigaction *)calloc(1,sizeof(struct sigaction));
+  newsa->sa_flags=0;
+  newsa->sa_handler=trap;
+  for (i=0;i<NUMINTR;i++) {
+    if (olds[i]==(struct sigaction *)0) 
+      olds[i]=calloc(1,sizeof(struct sigaction));
+    }
+  (void) sigaction(SIGUSR2,newsa,olds[0]);
+  (void) sigaction(SIGUSR1,newsa,olds[1]);
+  (void) sigaction(SIGINT,newsa,olds[2]);
+  (void) sigaction(SIGTERM,newsa,olds[3]);
+  (void) sigaction(SIGQUIT,newsa,olds[4]);
+  (void) sigaction(SIGHUP,newsa,olds[5]);
+  (void) sigaction(SIGALRM,newsa,olds[6]);
+  (void) sigaction(SIGCHLD,newsa,olds[7]);
+  (void) sigaction(SIGPIPE,newsa,olds[8]);
+  (void) free(newsa);
+  if ((sfd=signalfd(-1, &mask, 0))<0) {
+    (void) rou_alert(0,"%s Unable to set signalfd (error=<%s>)",
+                        OPEP,strerror(errno));
+    }
+  }
+else {
+  int i;
+
+  (void) sigaction(SIGPIPE,olds[8],(struct sigaction *)0);
+  (void) sigaction(SIGCHLD,olds[7],(struct sigaction *)0);
+  (void) sigaction(SIGALRM,olds[6],(struct sigaction *)0);
+  (void) sigaction(SIGHUP,olds[5],(struct sigaction *)0);
+  (void) sigaction(SIGQUIT,olds[4],(struct sigaction *)0);
+  (void) sigaction(SIGTERM,olds[3],(struct sigaction *)0);
+  (void) sigaction(SIGINT,olds[2],(struct sigaction *)0);
+  (void) sigaction(SIGUSR1,olds[1],(struct sigaction *)0);
+  (void) sigaction(SIGUSR2,olds[0],(struct sigaction *)0);
+  for (i=0;i<NUMINTR;i++) {
+    if (olds[i]!=(struct sigaction *)0) 
+      (void) free(olds[i]);
+    olds[i]=(struct sigaction *)0;
+    }
+  }
+alldone=onoff;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to "open/close" module and do         */
+/*      homework purpose                                */
+/*      return zero if everything right                 */
+/*                                                      */
+/********************************************************/
+PUBLIC int sig_modeunisig(_Bool mode)
+
+{
+#define OPEP    "unisig.c:sig_modeunisig"
+
+int status;
+
+status=0;
+if (mode!=modopen) {
+  switch ((int)mode) {
+    case true     :
+      (void) rou_modesubrou(mode);
+      (void) trapsigsegv(mode,gotsigsegv);
+      break;
+    case false    :
+      (void) trapsigsegv(mode,gotsigsegv);
+      (void) rou_modesubrou(mode);
+      break;
+    default       :
+      (void) fprintf(stderr,"Calling %s with wrong mode='%d' (Bug?!):",
+                            OPEP,(int)mode);
+      status=-1;
+      break;
+    }
+  modopen=mode;
+  }
+return status;
+#undef  OPEP
+}
diff --git a/lib/unisig.d b/lib/unisig.d
new file mode 100644 (file)
index 0000000..db49b7c
--- /dev/null
@@ -0,0 +1,61 @@
+unisig.o unisig.d : unisig.c /usr/include/stdc-predef.h /usr/include/errno.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/sys/wait.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/signal.h \
+ /usr/include/bits/signum-generic.h /usr/include/bits/signum-arch.h \
+ /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timespec.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/include/bits/types/stack_t.h /usr/include/sys/ucontext.h \
+ /usr/include/bits/sigstack.h /usr/include/bits/sigstksz.h \
+ /usr/include/bits/ss_flags.h /usr/include/bits/types/struct_sigstack.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/bits/types/idtype_t.h \
+ /usr/include/sys/signalfd.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/libc-header-start.h \
+ /usr/include/bits/wchar.h /usr/include/bits/stdint-intn.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/bits/stdint-least.h \
+ /usr/include/bits/signalfd.h /usr/include/stdlib.h \
+ /usr/include/bits/floatn.h /usr/include/bits/floatn-common.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/timer_t.h \
+ /usr/include/endian.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/struct_timeval.h \
+ /usr/include/alloca.h /usr/include/bits/stdlib-float.h \
+ /usr/include/stdio.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/string.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/strings.h unisig.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/linux/posix_types.h /usr/include/linux/stddef.h \
+ /usr/include/asm/posix_types.h /usr/include/asm/posix_types_64.h \
+ /usr/include/asm-generic/posix_types.h /usr/include/time.h \
+ /usr/include/bits/time.h /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h
diff --git a/lib/unisig.h b/lib/unisig.h
new file mode 100644 (file)
index 0000000..9c50ea9
--- /dev/null
@@ -0,0 +1,33 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/************************************************/
+/*                                             */
+/*     Unit level signal management            */
+/*      declaration                             */
+/*                                             */
+/************************************************/
+#ifndef        UNISIG
+#define UNISIG
+
+#include        <stdbool.h>
+#include        <signal.h>
+
+#include        "subrou.h"
+
+typedef void (*sighandler_t)(int);
+
+
+extern  _Bool hangup;   //Hangup signal received
+extern  _Bool reload;   //reload configuration signal received
+extern  _Bool wakeup;   //just got a signal
+extern  _Bool childout; //A child process just vanished
+
+//"standard" signal trap
+extern void sig_alrm(int sig);
+
+//trapping application signal (SIGTERM, SIGHUP, etc...
+extern void sig_trapsignal(_Bool onoff,sighandler_t trap);
+
+//homework to be done before starting/stopping module.
+extern int sig_modeunisig(_Bool mode);
+
+#endif
diff --git a/lib/unisql.c b/lib/unisql.c
new file mode 100644 (file)
index 0000000..1ffdebe
--- /dev/null
@@ -0,0 +1,251 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Module for low level SQL subroutine             */
+/*                                                     */
+/********************************************************/
+#include        <ctype.h>
+#include        <errno.h>
+#include        <iconv.h>
+#include        <stdbool.h>
+#include        <stdlib.h>
+#include        <string.h>
+
+#include       "subrou.h"
+#include       "unisql.h"
+
+#define DLANG   "UTF-8"
+
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Procedure to convert a char sequence    */
+/*     from on char set to another.            */
+/*     if successful return a new pointer      */
+/*                                             */
+/************************************************/
+static char *cnvconvert(char *lfrom,char *lto,char *encoded)
+
+{
+#define        OPEP    "unisql.c:cnvconvert,"
+
+char *converted;
+char *marker;
+iconv_t hconv;
+size_t inbuflft;
+char *inbuf;
+size_t outbuflft;
+char *outbuf;
+int phase;
+int proceed;
+
+converted=(char *)0;
+marker=(char *)0;
+hconv=(iconv_t)0;
+inbuflft=(size_t)0;
+inbuf=(char *)0;
+outbuflft=(size_t)0;
+outbuf=(char *)0;
+phase=0;
+proceed=1;
+while (proceed==true) {
+  switch (phase) {
+    case 0     :       /*opening conv channel  */
+      if ((hconv=iconv_open(lto,lfrom))==(iconv_t)-1) {
+       (void) rou_alert(0,"%s Unexpected lang conversion request "
+                           "from=<%s> to=<%s> (errno=<%s>)",
+                          OPEP,lfrom,lto,strerror(errno));
+       phase=999;      /*trouble trouble       */
+       }
+      break;
+    case 1     :       /*doing convertion      */
+      inbuflft=strlen(encoded);
+      outbuflft=(inbuflft*4)+1;        /*spare room    */
+      inbuf=encoded;
+      marker=(char *)calloc(outbuflft,sizeof(char));
+      outbuf=marker;
+      while (inbuflft>0) {
+        if (iconv(hconv,&inbuf,&inbuflft,&outbuf,&outbuflft)==(size_t)(-1)) {
+          inbuf++;
+          inbuflft=strlen(inbuf);
+          //replacing out of lfrom subset char by '_'
+          (void) strcat(marker,"_");
+          outbuf++;
+          outbuflft--;
+          }
+        }
+      break;
+    case 2     :       /*doing convertion      */
+      if (iconv_close(hconv)<0) 
+       (void) rou_alert(0,"%s Unexpected close conversion error "
+                           "(errno=<%s>) (bug?)",OPEP,strerror(errno));
+      break;
+    default    :       /*SAFE Guard            */
+      converted=marker;
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return converted;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to free memory used by a user         */
+/*      definition.                                     */
+/*                                                      */
+/********************************************************/
+PUBLIC USRTYP *sql_freeusr(USRTYP *usr)
+
+{
+if (usr!=(USRTYP *)0) {
+  usr->hash=rou_freestr(usr->hash);
+  usr->password=rou_freestr(usr->password);
+  usr->email=rou_freestr(usr->email);
+  (void) free(usr);
+  usr=(USRTYP *)0;
+  }
+return usr;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to free memory used by a session      */
+/*      definition.                                     */
+/*                                                      */
+/********************************************************/
+PUBLIC SESTYP *sql_freeses(SESTYP *ses)
+
+{
+if (ses!=(SESTYP *)0) {
+  ses->duration=rou_freestr(ses->duration);
+  ses->hsubject=rou_freestr(ses->hsubject);
+  ses->hfrom=rou_freestr(ses->hfrom);
+  ses->sfrom=rou_freestr(ses->sfrom);
+  ses->sessid=rou_freestr(ses->sessid);
+  (void) free(ses);
+  ses=(SESTYP *)0;
+  }
+return ses;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*      Procedure to free memory used by a remote SMTP  */
+/*      data record definition.                         */
+/*                                                      */
+/********************************************************/
+PUBLIC SRVTYP *sql_freesrv(SRVTYP *smtpdata)
+
+{
+if (smtpdata!=(SRVTYP *)0) {
+  smtpdata->reverse=rou_freestr(smtpdata->reverse);
+  smtpdata->listing=rou_freestr(smtpdata->listing);
+  smtpdata->rmtip=rou_freestr(smtpdata->rmtip);
+  (void) free(smtpdata);
+  smtpdata=(SRVTYP *)0;
+  }
+return smtpdata;
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Routine to check if string is properly  */
+/*     coded, if it is not the case replace all*/
+/*     dubious charactere with plain ASCII     */
+/*                                             */
+/************************************************/
+PUBLIC char *sql_checkencoding(char *strencoded)
+
+{
+#define OPEP    "unisql.c:sql_checkencoding,"
+
+char *converted;
+char *ptr;
+int phase;
+int proceed;
+
+converted=(char *)0;
+ptr=(char *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0     :       //set the destination convertion
+      if (((ptr=getenv("DB_LANG"))==(char *)0)||(strlen(ptr)==0))
+        ptr=DLANG;
+      break;
+    case 1      :
+      if ((converted=cnvconvert(ptr,ptr,strencoded))!=(char *)0) 
+        phase=999;     //everything fine converting to DB_LANG
+      break;
+    case 2      :       //conversion trouble
+      (void) rou_alert(0,"%s Wrong encoding for db access key <%s> (Config?)",
+                          OPEP,strencoded);
+      break;
+    default    :       /*SAFE Guard            */
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return converted;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/************************************************/
+/*                                             */
+/*     Routine to check if key is acceptable   */
+/*      to be used as a search element.         */
+/*      return true if OK.                      */
+/*                                             */
+/************************************************/
+PUBLIC _Bool sql_check_key(char *key)
+
+{
+#define OPEP    "unisql.c:sql_check_key,"
+
+_Bool isok;
+
+isok=true;
+if ((key!=(char *)0)&&(strlen(key)>0)) {
+  for (int i=0,l=strlen(key);i<l;i++) {
+    register char car;
+
+    car=key[i];
+    switch (car) {
+      case '@'      :
+      case '.'      :
+        continue;
+        break;
+      default       :
+        if (isalnum((int)car)!=0)
+          continue;
+        break;
+      }
+    isok=false;
+    (void) rou_alert(0,"%s search key <%s> not accepted, '%c' is not "
+                       "an acceptable char",OPEP,key,car);
+    break;      //no need to check further
+    }
+  }
+return isok;
+
+#undef OPEP
+}
diff --git a/lib/unisql.d b/lib/unisql.d
new file mode 100644 (file)
index 0000000..8e708f0
--- /dev/null
@@ -0,0 +1,41 @@
+unisql.o unisql.d : unisql.c /usr/include/stdc-predef.h /usr/include/ctype.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/types/locale_t.h \
+ /usr/include/bits/types/__locale_t.h /usr/include/errno.h \
+ /usr/include/bits/errno.h /usr/include/linux/errno.h \
+ /usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
+ /usr/include/asm-generic/errno-base.h /usr/include/iconv.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h \
+ /usr/include/stdlib.h /usr/include/bits/libc-header-start.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/bits/floatn.h /usr/include/bits/floatn-common.h \
+ /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
+ /usr/include/bits/types/timer_t.h /usr/include/bits/stdint-intn.h \
+ /usr/include/endian.h /usr/include/bits/byteswap.h \
+ /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
+ /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
+ /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/types/struct_timespec.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/strings.h subrou.h /usr/include/linux/types.h \
+ /usr/include/asm/types.h /usr/include/asm-generic/types.h \
+ /usr/include/asm-generic/int-ll64.h /usr/include/asm/bitsperlong.h \
+ /usr/include/asm-generic/bitsperlong.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/time.h /usr/include/bits/time.h \
+ /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_itimerspec.h unisql.h
diff --git a/lib/unisql.h b/lib/unisql.h
new file mode 100644 (file)
index 0000000..95afe3e
--- /dev/null
@@ -0,0 +1,90 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/************************************************/
+/*                                             */
+/*     Low level SQL subroutine declaration    */
+/*                                             */
+/************************************************/
+#ifndef        UNISQL
+#define UNISQL
+
+#include        <sys/types.h>
+
+#define SQLENV  5       //number of ENV parameters for 
+                        //SQL definition
+                        
+typedef enum    {
+        sql_select,     //select a record
+        sql_insert,     //create a record
+        sql_update,     //update a record
+        sql_delete,     //delete a record
+        sql_uknown      //No action
+        }SQLENUM;
+
+//defining a database column definition retrieval
+typedef struct  {
+        int num;        //field number
+        char *name;     //field name;
+        }FLDTYP;
+
+//structure about user within the database
+typedef struct  {
+        char *email;    //user emails
+        char *password; //user crypted password
+        char *hash;     //'email:realm:password' MD5
+        u_int  lock;    //account is lock
+        u_long space;   //user space used
+        u_long mxspace; //user maximun space
+        }USRTYP;
+
+//structure about an email session
+//A session is the exchaneg mail from, rcpts and data
+//with a remote server
+typedef struct  {
+        char *sessid;   //session id
+        char *sfrom;    //the "mail from" coming from SMTP exchange
+        char *hsubject; //the "Subject:" as within the email header
+        char *hfrom;    //the "From:" as within the email header
+        char *duration; //Session duration un millisec
+        u_long taille;  //Email size
+        }SESTYP;
+
+//Structure about email action status (managing email)
+typedef struct  {
+        char *sessid;   //session id
+        char code;      //Email status code
+        char *remoteip; //The remoter server IP number
+        char *reverse;  //The remoter server reverse address
+        char *sfrom;    //the "MAIL FROM:" within the SMTP exchange
+        char *hfrom;    //the "From:" within the email header
+        char *hsubject; //the "Subject:" within the email header
+        char *rcptto;   //email recipient
+        char **resp;    //Multiline status
+        }ACTTYP;
+
+//Structure about remotes SMTP server
+typedef struct  {
+        char *rmtip;    //Remote IP number
+        time_t lastscan;//last scan time
+        time_t update;  //last update time
+        int links;      //Number of connection from this remote 
+        int credit;     //Remote credit number
+        char *reverse;  //Remote serveur reverss-address
+        char *listing;  //Remote listing explaination
+        }SRVTYP;
+
+//procedure to free space used by an USRTYP
+extern USRTYP *sql_freeusr(USRTYP *usr);
+
+//procedure to free memory used by a SESTYP
+extern SESTYP *sql_freeses(SESTYP *ses);
+
+//procedure to free memory used by a remote server data
+extern SRVTYP *sql_freesrv(SRVTYP *srvdata);
+
+//Procedure to check if string is properly coded according DB_LANG
+extern char *sql_checkencoding(char *strencoded);
+
+//Procedure to check if a SQL key is acceptable
+extern _Bool sql_check_key(char *key);
+
+#endif
diff --git a/lib/unitls.c b/lib/unitls.c
new file mode 100644 (file)
index 0000000..22eac5c
--- /dev/null
@@ -0,0 +1,1364 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Implement all routine to manage SMTP low level  */
+/*     exchange.                                       */
+/*                                                     */
+/********************************************************/
+#include        <openssl/asn1.h>
+#include        <openssl/bn.h>
+#include        <openssl/err.h>
+#include        <openssl/x509.h>
+#include        <openssl/x509v3.h>
+#include        <sys/socket.h>
+#include        <ifaddrs.h>
+#include        <netdb.h>
+#include        <poll.h>
+#include        <signal.h>
+#include        <stdio.h>
+#include        <unistd.h>
+
+#include       "subrou.h"
+#include       "unitls.h"
+
+//alternate define SSL_CIPHER_LIST       "ALL:!LOW"
+#define SSL_CIPHER_LIST "DEFAULT"
+
+static  _Bool modopen;    //module open/close status
+//Binding information as AFN
+static  AFNTYP **afns=(AFNTYP **)0;    
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to report display ALL ssl error queue */
+/*                                                      */
+/********************************************************/
+static void showerrorstack(char *msg)
+
+{
+int error;
+
+(void) rou_alert(0,"SSL error queue caused by <%s>",msg);
+while ((error=ERR_get_error())!=0) {
+  (void) rou_alert(0,"\tcode='%d, <%s>",error,ERR_error_string(error,(char *)0));
+  }
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to report display all erro queue      */
+/*      after afyer an ssl action. Return true if no    */
+/*      fatal error found.                              */
+/*                                                      */
+/********************************************************/
+static _Bool showtlserror(TLSTYP *tls,int sslerror,char *msg,...)
+
+{
+#define OPEP    "unitls.c:showtlserror"
+_Bool good;
+va_list args;
+char info[300]; 
+
+good=true;
+va_start(args,msg);
+(void) vsnprintf(info,sizeof(info),msg,args);
+(void) rou_alert(0,"showtlserror sslerror='%d' code='%d'",
+                    sslerror,SSL_get_error(tls->ssl,sslerror));
+if (sslerror<=0) {
+  char *detail;
+  int code;
+
+  good=false;
+  detail="Unexpected error";
+  switch (sslerror) {
+    case 0      :
+      (void) showerrorstack(info);
+      break;
+    default     :
+      if (tls->ssl==(SSL *)0)    //in case of trouble
+        (void) rou_core_dump("%s Unexpected NULL SSL (Bug?)",OPEP);
+      code=SSL_get_error(tls->ssl,sslerror);
+      switch (code) {
+        case SSL_ERROR_ZERO_RETURN      :
+          detail="zero return";
+          break;
+        case SSL_ERROR_WANT_READ        :
+          detail="want read";
+          break;
+        case SSL_ERROR_WANT_WRITE       :
+          detail="want write";
+          break;
+        case SSL_ERROR_WANT_CONNECT     :
+          detail="want connect";
+          break;
+        case SSL_ERROR_WANT_X509_LOOKUP :
+          detail="want X509 lookup";
+          break;
+        case SSL_ERROR_SYSCALL          :
+          if (ERR_get_error()==0)
+            detail="Premature EOF";
+          else
+            detail="syscall error";
+          break;
+        case SSL_ERROR_SSL              :
+          (void) showerrorstack(info);
+          detail=ERR_error_string(ERR_get_error(),(char *)0);
+          break;
+        default                         :
+          (void) rou_alert(0,"%s, caused by <%s>  Unexpected ssl error='%d'",
+                             OPEP,info,code);
+          detail=(char *)0;
+          break;
+        }
+      break;
+    }
+  if (detail!=(char *)0)
+    (void) rou_alert(4,"%s, caused by <%s>, error=<%s>",OPEP,info,detail);
+  }
+va_end(args);
+return good;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to open an SSL channel                */
+/*                                                      */
+/********************************************************/
+static TLSTYP *freetls(TLSTYP *tls)
+
+{
+if (tls!=(TLSTYP *)0) {
+  tls->peerip=rou_freestr(tls->peerip);
+  tls->peername=rou_freestr(tls->peername);
+  tls->locip=rou_freestr(tls->locip);
+  tls->locport=rou_freestr(tls->locport);
+  if (tls->ssl!=(SSL *)0) {
+    (void) SSL_clear(tls->ssl);
+    (void) SSL_free(tls->ssl);
+    }
+  if (tls->ctx!=(SSL_CTX *)0)
+    (void) SSL_CTX_free(tls->ctx);
+  (void) free(tls);
+  tls=(TLSTYP *)0;
+  }
+return tls;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to load the certificat beeing as root */
+/*                                                      */
+/********************************************************/
+static _Bool load_certs(TLSTYP *tls,const char *certs[])
+
+{
+#define OPEP    "unitls.c:load_certs,"
+
+_Bool isok;
+uid_t gid;      //ccurrent group id
+uid_t uid;      //ccurrent user id
+
+gid=getegid();  //let be back to root if needed
+uid=geteuid();  //to open certificate file
+if (setegid(getgid())<0)
+  (void) rou_core_dump("%s Unable to set the Egid to '%d' (error=<%s>",
+                        OPEP,getgid(),strerror(errno));
+if (seteuid(getuid())<0)
+  (void) rou_core_dump("%s Unable to set the Euid to '%d' (error=<%s>",
+                        OPEP,getuid(),strerror(errno));
+isok=true;
+for (int i=0;(i<3)&&(isok==true);i++) {
+  switch (i) {
+    case 0      :       //loading the key file
+      (void) rou_alert(7,"%s key file=<%s>",OPEP,certs[i]);
+      isok=(SSL_CTX_use_PrivateKey_file(tls->ctx,certs[i],SSL_FILETYPE_PEM)==1);
+      break;
+    case 1      :       //loading the chain file
+      (void) rou_alert(7,"%s chain file=<%s>",OPEP,certs[i]);
+      isok=(SSL_CTX_use_certificate_chain_file(tls->ctx,certs[i])==1);
+      break;
+    case 2      :       //loading the root certificate
+      (void) rou_alert(7,"%s root certificate=<%s>",OPEP,certs[i]);
+      isok=(SSL_CTX_load_verify_locations(tls->ctx,certs[i],(const char *)0)==1);
+      break;
+    default     :       //SAFE Guard
+      (void) rou_alert(0,"%s value override (Bug!)",OPEP);
+      break;
+    }
+  if (isok==false) {
+    char msg[200];
+
+    (void) snprintf(msg,sizeof(msg),"%s, file[%d] <%s> missing?",OPEP,i,certs[i]);
+    (void) showtlserror(tls,0,msg);
+    break;
+    }
+  }
+(void) setegid(gid);    //recover the standard application
+(void) seteuid(uid);    //uid:gid
+return isok;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to set the link certificate           */
+/*                                                      */
+/********************************************************/
+static int set_crypting(TLSTYP *tls,_Bool server)
+
+{
+#define OPEP    "unitls.c:set_crypting"
+
+static const char *cenv[]={"CA_KEY","CA_CERT","CA_ROOT","CA_VERIFY"};
+static const char *envsec="SSL_SECURITY";
+
+int done;
+const char *certs[sizeof(cenv)/sizeof(char *)];
+const SSL_METHOD *(*tls_methode)();
+const char *cipher_list;
+int seclevel;
+const char *certext;
+int mode;
+int phase;
+_Bool proceed;
+
+done=false;
+tls_methode=TLS_client_method;
+if ((cipher_list=getenv("CIPHER_LIST"))==(char *)0)
+  cipher_list=SSL_CIPHER_LIST;
+if (getenv(envsec)!=(char *)0)
+  seclevel=atoi(getenv(envsec));
+else
+  seclevel=2;
+certext="CLT";
+if (tls->server==true) {
+  tls_methode=TLS_server_method;
+  certext="SRV";
+  }
+mode=SSL_VERIFY_NONE;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(0,"%s JMPDBG phase='%d'",OPEP,phase);
+  switch (phase) {
+    case 0      :       //loading certificate names
+      for (int i=0;i<(sizeof(cenv)/sizeof(char *));i++) {
+        char data[100];
+
+        (void) snprintf(data,sizeof(data),"%s_%s",cenv[i],certext);
+        certs[i]=getenv(data);
+        if (certs[i]==(char *)0) {
+          (void) rou_alert(0,"%s Missing <%s> environment variable (config?)",
+                              OPEP,data);
+          phase=999;    //missing  certificate info.
+          }
+        }
+      //Set the verify mode
+      if (certs[3]!=(char *)0) {  //env value can be missing
+        if (atoi(certs[3])==1) {
+          mode=SSL_VERIFY_PEER; //to make sure peer certificate is OK
+          }
+        }
+      break;
+    case 1      :       //pre-configure SSL
+      (void) SSL_library_init();
+      (void) SSL_load_error_strings();
+      (void) ERR_clear_error();
+      if ((tls->ctx=SSL_CTX_new(tls_methode()))==(SSL_CTX *)0) {
+        (void) showtlserror(tls,0,"Get CTX");
+        phase=999;      //no need to go furter
+        }
+      break;
+    case 2      :       //load certificate information
+      if (load_certs(tls,certs)==false) {
+        (void) rou_alert(0,"%s Unable to load certificate information (config?)",
+                            OPEP);
+        phase=999;      //no need to go furter
+        }
+      break;
+    case 3      :       //verify management
+      if (((mode&SSL_VERIFY_PEER)!=0)||(tls->server==false))
+        tls->checkpeer=true;
+      (void) SSL_CTX_set_verify(tls->ctx,mode,(int(*)())0);
+      (void) SSL_CTX_set_purpose(tls->ctx,X509_PURPOSE_ANY);
+      (void) SSL_CTX_set_verify_depth(tls->ctx,7);
+      (void) SSL_CTX_set_options(tls->ctx,SSL_OP_ALL);
+      //(void) SSL_CTX_set_min_proto_version(tls->ctx,TLS1_3_VERSION);
+      (void) SSL_CTX_set_security_level(tls->ctx,seclevel);
+      if (SSL_CTX_set_cipher_list(tls->ctx,cipher_list)==0) {
+        (void) showtlserror(tls,0,"No cipher list");
+        phase=999;
+        }
+      break;
+    case 4      :       //allowing partial write
+      (void) SSL_CTX_set_mode(tls->ctx,SSL_MODE_ENABLE_PARTIAL_WRITE);
+      break;
+    case 5      :       //everything fine
+      done=true;
+      break;
+    default     :       //SAFE Guard
+      if (done==false) {
+        if (tls->ctx!=(SSL_CTX *)0) {
+          (void) SSL_CTX_free(tls->ctx);
+          tls->ctx=(SSL_CTX *)0;
+          }
+        }
+      proceed=false;
+      break;
+    }
+  phase++;
+ }
+return done;
+
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to get peerip, local name and port    */
+/*                                                      */
+/********************************************************/
+static void getnames(TLSTYP *tls)
+
+{
+#define OPEP "unitls.c:getnames"
+
+socklen_t taille;
+struct sockaddr connip;
+char host[NI_MAXHOST];
+char serv[NI_MAXSERV];
+int phase;
+_Bool proceed;
+
+taille=(socklen_t)(sizeof(connip));
+(void) strcpy(host,"");
+(void) strcpy(serv,"");
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Checking if everithing is fine
+      if (tls==(TLSTYP *)0) {
+        (void) rou_alert(0,"%s, TLS is NULL (bug?)",OPEP);
+        }
+      break;
+    case 1      :       //Getting local name and service
+      (void) strcpy(host,"lochost");
+      (void) strcpy(serv,"locserv");
+      if (getsockname(tls->handle,&connip,&taille)==0) {
+        int mode;
+        int status;
+    
+        mode=NI_NAMEREQD|NI_NUMERICSERV;
+        status=getnameinfo(&connip,taille,host,sizeof(host),serv,sizeof(serv),mode);
+        if (status!=0) {
+          (void) strcpy(host,"loc-Unknown");
+          (void) strcpy(serv,"loc-Unknown");
+          }  
+        }
+      tls->locip=strdup(host); 
+      tls->locport=strdup(serv); 
+      break;
+    case 2      :       //Getting peer name and service
+      (void) strcpy(host,"remhost");
+      (void) strcpy(serv,"remserv");
+      if (getpeername(tls->handle,&connip,&taille)==0) {
+        int mode;
+        int status;
+    
+        mode=NI_NUMERICHOST|NI_NUMERICSERV;
+        status=getnameinfo(&connip,taille,host,sizeof(host),serv,sizeof(serv),mode);
+        if (status!=0) {
+          (void) strcpy(host,"peer-Unknown");
+          (void) strcpy(serv,"peer-Unknown");
+          }
+        tls->peerip=strdup(host); 
+        mode=NI_NAMEREQD|NI_NUMERICSERV;
+        status=getnameinfo(&connip,taille,host,sizeof(host),serv,sizeof(serv),mode);
+        if (status!=0) 
+          (void) strcpy(host,"Unknown");
+        tls->peername=strdup(host); 
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to parse an email protocol            */
+/*                                                     */
+/********************************************************/
+PUBLIC PROTYP tls_getprotocol(const char *strproto)
+
+{
+static struct  {
+                PROTYP proto;
+                const char *voca;
+                }prolist[]={
+                          {pro_smtp,""},
+                          {pro_smtp,"smtp"},
+                          {pro_smtps,"smtps"},
+                          {pro_unknwn,(char *)0}
+                          };
+PROTYP proto;
+
+
+proto=pro_unknwn;
+for (int i=0;prolist[i].voca!=(char *)0;i++) {
+  if (strcasecmp(strproto,prolist[i].voca)==0) {
+    proto=prolist[i].proto;
+    break;
+    }
+  }
+return proto;
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Procedure to the system interrace and build     */
+/*      a list of local IPV4 interface available        */
+/*      and build the LISTYP structure.                 */
+/*                                                     */
+/********************************************************/
+static void buildafns()
+
+{
+#define OPEP    "unitls.c,buildafns,"
+
+char *localip;
+struct ifaddrs *ifaddr;
+int phase;
+_Bool proceed;
+
+localip=strdup("");
+afns=(AFNTYP **)0;
+ifaddr=(struct ifaddrs *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Get the interface list
+      if (getifaddrs(&ifaddr)<0) {
+        (void) rou_alert(0,"%s Unable to get local IP (error=<%s> system?)",
+                           OPEP,strerror(errno));
+        phase=999;      //no need to go further
+        }
+      break;
+    case 1      :       //Scan interface lis
+      for (struct ifaddrs *ifa=ifaddr;ifa!=(struct ifaddrs *)0;ifa=ifa->ifa_next) {
+        int family;
+        int taille;
+        int er;
+        char *newloc;
+        char host[NI_MAXHOST];
+
+        if (ifa->ifa_addr==(struct sockaddr *)0)
+            continue;
+        family=ifa->ifa_addr->sa_family;
+        switch (family) {
+          case AF_INET  :
+            taille=sizeof(struct sockaddr_in);
+            break;
+          case AF_INET6 :
+            taille=sizeof(struct sockaddr_in6);
+            continue;   //No scanning for IPV6 Number (Jun 2025)
+            break;
+          default       :
+            continue;
+            break;
+          }
+        er=getnameinfo(ifa->ifa_addr,taille,host,NI_MAXHOST,NULL,0,NI_NUMERICHOST);
+        if (er!=0) {
+          (void) rou_alert(0,"%s etnameinfo() failed: (error=<%s> system?)",
+                             OPEP,strerror(errno));
+          continue;
+          }
+        newloc=(char *)calloc(strlen(localip)+strlen(host)+3,sizeof(char));
+        if (strlen(localip)>0) {
+          (void) strcpy(newloc,localip);
+          (void) strcat(newloc,",");
+          }
+        (void) strcat(newloc,host);
+        (void) free(localip);
+        localip=newloc;
+        }
+      break;
+    case 2      :       //converting interface IP list to AFNTYP list
+      afns=afn_getipnums(localip);
+      (void) freeifaddrs(ifaddr);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+localip=rou_freestr(localip);
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to build a ID string about the        */
+/*      encrypted connecion within TLS                  */
+/*                                                      */
+/********************************************************/
+PUBLIC char *tls_getcipherid(TLSTYP *tls)
+
+{
+#define OPEP    "unitls.c:tls_getcipherid,"
+#define MXID    72
+
+char *cipherid;
+const SSL_CIPHER *cipher;
+const char *version;
+const char *name;
+const char *verif;
+int bits;
+int phase;
+_Bool proceed;
+
+cipherid=(char *)0;
+cipher=(const SSL_CIPHER *)0;
+version=(char *)0;
+name=(char *)0;
+verif=(char *)0;
+bits=0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //do we have a TLS
+      if ((tls==(TLSTYP *)0)||(tls->ssl==(SSL *)0)) {
+        (void) rou_alert(0,"%s TLS or SSL NULL (Bug?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 1      :       //do we have a TLS
+      if ((cipher=SSL_get_current_cipher(tls->ssl))==(const SSL_CIPHER *)0) {
+        (void) rou_alert(0,"%s Unable to get cypher (BUg?)",OPEP);
+        phase=999;
+        }
+      break;
+    case 2      :       //do we have a TLS
+      version=SSL_CIPHER_get_version(cipher);
+      name=SSL_CIPHER_get_name(cipher);
+      bits=SSL_CIPHER_get_bits(cipher,0);
+      if (strcmp(version,"SSLv3")==0)
+        version="TLSv1/SSLv3";
+      break;
+    case 3      :       //set verify mode
+      switch(SSL_get_verify_mode(tls->ssl)) {
+        case SSL_VERIFY_PEER                             :
+            //No break
+        case SSL_VERIFY_CLIENT_ONCE                      :
+            //No break
+        case (SSL_VERIFY_NONE)                          :
+          switch(SSL_get_verify_result(tls->ssl)) {
+            case (X509_V_OK)            :
+              verif="OK";
+              break;
+            default                     :
+              verif="FAIL";
+              break;
+            }
+          break;
+        default                                         :
+          verif="NO";
+          break;
+        }
+      break;
+    case 4      :       //we have ALL data
+      cipherid=(char *)calloc(1,MXID);
+      (void) snprintf(cipherid,MXID,"version=%s cipher=%s bits=%d verify=%s",
+                                     version,name,bits,verif);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return cipherid;
+#undef  MXID
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to verify remote certificate          */
+/*                                                      */
+/********************************************************/
+PUBLIC _Bool tls_verify(TLSTYP *tls)
+
+{
+#define OPEP    "unitls.c:tls_verify,"
+X509 *peer;
+_Bool ok;
+int phase;
+_Bool proceed;
+
+peer=(X509 *)0;
+ok=false;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //do we need to check peer
+      if (tls->checkpeer==false) {
+        (void) rou_alert(1,"%s Peer [%s]; certificate not verified",
+                            OPEP,tls->peerip);
+        phase=999;      //No need to check certificate
+        }
+      break;
+    case 1      :       //get remote certificate
+      if ((peer=SSL_get_peer_certificate(tls->ssl))==(X509 *)0) {
+        char msg[200];
+
+        (void) snprintf(msg,sizeof(msg),"%s, Unable to get certificate "
+                                        "from remote [%s]",
+                                        OPEP,tls->peerip);
+        (void) showtlserror(tls,0,msg);
+        phase=999;      //no need to go furter
+        }
+      break;
+    case 2      :  {    //displaying certificate
+      char *line;
+
+      line=X509_NAME_oneline(X509_get_subject_name(peer),0,0);
+      (void) rou_alert(4,"Certificate subject=<%s>",line);
+      line=rou_freestr(line);
+      line=X509_NAME_oneline(X509_get_issuer_name(peer),0,0);
+      (void) rou_alert(4,"Certificate issuer=<%s>",line);
+      line=rou_freestr(line);
+      }
+      break;
+    case 3      :  {     //verifying certificate
+      int verif;
+      char *line;
+      char *cn;         //Certificate Common Name
+
+      cn="CN=Unknown";
+      line=X509_NAME_oneline(X509_get_subject_name(peer),0,0);
+      if (line!=(char *)0) {
+        char *ptr;
+
+        if ((ptr=strstr(line,"CN="))!=(char *)0) 
+          cn=ptr;
+        }
+      verif=SSL_get_verify_result(tls->ssl);
+      switch (verif) {
+        case X509_V_OK  :
+          (void) rou_alert(1,"Peer is [%s]/%s",tls->peerip,tls->peername);
+          (void) rou_alert(1,"%s; Remote certificate is verified",cn);
+          break;
+        default         :
+          (void) rou_alert(0,"%s Remote certificate status='%d'",OPEP,verif);
+          break;
+        }
+      line=rou_freestr(line);
+      }
+      break;
+    case 4      :       //everything is fine
+      (void) X509_free(peer);
+      ok=true;
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return ok;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return the current sercurity level */
+/*                                                      */
+/********************************************************/
+PUBLIC int tls_get_sec_level(TLSTYP *tls)
+
+{
+int level;
+
+level=-1;
+if (tls!=(TLSTYP *)0) {
+  level=SSL_get_security_level(tls->ssl);
+  }
+return level;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to open an SSL channel                */
+/*                                                      */
+/********************************************************/
+PUBLIC TLSTYP *tls_opentls(int handle,_Bool server)
+
+{
+#define OPEP    "unitls.c:tls_opentls"
+
+TLSTYP *tls;
+int phase;
+_Bool proceed;
+
+tls=(TLSTYP *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  //(void) rou_alert(6,"%s JMPDBG phase='%d', serveur='%d'",OPEP,phase,server);
+  switch (phase) {
+    case 0      :       //prepare the structure first;
+      tls=(TLSTYP *)calloc(1,sizeof(TLSTYP));
+      tls->handle=handle;
+      tls->server=server;
+      (void) getnames(tls);
+      break;
+    case 1      :       //set certificate according client/server mode
+      if (set_crypting(tls,server)==false) {
+        (void) rou_alert(1,"%s Unable to open a TLS channel",OPEP);
+        tls=freetls(tls);
+        phase=999;
+        }
+      break;
+    case 2      :       //Setting the TLS channel
+      if ((tls->ssl=tls_setsocket(handle,tls->ctx))==(SSL *)0) 
+        phase=999;      //trouble, trouble no need to go furter
+      break;
+    case 3      :       //Setting the TLS channel actif
+      switch (server) {
+        case false  :   //mode client
+          if (tls_connect(tls)<0) 
+            phase=999;  //trouble trouble
+          break;
+        case true   :   //mode server
+          if (tls_accept(tls)<0) 
+            phase=999;  //trouble trouble
+          break;
+        }
+      break;
+    case 4      :       //Setting the TLS channel actif
+      proceed=false;
+      break;
+    default     :       //SAFE guard
+      tls=freetls(tls);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return tls;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to close an SSL channel               */
+/*                                                      */
+/********************************************************/
+PUBLIC TLSTYP *tls_closetls(TLSTYP *tls)
+
+{
+#define OPEP    "unitls.c:tls_closetls"
+
+int status;
+int phase;
+_Bool proceed;
+
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Check if TLS is OK
+      if (tls==(TLSTYP *)0) {
+        (void) rou_core_dump("%s Unexpected NULL SSL (Bug?)",OPEP);
+        }
+      break;
+    case 1      :       //set certificate
+      //valgrind is trapping SIGPIPE and stop
+      struct sigaction oldsa;
+
+      (void) sigaction(SIGPIPE,(struct sigaction *)0,&oldsa);
+      (void) signal(SIGPIPE, SIG_IGN);
+      status=SSL_shutdown(tls->ssl);
+      if (status<0)
+        (void) rou_alert(0,"%s, Link with [%s], ssl_shutdown status='%d'",
+                            OPEP,tls->peerip,status);
+      (void) sigaction(SIGPIPE,&oldsa,(struct sigaction *)0);
+      break;
+    default     :       //SAFE guard
+      tls=freetls(tls);
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return tls;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to write a buffer contents to an SSL  */
+/*      channel. Retune the number of char written or   */
+/*      -1 if trouble.                                  */
+/*                                                      */
+/********************************************************/
+PUBLIC int tls_write(TLSTYP *tls,char *buffer,int tosend)
+
+{
+#define OPEP    "unitls.c:tls_write"
+
+int sent;
+
+sent=0;
+if (tls!=(TLSTYP *)0) {
+  _Bool proceed;
+  int sofar;
+
+  proceed=true;
+  while (proceed==true) {
+    int status;
+
+    status=0;
+    proceed=false;
+    sofar=SSL_write(tls->ssl,buffer,tosend);
+    switch (sofar) {
+      case -1     :       //trouble to write
+        switch (status=SSL_get_error(tls->ssl,-1)) {
+          case SSL_ERROR_WANT_READ        :     //
+          case SSL_ERROR_WANT_WRITE       :     //"Wanted" error
+            (void) usleep(1000);
+            proceed=true;
+            break;
+          default                         :
+            (void) showtlserror(tls,sofar,"%s Premature EOF with crypted link",
+                                OPEP);
+            sent=-1;
+            break;
+          }
+        break;
+      case  0     :       //no char sent
+        switch (SSL_get_error(tls->ssl,0)) {
+          case SSL_ERROR_SYSCALL          :       //EOF received?
+            tls->goteof=true;
+            if (ERR_get_error()!=0)
+              (void) rou_alert(0,"%s wrong EOF",OPEP);
+            break;
+          default                         :
+            (void) rou_alert(0,"%s Unexpected SSL_write error='%d'",
+                                OPEP,SSL_get_error(tls->ssl,0));
+            (void) showtlserror(tls,0,"show SSL error",OPEP);
+            break;
+          }
+        break;
+      default     :       //some character sent
+        sent+=sofar;
+        if (sent<tosend)
+          proceed=true;
+        break;
+      }
+    }
+  }
+return sent;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to read up to maxread character from  */
+/*      an tls channel and store into a buffer.         */
+/*      return the number of char read, or -1 if        */
+/*      trouble.                                        */
+/*                                                      */
+/********************************************************/
+PUBLIC int tls_read(TLSTYP *tls,char *buffer,int maxread)
+
+{
+#define OPEP    "unitls.c:tls_read"
+
+int got;
+
+got=-1;
+if (tls!=(TLSTYP *)0) {
+  int status;
+
+  status=0;
+  got=SSL_read(tls->ssl,buffer,maxread);
+  switch (got) {
+    case -1     :       //trouble to write
+      status=SSL_get_error(tls->ssl,-1);
+      switch (status) {
+        case SSL_ERROR_WANT_READ        :       //"wanted" error
+        case SSL_ERROR_WANT_WRITE       :       //
+          (void) usleep(1000);                  //to ease up incoming charactere
+          got=0;
+          break;
+        default                         :
+          (void) showtlserror(tls,-1,"%s  Unexpected SSL_read status='%d')",
+                                      OPEP,status);
+          break;
+        }
+      break;
+    case  0     :       //no char received
+      switch (SSL_get_error(tls->ssl,0)) {
+        case SSL_ERROR_ZERO_RETURN      :       //No char available on link
+        case SSL_ERROR_SYSCALL          :       //EOF received?
+          tls->goteof=true;
+          if (ERR_get_error()!=0)
+            (void) rou_alert(0,"%s wrong EOF",OPEP);
+          break;
+        default                         :
+          (void) rou_alert(0,"%s Unexpected SSL_read error='%d'",
+                              OPEP,SSL_get_error(tls->ssl,0));
+          (void) showtlserror(tls,0,"show SSL error",OPEP);
+          got=-1;
+          break;
+        }
+      break;
+    default     :       //some character received
+      break;
+    }
+  }
+return got;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to wait for characeter to be available*/
+/*      on the tls link.                                */
+/*      Wait for at most mili-second time.              */
+/*                                                      */
+/********************************************************/
+PUBLIC int tls_waitforchar(TLSTYP *tls,u_int millisec)
+
+{
+#define        OPEP    "unitls.c:tls_waitforchar,"
+
+int status;
+struct pollfd polling[1];
+_Bool proceed;
+int phase;
+
+status=-1;
+polling[0].events=POLLIN|POLLPRI;
+polling[0].revents=(short)0;
+proceed=true;
+phase=0;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //check TLS
+      if ((tls==(TLSTYP *)0)||(tls->handle<0)) {
+        (void) rou_alert(0,"%s TLS is not available (Bug?)",OPEP);
+        phase=999;      //trouble trouble (Bug?)
+        }
+      break;
+    case 1      :       //lets assume we are cleartext mode
+      polling[0].fd=tls->handle;
+      if (tls->tls==true) {     //we are crypted mode
+        if (tls->ssl==(SSL *)0) { //Is ssl definined
+          (void) rou_alert(0,"%s Crypted mode but no SSL (Bug?)",OPEP);
+          phase=999;      //trouble trouble (Bug?)
+          }
+        else
+          polling[0].fd=SSL_get_fd(tls->ssl);
+        }
+      break;
+    case 2      :       //lets wait for char
+      status=poll(polling,1,millisec);
+      switch (status) {
+        case -1         :       //Polling error
+          (void) rou_alert(0,"%s Polling error (error=<%s>)",OPEP,strerror(errno));
+          break;
+        case  0         :       //polling timeout
+          switch (errno) {
+            default     :       //lets say nothind to do
+              (void) rou_alert(0,"%s Polling timeout (error='%d'-<%s>)",
+                                  OPEP,errno,strerror(errno));
+              break;
+            }
+          break;
+        case  1         :       //char is available.
+                                //nothing to do
+          break;
+        }
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to accept a TLS connection from a     */
+/*      remote client.                                  */
+/*                                                      */
+/********************************************************/
+PUBLIC int tls_accept(TLSTYP *tls)
+
+{
+#define        OPEP    "unitls.c:tls_accept,"
+
+int statut;
+X509 *peer;
+int tic;
+int done;
+
+statut=-1;
+peer=(X509 *)0;
+tic=30;         //30 second MAX to extablish SSL connexio
+done=false;
+if ((tls->bio=BIO_new_fd(tls->handle,BIO_NOCLOSE))==(BIO *)0) {
+  (void) rou_core_dump("%s Unable to get the BIO (error=<%s>)",
+                       OPEP,strerror(errno));
+  }
+(void) SSL_set_bio(tls->ssl,tls->bio,tls->bio);
+while (done==false) {
+  int sslerr;
+
+  statut=SSL_accept(tls->ssl);
+  switch (sslerr=SSL_get_error(tls->ssl,statut)) {
+    case SSL_ERROR_NONE             :
+      statut=0;
+      if ((peer=SSL_get_peer_certificate(tls->ssl))!=(X509 *)0) {
+        if (SSL_get_verify_result(tls->ssl)!=X509_V_OK)
+          statut=-1;
+        (void) X509_free(peer);
+        }
+      done=true;
+      break;
+    case SSL_ERROR_WANT_READ :
+      switch(tls_waitforchar(tls,(u_int)1000)) {
+        case -1                     :
+          switch(errno) {      /*received a signal, lets see...*/
+           case EINTR       :  /*could be a TERM signal        */
+             break;    
+           default          :  /*hummm real code fault report  */
+             (void) rou_core_dump("%s poll error '%s'",OPEP,strerror(errno));
+              done=true;
+             statut=-1;
+             break;
+           }
+         break;
+        case  0                     :  /*standard time out             */
+          tic--; 
+          if (tic<=0) {
+            done=true;
+            (void) rou_alert(0,"%s, SSL_accept too long to establish",OPEP);
+           statut=-1;
+           }
+          break;
+       default              :
+          break;
+        }
+      break;
+#ifdef SSL_ERROR_WANT_ACCEPT
+    case SSL_ERROR_WANT_ACCEPT :
+#endif
+    case SSL_ERROR_WANT_WRITE  :
+      break;
+    case SSL_ERROR_SYSCALL:    /*EOF received          */
+      if (ERR_get_error()==0)
+       tls->goteof=true;               
+      else
+        (void) showtlserror(tls,statut,"%s SSL_SYSCALL pbs",OPEP);
+      statut=-1;
+      done=true;
+      break;
+    default                            :
+      (void) showtlserror(tls,statut,"%s SSL_accept fatal error "
+                                    "(sslerr='%d')",OPEP,sslerr);
+      statut=-1;
+      done=true;
+      break; 
+    }
+  }
+return statut;
+#undef   OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedur to initiate a TLS connection from the  */
+/*      client side.                                   */
+/*     Return -1 if trouble, 0 otherwise               */
+/*                                                      */
+/********************************************************/
+PUBLIC int tls_connect(TLSTYP *tls)
+
+{
+#define OPEP   "unitls.c:tls_connect,"
+
+int done;
+int statut;
+int tic;
+
+done=false;
+statut=-1;
+tic=30;         //trying for 30 second
+if ((tls->bio=BIO_new_fd(tls->handle,BIO_NOCLOSE))==(BIO *)0) {
+  (void) rou_core_dump("%s Unable to get the BIO (error=<%s>)",
+                       OPEP,strerror(errno));
+  }
+(void) SSL_set_bio(tls->ssl,tls->bio,tls->bio);
+while (done==false) {
+  statut=SSL_connect(tls->ssl);
+  switch (SSL_get_error(tls->ssl,statut)) {
+    case SSL_ERROR_NONE             :
+      done=true;
+      statut=0;
+      break;
+    case SSL_ERROR_WANT_READ :
+      switch (tls_waitforchar(tls,(u_int)1000)) {
+        case -1                     :
+          switch(errno) {      /*received a signal, lets see...*/
+           case EINTR       :  /*could be a TERM signal        */
+             break;    
+           default          :  /*hummm real code fault report  */
+             (void) rou_core_dump("%s poll error '%s'",OPEP,strerror(errno));
+              done=true;
+             statut=-1;
+             break;
+           }
+         break;
+        case  0                     :  /*standard time out             */
+          tic--; 
+          if (tic<=0) {
+            done=true;
+            (void) rou_alert(0,"%s, SSL_connect too long to establish",OPEP);
+           statut=-1;
+           }
+          break;
+       default              :
+          break;
+        }
+      break;
+    case SSL_ERROR_WANT_WRITE  :
+    case SSL_ERROR_WANT_CONNECT        :
+      break;
+    default                            :
+      (void) showtlserror(tls,statut,"%s SSL_connect fatal error",OPEP);
+      statut=-1;
+      done=true;
+      break;
+    }
+  }
+return statut;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to check if remote certificat is a    */
+/*      valide one.                                     */
+/*      return -1if trouble, 0 otherwise                */
+/*                                                      */
+/********************************************************/
+PUBLIC int tls_check_peer(TLSTYP *tls)
+
+{
+#define OPEP "unitls.c:tls_check_peer"
+
+int status;
+X509 *peer;
+int phase;
+int proceed;
+
+status=-1;
+peer=(X509 *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //check SSL
+      if ((tls==(TLSTYP *)0)||(tls->ssl=(SSL *)0)) {
+        phase=999;      //trouble trouble (Bug?)
+        }
+      break;
+    case 1      :       //get peer
+      if ((peer=SSL_get_peer_certificate(tls->ssl))==(X509 *)0) {
+        (void) showtlserror(tls,0,"Get peer Certificate");
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 2      :       //is peer valid?
+      if (SSL_get_verify_result(tls->ssl)!=X509_V_OK) {
+        (void) showtlserror(tls,0,"Get Verify peer Certificate");
+        phase=999;      //trouble trouble
+        }
+      (void) X509_free(peer);
+      break;
+    case 3      :       //everything is right
+      status=0;
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return status;
+#undef  OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to link a socket reference to an      */
+/*      crypted (SSL) channel.                          */
+/*      return an SSL pointer or a NULL pointer         */
+/*                                                      */
+/********************************************************/
+PUBLIC SSL *tls_setsocket(int handle,SSL_CTX *ctx)
+
+{
+#define OPEP    "unitls.c:tls_setsocket"
+
+SSL *ssl;
+int phase;
+_Bool proceed;
+
+ssl=(SSL *)0;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //Setting inital context
+      if ((ssl=SSL_new(ctx))==(SSL *)0) {
+        char reason[200];
+
+        (void) ERR_error_string(ERR_get_error(),reason);
+        (void) rou_alert(0,"%S Unable to get a new SSL channel (error=<%s>)",
+                           OPEP,reason);
+        phase=999;      //trouble trouble
+        }
+      break;
+    case 1      :       //assign SSL to socket reference
+      if (SSL_set_fd(ssl,handle)==0) {
+        char reason[200];
+
+        (void) ERR_error_string(ERR_get_error(),reason);
+        (void) rou_alert(0,"%S Unable to set new SSL to socket (error=<%s>)",
+                           OPEP,reason);
+        (void) free(ssl);
+        ssl=(SSL *)0;
+        phase=999;
+        }
+      break;
+    default     :       //SAFE guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+return ssl;
+#undef OPEP
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to return the AFN list of binding data*/
+/*                                                      */
+/********************************************************/
+PUBLIC AFNTYP **tls_get_bind_afn()
+
+{
+return afns;
+}
+/*
+^L
+*/
+/********************************************************/
+/*                                                      */
+/*     Procedure to "open/close" module and do         */
+/*      homework purpose                                */
+/*      return zero if everything right                 */
+/*                                                      */
+/********************************************************/
+int tls_modeunitls(_Bool mode)
+
+{
+#define OPEP    "unitls.c:soc_modeunitls,"
+
+int status;
+
+status=0;
+if (mode!=modopen) {
+  (void) rou_modesubrou(mode);
+  switch ((int)mode) {
+    case true     :
+      (void) SSL_library_init();
+      (void) ERR_clear_error();
+      (void) buildafns();
+      break;
+    case false    :
+      afns=(AFNTYP **)rou_freelist((void **)afns,(genfree_t)afn_freeipnum);
+      break;
+    default       :
+      (void) fprintf(stderr,"Calling %s with wrong mode='%d' (Bug?!):",
+                            OPEP,(int)mode);
+      status=-1;
+      break;
+    }
+  modopen=mode;
+  }
+return status;
+#undef  OPEP
+}
diff --git a/lib/unitls.d b/lib/unitls.d
new file mode 100644 (file)
index 0000000..950f43a
--- /dev/null
@@ -0,0 +1,118 @@
+unitls.o unitls.d : unitls.c /usr/include/stdc-predef.h /usr/include/openssl/asn1.h \
+ /usr/include/openssl/macros.h /usr/include/openssl/opensslconf.h \
+ /usr/include/openssl/configuration.h /usr/include/openssl/opensslv.h \
+ /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
+ /usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
+ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stddef.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdarg.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/bits/time64.h /usr/include/bits/types/__fpos_t.h \
+ /usr/include/bits/types/__mbstate_t.h \
+ /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
+ /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
+ /usr/include/bits/types/cookie_io_functions_t.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/floatn.h \
+ /usr/include/bits/floatn-common.h /usr/include/time.h \
+ /usr/include/bits/time.h /usr/include/bits/types/clock_t.h \
+ /usr/include/bits/types/time_t.h /usr/include/bits/types/struct_tm.h \
+ /usr/include/bits/types/struct_timespec.h /usr/include/bits/endian.h \
+ /usr/include/bits/endianness.h /usr/include/bits/types/clockid_t.h \
+ /usr/include/bits/types/timer_t.h \
+ /usr/include/bits/types/struct_itimerspec.h \
+ /usr/include/bits/types/locale_t.h /usr/include/bits/types/__locale_t.h \
+ /usr/include/openssl/e_os2.h /usr/include/sys/types.h \
+ /usr/include/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
+ /usr/include/bits/types/struct_timeval.h \
+ /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
+ /usr/include/bits/pthreadtypes-arch.h \
+ /usr/include/bits/atomic_wide_counter.h /usr/include/bits/struct_mutex.h \
+ /usr/include/bits/struct_rwlock.h /usr/include/inttypes.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdint.h \
+ /usr/include/stdint.h /usr/include/bits/wchar.h \
+ /usr/include/bits/stdint-uintn.h /usr/include/bits/stdint-least.h \
+ /usr/include/openssl/bio.h /usr/include/openssl/crypto.h \
+ /usr/include/stdlib.h /usr/include/bits/waitflags.h \
+ /usr/include/bits/waitstatus.h /usr/include/alloca.h \
+ /usr/include/bits/stdlib-float.h /usr/include/openssl/safestack.h \
+ /usr/include/openssl/stack.h /usr/include/openssl/types.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/limits.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/syslimits.h \
+ /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/bits/pthread_stack_min-dynamic.h \
+ /usr/include/bits/pthread_stack_min.h /usr/include/bits/posix2_lim.h \
+ /usr/include/openssl/cryptoerr.h /usr/include/openssl/symhacks.h \
+ /usr/include/openssl/cryptoerr_legacy.h /usr/include/openssl/core.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include-fixed/pthread.h \
+ /usr/include/sched.h /usr/include/bits/sched.h \
+ /usr/include/bits/types/struct_sched_param.h /usr/include/bits/cpu-set.h \
+ /usr/include/bits/setjmp.h \
+ /usr/include/bits/types/struct___jmp_buf_tag.h \
+ /usr/include/openssl/bioerr.h /usr/include/openssl/asn1err.h \
+ /usr/include/openssl/bn.h /usr/include/openssl/bnerr.h \
+ /usr/include/openssl/err.h /usr/include/openssl/lhash.h \
+ /usr/include/errno.h /usr/include/bits/errno.h \
+ /usr/include/linux/errno.h /usr/include/asm/errno.h \
+ /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
+ /usr/include/openssl/x509.h /usr/include/openssl/buffer.h \
+ /usr/include/openssl/buffererr.h /usr/include/openssl/evp.h \
+ /usr/include/openssl/core_dispatch.h /usr/include/openssl/indicator.h \
+ /usr/include/openssl/params.h /usr/include/openssl/evperr.h \
+ /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h \
+ /usr/include/openssl/objectserr.h /usr/include/openssl/ec.h \
+ /usr/include/string.h /usr/include/strings.h \
+ /usr/include/openssl/ecerr.h /usr/include/openssl/rsa.h \
+ /usr/include/openssl/rsaerr.h /usr/include/openssl/dsa.h \
+ /usr/include/openssl/dh.h /usr/include/openssl/dherr.h \
+ /usr/include/openssl/dsaerr.h /usr/include/openssl/sha.h \
+ /usr/include/openssl/x509err.h /usr/include/openssl/x509_vfy.h \
+ /usr/include/openssl/pkcs7.h /usr/include/openssl/pkcs7err.h \
+ /usr/include/openssl/http.h /usr/include/openssl/conf.h \
+ /usr/include/openssl/conferr.h /usr/include/openssl/conftypes.h \
+ /usr/include/openssl/x509v3.h /usr/include/openssl/x509v3err.h \
+ /usr/include/sys/socket.h /usr/include/bits/types/struct_iovec.h \
+ /usr/include/bits/socket.h /usr/include/bits/socket_type.h \
+ /usr/include/bits/sockaddr.h /usr/include/asm/socket.h \
+ /usr/include/asm-generic/socket.h /usr/include/linux/posix_types.h \
+ /usr/include/linux/stddef.h /usr/include/asm/posix_types.h \
+ /usr/include/asm/posix_types_64.h /usr/include/asm-generic/posix_types.h \
+ /usr/include/asm/bitsperlong.h /usr/include/asm-generic/bitsperlong.h \
+ /usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
+ /usr/include/bits/types/struct_osockaddr.h /usr/include/ifaddrs.h \
+ /usr/include/netdb.h /usr/include/netinet/in.h /usr/include/bits/in.h \
+ /usr/include/rpc/netdb.h /usr/include/bits/netdb.h /usr/include/poll.h \
+ /usr/include/sys/poll.h /usr/include/bits/poll.h /usr/include/signal.h \
+ /usr/include/bits/signum-generic.h /usr/include/bits/signum-arch.h \
+ /usr/include/bits/types/sig_atomic_t.h \
+ /usr/include/bits/types/siginfo_t.h /usr/include/bits/types/__sigval_t.h \
+ /usr/include/bits/siginfo-arch.h /usr/include/bits/siginfo-consts.h \
+ /usr/include/bits/types/sigval_t.h /usr/include/bits/types/sigevent_t.h \
+ /usr/include/bits/sigevent-consts.h /usr/include/bits/sigaction.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/types/stack_t.h \
+ /usr/include/sys/ucontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigstksz.h /usr/include/bits/ss_flags.h \
+ /usr/include/bits/types/struct_sigstack.h /usr/include/bits/sigthread.h \
+ /usr/include/bits/signal_ext.h /usr/include/unistd.h \
+ /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+ /usr/include/bits/confname.h /usr/include/bits/getopt_posix.h \
+ /usr/include/bits/getopt_core.h /usr/include/bits/unistd_ext.h subrou.h \
+ /usr/include/linux/types.h /usr/include/asm/types.h \
+ /usr/include/asm-generic/types.h /usr/include/asm-generic/int-ll64.h \
+ /usr/lib/gcc/x86_64-ok-linux-gnu/13.4.0/include/stdbool.h unitls.h \
+ /usr/include/openssl/ssl.h /usr/include/openssl/e_ostime.h \
+ /usr/include/sys/time.h /usr/include/openssl/comp.h \
+ /usr/include/openssl/comperr.h /usr/include/openssl/pem.h \
+ /usr/include/openssl/pemerr.h /usr/include/openssl/hmac.h \
+ /usr/include/openssl/async.h /usr/include/openssl/asyncerr.h \
+ /usr/include/openssl/ct.h /usr/include/openssl/cterr.h \
+ /usr/include/openssl/sslerr.h /usr/include/openssl/sslerr_legacy.h \
+ /usr/include/openssl/prov_ssl.h /usr/include/openssl/ssl2.h \
+ /usr/include/openssl/ssl3.h /usr/include/openssl/tls1.h \
+ /usr/include/openssl/dtls1.h /usr/include/openssl/srtp.h \
+ /usr/include/openssl/quic.h subafn.h
diff --git a/lib/unitls.h b/lib/unitls.h
new file mode 100644 (file)
index 0000000..83a0460
--- /dev/null
@@ -0,0 +1,89 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Define all routine to manage transport secure   */
+/*      layer.                                          */
+/*                                                     */
+/********************************************************/
+#ifndef        UNIUTL
+#define UNIUTL
+
+#include        <stdbool.h>
+#include        <openssl/ssl.h>
+
+#include        "subafn.h"
+
+//defining email protocol value.
+typedef enum    {
+        pro_smtp,       //text SMTP protocol, in clear mode
+        pro_starttls,   //Text SMTP protocol, encrypted upon request
+        pro_smtps,      //Text SMTP protocol, text encrypted from start
+        pro_unknwn      //Protcole undefined
+        }PROTYP;
+
+typedef struct  {       //structure about listening port
+        PROTYP proto;   //Protocol to be used
+        char *port;     //binding port number
+        int iteration;  //Number of binding iteration
+        AFNTYP *afn;    //Information about the IP
+        }LISTYP;
+
+typedef struct  {
+        _Bool server;   //SSL server/client mode
+        _Bool checkpeer;//Check peer certificate
+        _Bool goteof;   //SSL End Of File
+        _Bool tls;      //link in TLS (crypted) mode
+        BIO *bio;       //SSL Basic IO
+        int handle;     //device handle
+        char *peerip;   //Remote IP number
+        char *peername; //Remote reverse address
+        char *locip;    //Local IP number
+        char *locport;  //local Port number
+        SSL_CTX *ctx;   //SSL context
+        SSL *ssl;       //SSL link
+        }TLSTYP;
+
+//procedure to report ll information about
+//the TLS channel
+extern char *tls_getcipherid(TLSTYP *tls);
+
+//procedure to verify certificate linked to TLS channel
+extern _Bool tls_verify(TLSTYP *tls);
+
+//procedure to get the security level
+extern int tls_get_sec_level(TLSTYP *tls);
+
+//procedure to open an tls channel
+extern TLSTYP *tls_opentls(int handle,_Bool server);
+
+//procedure to close an tls channel
+extern TLSTYP *tls_closetls(TLSTYP *tls);
+
+//write on the SSL channel
+extern int tls_write(TLSTYP *tls,char *buffer,int tosend);
+
+//read from the SSL channel
+extern int tls_read(TLSTYP *tls,char *buffer,int maxread);
+
+//wait for a char alvalable on channel
+extern int tls_waitforchar(TLSTYP *tls,u_int millisec);
+
+//accept from a TLS client connection
+extern int tls_accept(TLSTYP *tls);
+
+//connect to a TLS serveur connection
+extern int tls_connect(TLSTYP *tls);
+
+//check peer certificat
+extern int tls_check_peer(TLSTYP *tls);
+
+//set socket as an tls socket
+extern SSL *tls_setsocket(int handle,SSL_CTX *ctx);
+
+//getting binding information as AFN structure
+extern AFNTYP **tls_get_bind_afn();
+
+//homework to be done before starting/stopping module.
+extern int tls_modeunitls(_Bool mode);
+
+#endif
diff --git a/linux/osukiss/mailleur b/linux/osukiss/mailleur
new file mode 100644 (file)
index 0000000..d9e78bf
--- /dev/null
@@ -0,0 +1,63 @@
+#! /usr/bin/bash
+#=================================================================
+# Begin receiver
+#
+# Description : Start mailleur receiver daemon
+# chkconfig: 2345 94 12
+#
+#=================================================================
+
+### BEGIN INIT INFO
+# Provides:            $mailler
+# Required-Start:      $network $dovecot $database
+# Should-Start:
+# Required-Stop:       sendsignals
+# Should-Stop:
+# Default-Start:       3 4 5
+# Default-Stop:        0 1 2 6
+# Short-Description:   Starts mailleur reciever and sorter  daemon.
+# Description:         Starts mailleur needed daemon.
+# X-LFS-Provided-By:   LFS
+### END INIT INFO
+
+. /lib/lsb/init-functions
+
+APPNAME=mailleur
+DAEMON=receiver
+
+#checking profile
+if [ -f  /etc/profile ] ; then
+  . /etc/profile
+  fi
+
+#--------------------------------------------------------------
+#to start all mailleur daemon
+#--------------------------------------------------------------
+case "$1" in
+    start              )
+    for serv in receiver sorter scanner sender
+      do
+      /usr/share/$APPNAME/linux/osukiss/$serv $1
+      done
+     ;;
+    condrestart                |                       \
+    reload             |                       \
+    stop               )
+    for serv in scanner sorter sender receiver
+      do
+      /usr/share/$APPNAME/linux/osukiss/$serv $1
+      done
+     ;;
+    restart            )
+     $0 stop
+     sleep 1
+     $0 start
+     ;;
+    *)
+        echo "Usage: $0 {start|stop|reload|condrestart|restart}"
+        exit 1
+        ;;
+esac
+
+
+
diff --git a/linux/osukiss/receiver b/linux/osukiss/receiver
new file mode 100644 (file)
index 0000000..b908605
--- /dev/null
@@ -0,0 +1,106 @@
+#!/bin/sh
+#=================================================================
+# Begin receiver
+#
+# Description : Start mailleur receiver daemon
+# chkconfig: 2345 94 12
+#
+#=================================================================
+
+### BEGIN INIT INFO
+# Provides:            $receiver
+# Required-Start:      $sorter
+# Should-Start:
+# Required-Stop:       sendsignals
+# Should-Stop:
+# Default-Start:       3 4 5
+# Default-Stop:        0 1 2 6
+# Short-Description:   Starts reciever daemon.
+# Description:         Starts reciever daemon.
+# X-LFS-Provided-By:   LFS
+### END INIT INFO
+
+. /lib/lsb/init-functions
+
+APPNAME=mailleur
+DAEMON=receiver
+DAEMON_PID=/run/${APPNAME}/${DAEMON}.lock
+
+# Some functions to make the below more readable
+
+#loading load configuration file
+if [ -f  /etc/sysconfig/$APPNAME ] ; then
+  . /etc/sysconfig/$APPNAME
+fi
+
+#--------------------------------------------------------------
+#to start receiver daemon
+#--------------------------------------------------------------
+mng_daemon ()
+
+{
+case "$1" in
+  start)
+    mkdir -p /run/$APPNAME/
+    chown $APPNAME:mail /run/$APPNAME/
+    /usr/lib/$APPNAME/support/starting.sh osukiss
+    if [ $? != 0 ] ; then
+      exit 1;
+      fi
+    log_info_msg "Starting ${DAEMON} Server..."
+    start_daemon -f /usr/libexec/$APPNAME/bin/${DAEMON} $OPTIONS
+    evaluate_retval
+    ;;
+
+  stop)
+    log_info_msg "Stopping ${DAEMON} Server..."
+    killproc -p ${DAEMON_PID} /usr/libexec/$APPNAME/bin/${DAEMON}
+    evaluate_retval
+    rm -fr ${DAEMON_PID}
+    ;;
+
+ esac
+}
+
+case "$1" in
+      start    \
+    | stop     \
+    )
+       mng_daemon $1
+        ;;
+
+    reload)
+        log_info_msg "Reloading ${DAEMON} Server..."
+        pid=`cat ${DAEMON_PID} 2>/dev/null`
+
+        if [ -n "${pid}" ]; then
+           kill -HUP "${pid}"
+        else
+           (exit 1)
+        fi
+
+        evaluate_retval
+        ;;
+
+    restart)
+        $0 stop
+        sleep 1
+        $0 start
+        ;;
+
+    condrestart)
+        pid=`cat ${DAEMON_PID} 2>/dev/null`
+        if [ -n "${pid}"  -a -d /proc/$pid ]; then
+         $0 restart
+         fi
+        ;;
+
+
+    *)
+        echo "Usage: $0 {start|stop|reload|restart}"
+        exit 1
+        ;;
+esac
+
+
+
diff --git a/linux/osukiss/scanner b/linux/osukiss/scanner
new file mode 100644 (file)
index 0000000..d24d907
--- /dev/null
@@ -0,0 +1,106 @@
+#!/bin/sh
+#=================================================================
+# Begin sorter
+#
+# Description : Start mailleur sorter daemon
+# chkconfig: 2345 91 17
+#
+#=================================================================
+
+### BEGIN INIT INFO
+# Provides:            $scanner
+# Required-Start:      $
+# Should-Start:
+# Required-Stop:       sendsignals
+# Should-Stop:
+# Default-Start:       3 4 5
+# Default-Stop:        0 1 2 6
+# Short-Description:   Starts sorter daemon.
+# Description:         Starts sorter daemon.
+# X-LFS-Provided-By:   LFS
+### END INIT INFO
+
+. /lib/lsb/init-functions
+
+APPNAME=mailleur
+DAEMON=scanner
+DAEMON_PID=/run/${APPNAME}/${DAEMON}.lock
+
+# Some functions to make the below more readable
+
+#loading load configuration file
+if [ -f  /etc/sysconfig/$APPNAME ] ; then
+  . /etc/sysconfig/$APPNAME
+fi
+
+#--------------------------------------------------------------
+#to start sorter daemon
+#--------------------------------------------------------------
+mng_daemon ()
+
+{
+case "$1" in
+  start)
+    mkdir -p /run/$APPNAME/
+    chown $APPNAME:mail /run/$APPNAME/
+    /usr/lib/$APPNAME/support/starting.sh osukiss
+    if [ $? != 0 ] ; then
+      exit 1;
+      fi
+    log_info_msg "Starting ${DAEMON} Server..."
+    start_daemon -f /usr/libexec/$APPNAME/bin/${DAEMON} $OPTIONS
+    evaluate_retval
+    ;;
+
+  stop)
+    log_info_msg "Stopping ${DAEMON} Server..."
+    killproc -p ${DAEMON_PID} /usr/libexec/$APPNAME/bin/${DAEMON}
+    evaluate_retval
+    rm -fr ${DAEMON_PID}
+    ;;
+
+ esac
+}
+
+case "$1" in
+      start    \
+    | stop     \
+    )
+       mng_daemon $1
+        ;;
+
+    reload)
+        log_info_msg "Reloading ${DAEMON} Server..."
+        pid=`cat ${DAEMON_PID} 2>/dev/null`
+
+        if [ -n "${pid}" ]; then
+           kill -HUP "${pid}"
+        else
+           (exit 1)
+        fi
+
+        evaluate_retval
+        ;;
+
+    restart)
+        $0 stop
+        sleep 1
+        $0 start
+        ;;
+
+    condrestart)
+        pid=`cat ${DAEMON_PID} 2>/dev/null`
+        if [ -n "${pid}"  -a -d /proc/$pid ]; then
+         $0 restart
+         fi
+        ;;
+
+
+    *)
+        echo "Usage: $0 {start|stop|reload|restart|status}"
+        exit 1
+        ;;
+esac
+
+
+
diff --git a/linux/osukiss/sender b/linux/osukiss/sender
new file mode 100644 (file)
index 0000000..28b6401
--- /dev/null
@@ -0,0 +1,105 @@
+#!/bin/sh
+#=================================================================
+# Begin receiver
+#
+# Description : Start mailleur sender daemon
+# chkconfig: 2345 94 12
+#
+#=================================================================
+
+### BEGIN INIT INFO
+# Provides:            $sender
+# Should-Start:
+# Required-Stop:       sendsignals
+# Should-Stop:
+# Default-Start:       3 4 5
+# Default-Stop:        0 1 2 6
+# Short-Description:   Starts sender daemon.
+# Description:         Starts sender daemon.
+# X-LFS-Provided-By:   LFS
+### END INIT INFO
+
+. /lib/lsb/init-functions
+
+APPNAME=mailleur
+DAEMON=sender
+DAEMON_PID=/run/${APPNAME}/${DAEMON}.lock
+
+# Some functions to make the below more readable
+
+#loading load configuration file
+if [ -f  /etc/sysconfig/$APPNAME ] ; then
+  . /etc/sysconfig/$APPNAME
+fi
+
+#--------------------------------------------------------------
+#to start receiver daemon
+#--------------------------------------------------------------
+mng_daemon ()
+
+{
+case "$1" in
+  start)
+    mkdir -p /run/$APPNAME/
+    chown $APPNAME:mail /run/$APPNAME/
+    /usr/lib/$APPNAME/support/starting.sh osukiss
+    if [ $? != 0 ] ; then
+      exit 1;
+      fi
+    log_info_msg "Starting ${DAEMON} Server..."
+    start_daemon -f /usr/libexec/$APPNAME/bin/${DAEMON} $OPTIONS
+    evaluate_retval
+    ;;
+
+  stop)
+    log_info_msg "Stopping ${DAEMON} Server..."
+    killproc -p ${DAEMON_PID} /usr/libexec/$APPNAME/bin/${DAEMON}
+    evaluate_retval
+    rm -fr ${DAEMON_PID}
+    ;;
+
+ esac
+}
+
+case "$1" in
+      start    \
+    | stop     \
+    )
+       mng_daemon $1
+        ;;
+
+    reload)
+        log_info_msg "Reloading ${DAEMON} Server..."
+        pid=`cat ${DAEMON_PID} 2>/dev/null`
+
+        if [ -n "${pid}" ]; then
+           kill -HUP "${pid}"
+        else
+           (exit 1)
+        fi
+
+        evaluate_retval
+        ;;
+
+    restart)
+        $0 stop
+        sleep 1
+        $0 start
+        ;;
+
+    condrestart)
+        pid=`cat ${DAEMON_PID} 2>/dev/null`
+        if [ -n "${pid}"  -a -d /proc/$pid ]; then
+         $0 restart
+         fi
+        ;;
+
+
+    *)
+        echo "Usage: $0 {start|stop|reload|restart}"
+        exit 1
+        ;;
+esac
+
+
+
diff --git a/linux/osukiss/sorter b/linux/osukiss/sorter
new file mode 100644 (file)
index 0000000..30ceca9
--- /dev/null
@@ -0,0 +1,106 @@
+#!/bin/sh
+#=================================================================
+# Begin sorter
+#
+# Description : Start mailleur sorter daemon
+# chkconfig: 2345 91 17
+#
+#=================================================================
+
+### BEGIN INIT INFO
+# Provides:            $sorter
+# Required-Start:      $
+# Should-Start:
+# Required-Stop:       sendsignals
+# Should-Stop:
+# Default-Start:       3 4 5
+# Default-Stop:        0 1 2 6
+# Short-Description:   Starts sorter daemon.
+# Description:         Starts sorter daemon.
+# X-LFS-Provided-By:   LFS
+### END INIT INFO
+
+. /lib/lsb/init-functions
+
+APPNAME=mailleur
+DAEMON=sorter
+DAEMON_PID=/run/${APPNAME}/${DAEMON}.lock
+
+# Some functions to make the below more readable
+
+#loading load configuration file
+if [ -f  /etc/sysconfig/$APPNAME ] ; then
+  . /etc/sysconfig/$APPNAME
+fi
+
+#--------------------------------------------------------------
+#to start sorter daemon
+#--------------------------------------------------------------
+mng_daemon ()
+
+{
+case "$1" in
+  start)
+    mkdir -p /run/$APPNAME/
+    chown $APPNAME:mail /run/$APPNAME/
+    /usr/lib/$APPNAME/support/starting.sh osukiss
+    if [ $? != 0 ] ; then
+      exit 1;
+      fi
+    log_info_msg "Starting ${DAEMON} Server..."
+    start_daemon -f /usr/libexec/$APPNAME/bin/${DAEMON} $OPTIONS
+    evaluate_retval
+    ;;
+
+  stop)
+    log_info_msg "Stopping ${DAEMON} Server..."
+    killproc -p ${DAEMON_PID} /usr/libexec/$APPNAME/bin/${DAEMON}
+    evaluate_retval
+    rm -fr ${DAEMON_PID}
+    ;;
+
+ esac
+}
+
+case "$1" in
+      start    \
+    | stop     \
+    )
+       mng_daemon $1
+        ;;
+
+    reload)
+        log_info_msg "Reloading ${DAEMON} Server..."
+        pid=`cat ${DAEMON_PID} 2>/dev/null`
+
+        if [ -n "${pid}" ]; then
+           kill -HUP "${pid}"
+        else
+           (exit 1)
+        fi
+
+        evaluate_retval
+        ;;
+
+    restart)
+        $0 stop
+        sleep 1
+        $0 start
+        ;;
+
+    condrestart)
+        pid=`cat ${DAEMON_PID} 2>/dev/null`
+        if [ -n "${pid}"  -a -d /proc/$pid ]; then
+         $0 restart
+         fi
+        ;;
+
+
+    *)
+        echo "Usage: $0 {start|stop|reload|restart|status}"
+        exit 1
+        ;;
+esac
+
+
+
diff --git a/mailleur.spec.in b/mailleur.spec.in
new file mode 100644 (file)
index 0000000..6007825
--- /dev/null
@@ -0,0 +1,406 @@
+#-----------------------------------------------------------------------------
+%{?!dist:       %define dist    @@DIST@@}
+%define         spooldir        %{_localstatedir}/spool/
+%define         wwwdir         %{_localstatedir}/www/
+%define                dovedir         %{_sysconfdir}/dovecot/conf.d
+#-----------------------------------------------------------------------------
+Name           :       @@APPN@@
+Version                :       @@VERSION@@
+Release                :       @@RELEASE@@%{?dist}
+Summary                :       Application to filter and manage E-mail traffic
+Group          :       System Environment/Libraries
+
+Vendor         :       SAFE Inc.
+License                :       GPLv2
+URL            :       http://mailleur.safe.ca/
+
+Source0                :       %{name}-@@VERSION@@.tar.gz
+#-----------------------------------------------------------------------------
+BuildRequires  :       libxcrypt-devel
+BuildRequires  :       mariadb
+BuildRequires  :       postgresql
+BuildRequires  :       postgresql-server-devel
+
+Requires       :       bash
+Requires       :       bind-utils
+Requires       :       cpp
+Requires       :       dovecot         >  2.4.0
+Requires       :       httpd
+Requires       :       openssl
+Requires       :       php-apache
+Requires       :       php-cli
+Requires       :       php-dbg
+Requires       :       python3-certbot_apache
+Requires       :       sed
+
+#-----------------------------------------------------------------------------
+%description
+'%{name}' is an email firewall. Its purpose is to filter all email,
+rejecting viruses and unwelcome messages at the SMTP protocol level
+(and avoids bouncing to forged originators).
+It also provide extended audit logs, allowing effective mean to
+search about email exchange within time and transaction context.
+
+#-----------------------------------------------------------------------------
+%files                 
+%defattr(-,root,root,-)
+%attr(0754,root,root) %{_datadir}/%{name}/linux/osukiss/%{name}
+%attr(0754,root,root) %{_datadir}/%{name}/linux/osukiss/receiver
+%attr(0754,root,root) %{_datadir}/%{name}/linux/osukiss/scanner
+%attr(0754,root,root) %{_datadir}/%{name}/linux/osukiss/sender
+%attr(0754,root,root) %{_datadir}/%{name}/linux/osukiss/sorter
+%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/cron.d/%{name}.cron
+%attr(0644,%{name},mail) %config(noreplace) %{_sysconfdir}/pki/%{name}/*
+%attr(0644,%{name},mail) %config(noreplace) %{_sysconfdir}/sysconfig/%{name}
+#configuration directory
+%attr(0644,%{name},mail) %config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf
+%attr(0644,%{name},mail) %config(noreplace) %{_sysconfdir}/%{name}/blacklister.conf
+%attr(0644,%{name},mail) %config(noreplace) %{_sysconfdir}/%{name}/relayed.conf
+#dovecot SQL database access
+%attr(0644,%{name},mail) %config(noreplace) %{dovedir}/80-%{name}-usesql.conf
+#
+%{_bindir}/chkspf
+%{_libexecdir}/%{name}/bin-utils/chkspf
+%{_libexecdir}/%{name}/bin-utils/feeder
+%{_libdir}/%{name}/rpm_build_date
+%attr(0754,root,root) %{_libdir}/%{name}/shell/*.sh
+%attr(0754,root,root) %{_libdir}/%{name}/support/addconfig.sh
+%attr(0754,root,root) %{_libdir}/%{name}/support/do_database.sh
+%attr(0754,root,root) %{_libdir}/%{name}/support/do_httpd.sh
+%attr(0754,root,root) %{_libdir}/%{name}/support/dummy-cert.sh
+%attr(0754,root,root) %{_libdir}/%{name}/support/starting.sh
+%attr(0754,root,root) %config(noreplace) %{_libdir}/%{name}/support/do_dns_tlsa.sh
+%attr(0750,%{name},mail) %{_libdir}/%{name}/support/mailleur-reset-db.sh
+%attr(0750,%{name},mail) %{_libdir}/%{name}/support/mailleur-settest-db.sh
+%attr(0755,%{name},mail) %dir %{spooldir}/%{name}/
+%attr(0755,%{name},mail) %dir %{spooldir}/%{name}/queue
+%attr(0750,%{name},mail) %dir %{spooldir}/%{name}/mails
+%attr(0750,%{name},apache) %dir %{wwwdir}/%{name}/
+%{wwwdir}/%{name}/index.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/devsql.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/gesdis.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/gessql.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/lvllog.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/lvlmai.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/lvlrmt.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/lvlusr.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/release.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/subrou.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/unienv.php
+%attr(0750,%{name},apache) %{wwwdir}/%{name}/unilng.php
+%attr(0640,%{name},apache) %{wwwdir}/%{name}/reg-icons/*.gif
+%attr(0640,%{name},apache) %{wwwdir}/%{name}/reg-icons/*.png
+%attr(0644,root,root) %{_datadir}/%{name}/sql/%{name}.sql
+%attr(0644,root,root) %{_datadir}/%{name}/sql/preset.sql
+%exclude %{_datadir}/%{name}/sql/%{name}.mysql
+%exclude %{_datadir}/%{name}/sql/%{name}.postg
+#-----------------------------------------------------------------------------
+
+%preun
+OS=`%{_libdir}/%{name}/shell/getsysos.sh`
+if [ "$1" = 0 ]; then
+  case "$OS" in
+    "sysv"     |                               \
+    "osukiss"  )
+      for action in %{name} receiver scanner sender sorter 
+       do
+        %{_initrddir}/${action} stop > /dev/null 2>&1 || :
+        %{_sbindir}/chkconfig --del ${action} 
+        #removing remaining init synlink
+        %{__rm} %{_initrddir}/${action}
+       done
+      ;;
+    *          )
+      echo "Unable to find distribution"
+      exit -1
+      ;;
+    esac
+  %{_sbindir}/userdel %{name}
+  (
+  /bin/echo -n "%{name}-%{version} has been fully removed on "; date
+  /bin/echo "remaining file in this directory are data-base and logs"
+  /bin/echo "created while %{name} was in production."
+  /bin/echo "these files _may_ be removed too, but that they could"
+  /bin/echo "contain valuable data and admin should be careful"
+  /bin/echo "when removing them."
+  ) > %{_var}/log/README-%{name}-removed
+  fi
+
+%pre
+if [ ! "$(getent passwd %{name})" ]; then
+  %{_sbindir}/useradd                          \
+        -r                                     \
+        -c "%{name} daemon"                    \
+        -K SYS_UID_MIN=50                      \
+        -K SYS_UID_MAX=99                      \
+        -g mail                                        \
+        -s /usr/bin/bash                       \
+        -d %{_var}/spool/%{name}               \
+        %{name}
+  fi
+
+%post
+OS=`%{_libdir}/%{name}/shell/getsysos.sh`
+ln -nsf                                                        \
+       ./lvllog.php                                    \
+       %{_var}/www/%{name}/index.php                   \
+
+if [ "$1" = 1 ]; then
+  (
+  echo "#------------------------------------------"
+  echo "#Architecture type was set by rpm first install procedure"
+  echo "#"`date`
+  echo "AP_ARCH=%{_arch}"
+  echo "#------------------------------------------"
+  ) >> %{_sysconfdir}/%{name}/install
+  #installing init file according OS
+  case "$OS" in
+    "sysv"     |                                       \
+    "osukiss"  )
+      for action in %{name} receiver scanner sender sorter 
+       do
+        %{__ln_s}                                      \
+               %{_datadir}/%{name}/linux/$OS/${action} \
+               %{_initrddir}/${action}
+       done
+      %{_sbindir}/chkconfig --add %{name}
+      %{_sysconfdir}/rc.d/init.d/%{name} condrestart > /dev/null 2>&1 || :
+      ;;
+    *          )
+      echo "Unable to find distribution"
+      exit -1
+      ;;
+    esac
+  fi
+
+%posttrans
+OS=`%{_libdir}/%{name}/shell/getsysos.sh`
+case "$OS" in
+  "sysv"       |                               \
+  "osukiss"    )
+    %{_sysconfdir}/rc.d/init.d/dovecot restart > /dev/null 2>&1 || :
+    ;;
+  *            )
+    ;;
+  esac
+
+#=============================================================================
+%package               devel
+Summary                :       tools and components to test '%{name}'
+
+#-----------------------------------------------------------------------------
+
+Requires       :       %{name}                 =  %{version}-%{release}
+Requires       :       gdb
+
+#-----------------------------------------------------------------------------
+%description           devel
+'%{name}' tools and data set to improve/debug '%{name}'
+
+#-----------------------------------------------------------------------------
+%files                 devel
+%defattr(-,root,root,-)
+%attr(0750,root,root) %{_libdir}/%{name}/tools/genpsdusr.sh
+%attr(0640,root,root) %{_datadir}/%{name}/sql/datatest.sql
+%attr(0640,root,root) %{_datadir}/%{name}/sql/Makefile
+%attr(0644,root,root) %{_datadir}/%{name}/sql/datatest.def
+
+#=============================================================================
+%package               pgsql
+Summary                :       mailleur using postgresql as Data-base
+
+#-----------------------------------------------------------------------------
+
+Requires       :       %{name}                 =  %{version}-%{release}
+Requires       :       php-pgsql
+Requires       :       postgresql-server
+Requires       :       postgresql-contrib
+Requires       :       dovecot-postgresql
+
+Obsoletes      :       %{name}-mysql           <= %{version}-%{release}
+#-----------------------------------------------------------------------------
+%description           pgsql
+'%{name}' is configured to work with POSTGRESQL type database
+
+#-----------------------------------------------------------------------------
+%files                 pgsql
+%defattr(-,root,root,-)
+#dovecot SQL database access
+%attr(0644,%{name},mail) %config(noreplace) %{dovedir}/70-%{name}-pgsql.conf
+#library to access postgres
+%attr(0755,%{name},mail) %{_libexecdir}/%{name}/bin-posql/*
+
+#-----------------------------------------------------------------------------
+%post                  pgsql
+OS=`%{_libdir}/%{name}/shell/getsysos.sh`
+if [ "$1" = 1 ]; then
+  sed -i                                       \
+       -e "s/^DB_TYPE=.*$/DB_TYPE=POSTGRESQL/" \
+       -e "s/^DB_PORT=.*$/DB_PORT=5432/"       \
+       %{_sysconfdir}/%{name}/%{name}.conf
+  fi
+
+#generating postgresql receiver and sender
+#generating exec to be path of the /usr/sbin path
+for exe in                                     \
+       receiver                                \
+       scanner                                 \
+       sender                                  \
+       sorter
+  do
+  %{__ln_s} -nsf                               \
+       %{_libexecdir}/%{name}/bin-posql/$exe   \
+       %{_sbindir}
+  done
+
+case "$OS" in
+  "sysv"       |                                       \
+  "osukiss"    )
+    for action in %{name}
+      do
+      %{_sbindir}/chkconfig --add ${action}
+      %{_sysconfdir}/rc.d/init.d/${action} condrestart > /dev/null 2>&1 || :
+      done
+    ;;
+  *            )
+    echo "Unable to find distribution"
+    exit -1
+    ;;
+  esac
+
+#adjusting libexec bin directory
+%{__ln_s} -nsf  ./bin-posql %{_libexecdir}/%{name}/bin
+
+%postun                        pgsql
+if [ "$1" = 0 ]; then
+  rm -f %{_libexecdir}/%{name}/bin
+  rm -f %{_sbindir}/receiver
+  rm -f %{_sbindir}/scanner
+  rm -f %{_sbindir}/sender
+  rm -f %{_sbindir}/sorter
+  fi
+
+%posttrans             pgsql
+if [ "$1" = 1 ]; then
+  OS=`%{_libdir}/%{name}/shell/getsysos.sh`
+  case "$OS" in
+    "sysv"     |                               \
+    "osukiss"  )
+      %{_sysconfdir}/rc.d/init.d/postgresql restart > /dev/null 2>&1 || :
+      ;;
+    *          )
+      ;;
+    esac
+  fi
+
+#=============================================================================
+%package               mysql
+Summary                :       mailleur using mysql/mariadb as Data-base
+
+#-----------------------------------------------------------------------------
+
+Requires       :       %{name}                 =  %{version}-%{release}
+Requires       :       mariadb
+Requires       :       php-mysqlnd
+Requires       :       dovecot-mysql
+
+Obsoletes      :       %{name}-postgresql      <= %{version}-%{release}
+#-----------------------------------------------------------------------------
+%description           mysql
+'%{name}' is configured to work with MYSQL/MARIADB type database
+
+#-----------------------------------------------------------------------------
+%files                 mysql
+%defattr(-,root,root,-)
+#dovecot SQL database access
+%attr(0644,%{name},mail) %config(noreplace) %{dovedir}/70-%{name}-mysql.conf
+#library to access mariadb
+%attr(0755,%{name},mail) %{_libexecdir}/%{name}/bin-mysql/*
+
+#-----------------------------------------------------------------------------
+
+%post                  mysql
+OS=`%{_libdir}/%{name}/shell/getsysos.sh`
+if [ "$1" = 1 ]; then
+  sed -i                                       \
+       -e "s/^DB_TYPE=.*$/DB_TYPE=MYSQL/"      \
+       -e "s/^DB_PORT=.*$/DB_PORT=3306/"       \
+       %{_sysconfdir}/%{name}/%{name}.conf
+  fi
+#generating exec to be path of the /usr/sbin path
+for exe in                                     \
+       receiver                                \
+       scanner                                 \
+       sender                                  \
+       sorter
+  do
+  %{__ln_s} -nsf                               \
+       %{_libexecdir}/%{name}/bin-mysql/$exe   \
+       %{_sbindir}/
+  done
+
+case "$OS" in
+  "sysv"       |                               \
+  "osukiss"    )
+    for action in %{name}
+      do
+      %{_sbindir}/chkconfig --add ${action}
+      %{_sysconfdir}/rc.d/init.d/${action} condrestart > /dev/null 2>&1 || :
+      done
+    ;;
+  *            )
+    echo "Unable to find distribution"
+    exit -1
+    ;;
+  esac
+
+#adjusting libexec bin directory
+%{__ln_s}  -nsf ./bin-mysql %{_libexecdir}/%{name}/bin
+
+%postun                        mysql
+if [ "$1" = 0 ]; then
+  rm -f %{_libexecdir}/%{name}/bin
+  rm -f %{_sbindir}/receiver
+  rm -f %{_sbindir}/scanner
+  rm -f %{_sbindir}/sender
+  rm -f %{_sbindir}/sorter
+  fi
+
+%posttrans     mysql
+if [ "$1" = 1 ]; then
+  OS=`%{_libdir}/%{name}/shell/getsysos.sh`
+  case "$OS" in
+    "sysv"     |                               \
+    "osukiss"  )
+      %{_sysconfdir}/rc.d/init.d/mysqld restart > /dev/null 2>&1 || :
+      ;;
+    *          )
+      ;;
+    esac
+  fi
+
+#=============================================================================
+%prep
+%setup -q
+#-----------------------------------------------------------------------------
+#building application
+%build
+
+%{__make}
+
+#-----------------------------------------------------------------------------
+%clean
+%{__rm} -rf %{buildroot}
+
+#-----------------------------------------------------------------------------
+#installing program
+%install
+
+%{__make}                              \
+        DESTDIR="%{buildroot}"         \
+        install
+
+date   > %{buildroot}%{_libdir}/%{name}/rpm_build_date
+
+#=============================================================================
+%changelog
diff --git a/shell/getsysos.sh b/shell/getsysos.sh
new file mode 100644 (file)
index 0000000..17c9f2a
--- /dev/null
@@ -0,0 +1,25 @@
+#! /usr/bin/bash
+#---------------------------------------------------
+#shell script to get the OS used to run clement
+
+DIST=`                                         \
+      cat /etc/os-release              |       \
+       grep "^ID="                     |       \
+       sed -e 's:"::g'                 |       \
+       cut -d '=' -f2                  |       \
+       tr '[:upper:]' '[:lower:]'              \
+       `
+
+case "$DIST" in
+  "osukiss"    )       #Osukiss distribution
+    OS="osukiss"
+    ;;
+  "rocky"      |                               \
+  "fedora"     )
+    OS="systemd"       #systemd distribution
+    ;;
+  *            )       #all other are system V
+    OS="sysv"
+    ;;
+  esac
+echo $OS
diff --git a/shell/test-spf.sh b/shell/test-spf.sh
new file mode 100755 (executable)
index 0000000..bda07d6
--- /dev/null
@@ -0,0 +1,35 @@
+#! /usr/bin/bash
+#---------------------------------------------------------------------
+#shell script to test SPF directive
+#---------------------------------------------------------------------
+numfile=0;
+numerr=0;
+while [ $# -gt 0 ] 
+  do
+  filename=$1
+  numfile=$((numfile+1))
+  numline=0
+  echo "scanning \"$1\" test file"
+  while read line
+    do
+    numline=$((numline+1))
+    data=`echo $line | grep -o '^[^#]*'`
+    if [ -z "$data" ] ; then
+      continue;
+      fi
+    expect=`echo $data | cut -d ' ' -f1`
+    domain=`echo $data | cut -d ' ' -f2`
+    ipnum=`echo $data | cut -d ' ' -f3`
+    result=`bin-utils/chkspf $domain $ipnum`
+    echo -n "done bin-utils/chkspf $domain $ipnum "
+    if [ "$result" != "$expect" ] ; then
+      echo "Fail!"
+      numerr=$((numerr+1))
+      echo -e "\tError=$numerr\tline $numline, Expected:=$expect Result:=$result"
+    else 
+      echo "Success"
+      fi
+    done < $1
+  shift
+  done
+echo "number of test file scanned: $numfile, total error reported: $numerr";
diff --git a/sql/.gitignore b/sql/.gitignore
new file mode 100644 (file)
index 0000000..79d5f73
--- /dev/null
@@ -0,0 +1 @@
+popul.sql
diff --git a/sql/Makefile b/sql/Makefile
new file mode 100644 (file)
index 0000000..86d3b8f
--- /dev/null
@@ -0,0 +1,100 @@
+#--------------------------------------------------------------------
+#Generate database for postgresql and mysql
+#--------------------------------------------------------------------
+prod                                           \
+debug                                          \
+       :  mailleur.postg mailleur.mysql
+
+allclean                                       \
+clean  :  
+          @ rm -fr mailleur.mysql mailleur.postg
+
+#--------------------------------------------------------------------
+
+prepdb :  newpop debug deltest newtest
+          
+mailleur.postg                                 \
+       :  mailleur.sql
+          @ cpp -P -DPOSTGRESQL                \
+               mailleur.sql > mailleur.postg
+
+mailleur.mysql                                 \
+       :  mailleur.sql
+          @ cpp -P -DMYSQL                     \
+               mailleur.sql > mailleur.mysql
+
+dbmysql        :  mailleur.mysql
+          @ - (                                        \
+            echo "CREATE USER `id -un`@localhost;";    \
+            echo "CREATE USER apache@localhost;";      \
+            echo "CREATE USER dovecot@localhost;";     \
+            echo "GRANT ALL ON mailleur.*              \
+                       TO `id -un`@localhost           \
+                       WITH GRANT OPTION;";            \
+            echo "FLUSH PRIVILEGES;";                  \
+            echo "CREATE DATABASE mailleur;";          \
+            echo "ALTER DATABASE mailleur              \
+                        CHARACTER SET utf8;";          \
+            ) | sudo mariadb -q
+          @ cat mailleur.mysql | mariadb -q mailleur;
+          @ cpp -P -DMYSQL                             \
+               datatest.sql    | mariadb -q mailleur;
+            cpp -P -traditional -DMYSQL                \
+               popul.sql       | mariadb -q mailleur;
+          @ echo "MYSQL database ready"
+
+
+ndbmysql:
+          @-(                                          \
+            echo "DROP DATABASE mailleur;";            \
+            echo "DROP USER dovecot@localhost;";       \
+            echo "DROP USER apache@localhost;";        \
+            echo "DROP USER `id -un`@localhost;";      \
+            ) | sudo mariadb 
+
+dbpostg        :  mailleur.postg
+          @ -(                                         \
+            echo "CREATE ROLE apache WITH LOGIN;";     \
+            echo "CREATE ROLE dovecot WITH LOGIN;";    \
+            echo "CREATE EXTENSION pgcrypto;";         \
+            echo "CREATE DATABASE mailleur ";          \
+            echo "              ENCODING='UTF8';";     \
+            ) | psql -q -U postgres template1
+          @ cat mailleur.postg | psql -q mailleur;
+          @ cpp -DPOSTGRESQL                           \
+               -P                                      \
+               -traditional                            \
+               datatest.sql                    |       \
+                       sed -e"/^\/\//d"        |       \
+                       psql -q mailleur;
+          @ cpp -DPOSTGRESQL                           \
+               -P                                      \
+               -traditional                            \
+               popul.sql                       |       \
+                       sed -e"/^\/\//d"        |       \
+                       psql -q mailleur;
+          @ echo "POSGRESQL database ready"
+
+ndbpostg:
+          @-(                                          \
+            echo "drop database mailleur;";            \
+            echo "DROP ROLE apache;";                  \
+            echo "DROP ROLE dovecot;";                 \
+            echo "DROP EXTENSION pgcrypto;";           \
+            ) | psql -q -U postgres template1
+
+#--------------------------------------------------------------------
+#procedure to delete or create a postgresql and mysql test database
+
+newtest        :  dbpostg dbmysql
+
+deltest        :  ndbpostg ndbmysql
+
+newpop :
+          @ ../tools/genpsdusr.sh 2000 > popul.sql
+          @ echo "New 'popul' database created"
+
+#--------------------------------------------------------------------
+#--------------------------------------------------------------------
+.PHONY:          deltest
+#--------------------------------------------------------------------
diff --git a/sql/datatest.def b/sql/datatest.def
new file mode 100644 (file)
index 0000000..c09079f
--- /dev/null
@@ -0,0 +1,16 @@
+
+//X------------------------------------------------------------------
+//include file to ajuste result according database
+//Y------------------------------------------------------------------
+#ifdef POSTGRESQL
+#define ADDTIME(adding) 'adding'
+#define CONCAT(A,B)     A||B
+#define INET_ATON(A)    A
+#define ENCRYPT(A,B)    encrypt(A,B,'aes')
+#endif
+#ifdef MYSQL
+#define ADDTIME(adding) INTERVAL adding
+#define CONCAT(A,B)     concat(A,B)
+#define INET_ATON(A)    inet_aton(A)
+#define ENCRYPT(A,B)    encrypt(A,B)
+#endif
diff --git a/sql/datatest.sql b/sql/datatest.sql
new file mode 100644 (file)
index 0000000..1f72759
--- /dev/null
@@ -0,0 +1,81 @@
+
+#include       "datatest.def"
+
+//First remove ALL Email data
+DELETE FROM emails;
+//password is generated via command line:
+//openssl password
+//present password is crypted as a hash and in password too
+//adding a list of local email
+
+//==============================================================
+//set for local test
+INSERT INTO emails (email,password,lang)                       \
+        values ('jmp@example.com','4cabri','FRA');
+INSERT INTO emails (email,password)                            \
+        values ('root@example.com','4cabri');
+INSERT INTO emails (email,password)                            \
+        values ('postmaster@example.com','postmaster');
+INSERT INTO emails (email,password)                            \
+        values ('webmaster@example.com','webmaster');
+INSERT INTO emails (email,password)                            \
+        values ('utf8-áö_üñ@example.com','utf8-áö_üñ');
+INSERT INTO emails (email,password)                            \
+        values ('user1@mailleur.example.com','user1');
+
+//Set for system with Postgresql database 
+INSERT INTO emails (email,password)                            \
+         values ('user1@posdb.example.com','user1');
+INSERT INTO emails (email,password)                            \
+         values ('user2@posdb.example.com','user2');
+
+//Set for system with MySQL database
+INSERT INTO emails (email,password)                            \
+         values ('user1@mardb.example.com','user1');
+INSERT INTO emails (email,password)                            \
+         values ('user2@mardb.example.com','user2');
+
+//==============================================================
+//List of remote IP status
+//good credit IP
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('127.127.0.25',10,now());
+//---------------------------------------------------------------
+//low credit IP
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('27.151.45.98',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('45.197.14.139',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('213.209.157.107',-5,now()+ADDTIME(200 day));
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('213.209.157.108',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('122.175.193.134',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('176.53.146.143',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('209.85.218.68',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('216.246.113.145',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('193.111.208.122',-5,now());
+
+//==============================================================
+//selected IP from from 'clean' server
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('192.219.254.189',100,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('216.252.69.41',100,now());
+
+//selected IP with with very low credit
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('127.127.127.127',-50,now());
+
+//==============================================================
+//defining admin user
+INSERT INTO admins (email)                             \
+         values ('root@example.com');
+INSERT INTO admins (email)                             \
+         values ('jmp@example.com');
+//==============================================================
diff --git a/sql/mailleur.sql b/sql/mailleur.sql
new file mode 100644 (file)
index 0000000..bd7faed
--- /dev/null
@@ -0,0 +1,232 @@
+/********************************************************/
+/*                                                     */
+/*     This file define the mailleur data base         */
+/*     structure                                       */
+/*                                                     */
+/********************************************************/
+/*Specific PostgreSQL data base definitions            */
+#ifdef POSTGRESQL
+#define AUTOSERIAL     SERIAL UNIQUE
+#define DURATION       TIMESPAN
+#define DBTIMESTAMP    TIMESTAMP(0) WITH TIME ZONE
+#define DBNAN          'NaN'
+#define        DFLT            DEFAULT
+#define        SHORTTEXT       TEXT
+#define        TEXTUNIQUE      TEXT UNIQUE NOT NULL
+#define        INS(name)       name            /*index size    */
+#define        ESCLIT          E
+#define        mailapache      apache
+#define        maildove        dovecot
+#define        CASESENSITIVE
+#define ADDTIME(adding)        'adding'
+#define        INET            inet
+#define CRYPTED         bytea
+#endif
+
+/*Specific MySQL type data base definitions                    */
+#if defined(MYSQL) || defined(MYSQLI)
+#define AUTOSERIAL     INTEGER AUTO_INCREMENT UNIQUE PRIMARY KEY
+#define DURATION       INT8
+#define DBTIMESTAMP    TIMESTAMP NULL
+#define DBNAN          NULL
+#define        DFLT            DEFAULT
+#define        SHORTTEXT       VARCHAR(30)
+#define        TEXTUNIQUE      VARCHAR(100) UNIQUE NOT NULL
+#define        INS(name)       name(80)        /*index size    */
+#define        ESCLIT
+#define        mailapache      'apache'@'localhost'
+#define        maildove        'dovecot'@'localhost'
+#define        CASESENSITIVE   CHARACTER SET latin1 COLLATE latin1_bin
+#define ADDTIME(adding)        INTERVAL adding
+#define        INET            VARBINARY(16)
+#define CRYPTED         MEDIUMBLOB
+#endif
+
+/********************************************************/
+/********************************************************/
+/*                                                     */
+/*     User available email definition table           */
+/*                                                     */
+/********************************************************/
+//table to define LOCAL USER
+CREATE TABLE emails    (
+       email           TEXTUNIQUE,     //User email
+       creation        DBTIMESTAMP     //record creation
+                       DFLT NOW(),
+       lastemail       DBTIMESTAMP     //last email received
+                       DFLT NOW(),
+       lastscan        DBTIMESTAMP     //last scaning date
+                       DFLT NOW(),
+       password        TEXT            //user (encrypted) password
+                       DFLT '!',
+       hash            TEXT,           //'email:realm:password' MD5
+       lang            TEXT            //user prefered language
+                       DFLT 'ENG',     //English by default
+       space           INTEGER         //space used by user email      
+                       DFLT 0,
+       mxspace         INTEGER         //Maximun space available
+                       DFLT 20000,     //to user (20 Gig).
+       locked          INTEGER         //The account is locked out
+                       DFLT 0
+       );
+GRANT SELECT,INSERT,UPDATE,DELETE      ON emails TO mailapache;
+GRANT SELECT                           ON emails TO maildove;
+
+
+//table about session
+CREATE TABLE sessions  (
+       creation        DBTIMESTAMP     //record creation
+                       DFLT NOW(),
+       sessid          TEXTUNIQUE,     //Session id
+       sesstitle       TEXT,           //The session email title
+       sessfrom        TEXT,           //The session from
+       emailfrom       TEXT,           //The from within the email itself
+       taille          INTEGER         //The email size
+                       DFLT 0,
+       duration        TEXT            //email transfer duration
+                       DFLT NULL
+       );
+GRANT SELECT                           ON sessions TO mailapache;
+
+//table about recipient email information
+//summerize all emails received actions status according sessions and recipient
+CREATE TABLE actions   (
+       creation        DBTIMESTAMP     //record creation
+                       DFLT NOW(),
+       code            SHORTTEXT,      //action status (one char)
+       sessid          TEXT,           //Session id
+       remoteip        TEXT            //Remote serveur IP number
+                       DFLT NULL,
+       reverse         TEXT            //remote server reverse address name
+                       DFLT 'Unknown IP',
+       smtpfrom        TEXT,           //SMTP Connection "MAIL FROM:"
+       rcptto          TEXT,           //SMTP Connection "RCPT TO:"
+       emailfrom       TEXT,           //email contents "From:"
+       subject         TEXT,           //email contents "Subject:"
+       status          INTEGER,        //info status
+       numline         INTEGER,        //status line
+       info            TEXT            //line info
+       );
+GRANT SELECT                           ON sessions TO mailapache;
+
+//defining table about remote server
+CREATE TABLE remotes   (
+       remoteip        TEXTUNIQUE,     //remote IP number
+       lastscan        DBTIMESTAMP     //record last update
+                       DFLT '1970-01-01',
+       lastupdate      DBTIMESTAMP     //record creation
+                       DFLT NOW(),
+       credit          INTEGER         //Remote IP current credit (-100..+100)
+                       DFLT 5,
+       reverse         TEXT            //Remote official revers address
+                       DFLT NULL,
+       listing         TEXT            //explaination about black listing
+                       DFLT NULL,
+       links           INTEGER
+                       DFLT 1          //how many time the remote connected
+       );
+CREATE INDEX remotes_ndx ON remotes(lastscan,lastupdate);
+//set a local IP
+INSERT INTO remotes (remoteip,reverse,credit,lastscan)         \
+         values ('127.0.0.1','localhost.localldomain',99,now());
+
+//defining table about eventlog reference
+CREATE TABLE events    (
+       creation        DBTIMESTAMP     //record creation
+                       DFLT NOW(),
+       serial          AUTOSERIAL,     //record unique number
+       sessid          TEXTUNIQUE,     //Session id
+       start           INTEGER,        //Event log starting point
+       stop            INTEGER         //Event log ending point
+       );
+CREATE INDEX event_id_ndx ON events(sessid);
+CREATE INDEX event_date_ndx ON events(creation);
+
+
+//defining table about email aliases
+//list of aliases related to ONE email
+CREATE TABLE aliases   (
+       email           TEXT,           //Email address
+       alias           TEXT            //One aliases
+       );
+CREATE INDEX aliases_id_ndx ON aliases(email);
+
+//List all currently opened cookies
+CREATE TABLE cookies   (
+       cookuuid        TEXTUNIQUE,     //cookies unique id
+       email           TEXT,           //user email address
+       expire          DBTIMESTAMP     //cookies date limit
+       );
+
+//List emails address with admin capablities
+CREATE TABLE admins    (
+       email           TEXT            //user email address
+       );
+
+//--------------------------------------------------------------
+//Defining TRIGGER fonctions according database type
+//--------------------------------------------------------------
+//Caution this REALM value MUST be the same as defined
+//in mailleur.conf
+#define        REALM   'mailleur-email'
+#define        HASHING concat(NEW.email,':',REALM,':',NEW.password)
+
+
+#ifdef POSTGRESQL
+CREATE FUNCTION updpass()
+  RETURNS trigger AS $$
+  BEGIN
+    IF NEW.password IS NULL OR 
+       length(trim(NEW.password)) = 0 OR
+       substring(NEW.password,1,1) = '$' THEN
+      NEW.hash=NULL;
+      NEW.password=NEW.password;
+    ELSE
+      NEW.hash = md5(HASHING);
+      NEW.password = crypt (NEW.password,gen_salt('md5'));
+    END IF;
+  RETURN NEW;
+  END;
+  $$ LANGUAGE 'plpgsql';
+
+CREATE TRIGGER keephash
+       BEFORE INSERT OR UPDATE on emails
+       FOR EACH ROW EXECUTE PROCEDURE updpass();
+#endif
+
+#if defined(MYSQL) || defined(MYSQLI)
+
+DELIMITER $$
+
+CREATE TRIGGER sethash 
+  BEFORE UPDATE ON emails FOR EACH ROW
+  BEGIN 
+    IF NEW.password IS NULL OR 
+       LENGTH(TRIM(NEW.password))=0 OR
+       LEFT(NEW.password,1) = '$' THEN
+      set NEW.hash=NULL;
+      set NEW.password=NEW.password;
+    else 
+      set NEW.hash=MD5(HASHING);
+      set NEW.password=encrypt(NEW.password,concat('$1$',md5(rand())));
+    END IF;
+  END$$
+    
+  
+CREATE TRIGGER updhash
+  BEFORE INSERT ON emails FOR EACH ROW
+  BEGIN
+    IF NEW.password IS NULL OR 
+       LENGTH(TRIM(NEW.password))=0 OR
+       LEFT(NEW.password,1) = '$' THEN
+      set NEW.hash=NULL;
+      set NEW.password=NEW.password;
+    else 
+      set NEW.hash=MD5(HASHING);
+      set NEW.password=encrypt(NEW.password,concat('$1$',md5(rand())));
+    END IF;
+  END$$
+
+DELIMITER ;
+
+#endif
diff --git a/sql/preset.sql b/sql/preset.sql
new file mode 100644 (file)
index 0000000..7088111
--- /dev/null
@@ -0,0 +1,27 @@
+
+/********************************************************/
+/*                                                     */
+/*     This file define the mailleur data base         */
+/*     local preset value                              */
+/*                                                     */
+/********************************************************/
+
+/********************************************************/
+/*                                                     */
+/*     Creating the bare minimun user configuration    */
+/*                                                     */
+/********************************************************/
+//root is the default preset admin
+INSERT INTO admins (email)
+       VALUES (ROOTDOMAINNAME);
+
+//inserting the mail account
+INSERT INTO emails (email,password)
+       VALUES (ROOTDOMAINNAME,RPASS);
+
+/********************************************************/
+/*                                                     */
+/*     Preset value completed                          */
+/*                                                     */
+/********************************************************/
+
diff --git a/support/addconfig.sh b/support/addconfig.sh
new file mode 100755 (executable)
index 0000000..7f5f076
--- /dev/null
@@ -0,0 +1,19 @@
+#! /usr/bin/bash
+#-----------------------------------------------------------------
+#Shell script to add local information to configuration file
+#-----------------------------------------------------------------
+export APPNAME=mailleur
+export SPOOLDIR=/var/spool/$APPNAME/mails
+#loading all applications variables
+[ -f /etc/$APPNAME/$APPNAME.conf ] && . /etc/$APPNAME/$APPNAME.conf
+#overiding some variable by local setting
+[ -f /etc/sysconfig/$APPNAME ] && . /etc/sysconfig/$APPNAME
+#-----------------------------------------------------------------
+#loading the variable value
+LOCDOM=`dnsdomainname`
+
+sed -i                                                 \
+       -e "s/^DFLTDOM.*$/DFLTDOMAIN=\"$LOCDOM\"/"      \
+       /etc/$APPNAME/$APPNAME.conf
+
+
diff --git a/support/do_database.sh b/support/do_database.sh
new file mode 100755 (executable)
index 0000000..ccf4a9e
--- /dev/null
@@ -0,0 +1,124 @@
+#! /usr/bin/bash
+#-------------------------------------------------------------
+#$1 working directory
+#procedure to create the clement data-base the first
+#time the application is installed.
+#-------------------------------------------------------------
+#loading the variable value
+. /etc/mailleur/mailleur.conf
+#-------------------------------------------------------------
+
+#setting the Variable
+#first the Data-base type
+if [ -z "$DB_TYPE" ] ; then
+  DB_TYPE=POSTGRESQL;
+  fi
+
+#Then the data-base name
+if [ -z "$DB_NAME" ] ; then
+  DB_NAME=$APPNAME;
+  fi
+#-------------------------------------------------------------
+#moving to the right directory
+cd $1
+LOG=/etc/$APPNAME/dbinst.log
+SQLLOG=/etc/$APPNAME/dbinfo.log
+USER=`id -nu`
+#building data_base
+(
+echo "#=========================================="
+date
+echo "#------------------------------------------"
+/bin/echo "creating $DB_TYPE:$DB_NAME data-base" >> $LOG
+/bin/echo "LANG=<"$LANG"> DB_LANG=<"$DB_LANG">"
+) >> $LOG
+
+case "$DB_TYPE" in 
+  "TO_BE_DEFINED"      )
+    echo "Data base type is NOT set within mailleur.conf"
+    exit 1
+    ;;
+  "MYSQL"              )
+    SQL="/usr/bin/mariadb"
+    MSQL="/usr/bin/mariadb $APPNAME -q"
+    /etc/rc.d/init.d/mysqld status
+    if [ $? != 0 ] ; then
+      echo "MySQL serveur NOT found up and running (exiting!)"
+      exit 1
+      fi
+    if [ "$USER" = "root" ] ; then
+      chown $APPNAME $LOG
+      (
+      echo "CREATE USER apache@localhost;"
+      echo "GRANT USAGE ON $APPNAME.* TO apache@localhost;"
+      echo "CREATE USER dovecot@localhost;"
+      echo "GRANT USAGE ON $APPNAME.* TO dovecot@localhost;"
+      echo "GRANT SELECT ON $APPNAME.* TO dovecot@localhost;"
+      echo "CREATE USER $APPNAME@localhost;"
+      echo "GRANT ALL PRIVILEGES ON $APPNAME.* TO $APPNAME@localhost;"
+      echo "FLUSH PRIVILEGES;"
+      echo "CREATE DATABASE $APPNAME;"
+      echo "ALTER DATABASE $APPNAME CHARACTER SET utf8;"
+      ) | $SQL >> $LOG 2>&1
+      /bin/echo "data-base is now created" >> $LOG
+      fi
+    ;;
+  "*"          )
+    #undefined database type at that stage?
+    ;;
+  "POSTGRESQL"         )
+    SQL="/usr/bin/psql -q"
+    MSQL="/usr/bin/psql -U $APPNAME -q $APPNAME"
+    /etc/rc.d/init.d/postgresql status
+    if [ $? != 0 ] ; then
+      echo "Postgresql serveur NOT found up and running (exiting!)"
+      exit 1
+      fi
+    if [ ! -z "$DB_PORT" ] ; then
+      SQLPORT="-p $DB_PORT"
+      fi
+    if [ ! -z "$DB_HOST" ] ; then
+      SQLHOST="-h $DB_HOST"
+      fi
+    if [ "$USER" = "root" ] ; then
+      chown $APPNAME $LOG
+      (
+      echo "CREATE ROLE $APPNAME WITH LOGIN CREATEDB SUPERUSER;"
+      echo "CREATE ROLE apache WITH LOGIN;"
+      echo "CREATE ROLE dovecot WITH LOGIN;"
+      echo "CREATE EXTENSION IF NOT EXISTS pgcrypto;"
+      echo "CREATE DATABASE $APPNAME ENCODING='UTF8';"
+      ) | /usr/bin/su - -m postgres -c "$SQL $SQLHOST $SQLPORT template1" >> $LOG 2>&1
+      echo "data-base is now created" >> $LOG
+      fi
+    ;;
+  "*"          )
+    #undefined database type at that stage?
+    ;;
+  esac
+#loading database definition
+if [ -n "$MSQL" ] ; then
+  echo "#--------------- Create Database ----------------" >> $SQLLOG
+  cpp -P -D$DB_TYPE /usr/share/$APPNAME/sql/$APPNAME.sql       |       \
+               tee -a $SQLLOG                                  |       \
+               $MSQL >> $SQLLOG 2>&1
+  echo "#--------------- Set Preset value ---------------" >> $SQLLOG
+  cpp -P -D$DB_TYPE                                                    \
+         -DDOMAINNAME=\'$DOMAINNAME\'                                  \
+         -DROOTDOMAINNAME=\'root@$DOMAINNAME\'                         \
+         -DRPASS=\'$RPASS\'                                            \
+         /usr/share/$APPNAME/sql/preset.sql                    |       \
+               tee -a $SQLLOG                                  |       \
+               $MSQL >> $SQLLOG 2>&1
+  if [ -f /usr/share/$APPNAME/sql/dataset.sql ] ; then
+    echo "#--------------- Set dataset value ---------------" >> $SQLLOG
+    cpp -P -D$DB_TYPE                                                  \
+         -DDOMAINNAME=\'$DOMAINNAME\'                                  \
+         -DROOTDOMAINNAME=\'root@$DOMAINNAME\'                         \
+         -DRPASS=\'$RPASS\'                                            \
+         /usr/share/$APPNAME/sql/dataset.sql                   |       \
+               tee -a $SQLLOG                                  |       \
+               $MSQL >> $SQLLOG 2>&1
+    fi
+  echo "#--------------- Init Process Completed ---------" >> $SQLLOG
+  fi
diff --git a/support/do_dns_tlsa.sh b/support/do_dns_tlsa.sh
new file mode 100755 (executable)
index 0000000..0d04cf2
--- /dev/null
@@ -0,0 +1,119 @@
+#! /usr/bin/bash
+#-------------------------------------------------------------------
+#procedure to generate a lets encrypt certificate with a contanst
+#public/private key
+#-------------------------------------------------------------------
+#comment in if working in production
+DRYRUN="--dry-run"
+STAGING="--staging"
+MRKR=`date +"%F"`
+APPNAME=mailleur
+#-------------------------------------------------------------------
+WRKDIR=/etc/certbot/$APPNAME
+
+gen_cnf()
+
+{
+if [ ! -f ./cnffile ] ; then
+  #generate a config file
+  cat > ./cnffile << EOT
+[req]
+distinguished_name=req_dn
+[req_dn]
+commonName=`uname -n`
+[SAN]
+subjectAltName=DNS:`uname -n`
+EOT
+  fi
+}
+
+gen_key()
+
+{
+#Generate an Elliptic Curve Digital Signature Algorithm
+if [ ! -f ./ec_key.pem ] ; then
+  openssl ecparam                              \
+       -out ./ec_key.pem                       \
+       -genkey                                 \
+       -name prime256v1
+  fi
+}
+
+
+gen_req()
+
+{
+#Generate the CSR request
+openssl req                                    \
+       -config ./cnffile                       \
+       -outform PEM                            \
+       -new                                    \
+       -nodes                                  \
+       -subj '/'                               \
+       -reqexts SAN                            \
+       -out ./request.csr                      \
+       -keyout ./$APPNAME-key.pem              \
+       -key ./ec_key.pem
+}
+
+
+gen_cert()
+
+{
+#Request certificate (with a steady key) via cerbot
+#Note" this is done in dry-run mode (remove it to
+#pass in production mode)
+
+#removing previous 
+rm -f ./$MRKR-*
+certbot certonly                                       \
+       --apache                                        \
+       --force-renewal                                 \
+       --csr ./request.csr                             \
+       --fullchain-path ./$MRKR-fullchain.pem          \
+       --chain-path ./$MRKR-chain.pem                  \
+       --cert-path ./$MRKR-cert.pem                    \
+       --work-dir .                                    \
+       --logs-dir ./logs                               \
+       $STAGING                                        \
+       $DRYRUN                                         \
+
+ln -nsf                                                        \
+       ./$MRKR-fullchain.pem                           \
+       ./$APPNAME-fullchain.pem
+
+ln -nsf                                                        \
+       ./$MRKR-chain.pem                               \
+       ./$APPNAME-chain.pem
+
+ln -nsf                                                        \
+       ./$MRKR-cert.pem                                \
+       ./$APPNAME-cert.pem
+
+}
+
+gen_dane ()
+
+{
+SUM=`openssl ec -in ./ec_key.pem -pubout -outform DER 2>/dev/null | sha256sum`
+
+(
+echo ";--------------------------------------------"
+echo ";TLSA record to be inserted in domain DNS"
+echo -e "_25._tcp.`hostname -s`\tTLSA\t3 1 1\t$SUM"
+echo -e "_465._tcp.`hostname -s`\tTLSA\t3 1 1\t$SUM"
+echo -e "_587._tcp.`hostname -s`\tTLSA\t3 1 1\t$SUM"
+echo ";--------------------------------------------"
+) > ./dns_tlsa
+}
+
+#Set working directory
+mkdir -p $WRKDIR
+cd $WRKDIR
+
+gen_cnf
+gen_key
+gen_req
+gen_cert
+gen_dane
+
diff --git a/support/do_httpd.sh b/support/do_httpd.sh
new file mode 100755 (executable)
index 0000000..2f1a532
--- /dev/null
@@ -0,0 +1,105 @@
+#! /usr/bin/bash
+#-----------------------------------------------------------------
+#Shell script to add local information to configuration file
+#-----------------------------------------------------------------
+#loading the variable value
+. /etc/mailleur/mailleur.conf
+#-----------------------------------------------------
+#Updating clement httpd configuration file
+export hostipv4=`dig \`uname -n\` A +noall +short`;
+export hostipv6=`dig \`uname -n\` AAAA +noall +short`;
+if [ -z "$domain" ] ; then
+  export domain=`dnsdomainname`
+  fi
+
+#to make sure we have at least an hostipv4 reference
+if [ -z "$hostipv4" -a -z "$hostipv6" ] ; then
+  export hostipv4=`grep \`uname -n\` /etc/hosts | cut  -f1`
+  fi
+#-----------------------------------------------------------------
+add_web()
+
+{
+#updating httpd.con
+sed -i                                                                                 \
+  -e "s:#LoadModule ssl_:LoadModule ssl_:"                                     \
+  -e "s:#LoadModule rewrite:LoadModule rewrite:"                               \
+  -e "s:#LoadModule socache_shmcb_module:LoadModule socache_shmcb_module:"     \
+  /etc/httpd/httpd.conf
+  
+#converting '.' to '\.'
+reldom=`echo $domain | sed -e "s/\\./\\\\\\./g"`
+
+cat >> /etc/httpd/conf.d/$APPNAME.conf <<!EOT
+#$APPNAME START
+#Done by the $APPNAME first start (do_httpd.sh)
+
+<VirtualHost *:80>
+        ServerName             `hostname`
+       ServerAlias             $APPNAME.$domain
+       ServerAlias             localhost.localdomain
+        ServerAdmin            root@$domain
+       RewriteEngine           on
+       #comment in if you want to debug redirect
+       #LogLevel                alert rewrite:trace3
+        RewriteCond            %{HTTP_HOST} ^$APPNAME\.$reldom         [OR]
+       RewriteCond             %{HTTP_HOST} ^`hostname -s`\.$reldom
+        RewriteRule            ^(.*)$ https://`hostname -s`.$domain    [R=301]
+        ErrorLog               logs/mailerr.log
+        TransferLog            logs/maillog.log
+</VirtualHost>
+
+#-----------------------------------------------------------------
+#Listening on TLS port
+Listen  443
+
+SSLSessionCache        "shmcb:logs/ssl_scache(512000)"
+SSLSessionCacheTimeout  300
+
+#-----------------------------------------------------------------
+<VirtualHost *:443>
+        ServerName             `hostname`
+        ServerAlias            $APPNAME.$domain
+        ServerAlias            localhost.localdomain
+        ServerAdmin            root@$domain
+        DocumentRoot           /var/www/$APPNAME
+        Alias reg-icons                /var/www/$APPNAME/reg-icons
+        <Directory             /var/www/$APPNAME>
+                Options MultiViews FollowSymlinks ExecCGI
+                AllowOverride AuthConfig FileInfo Indexes Limit Options
+               Require all granted
+        </Directory>
+       #----------------------------------------------
+       #SSL
+       SSLEngine On
+       SSLCertificateKeyFile   /etc/pki/$APPNAME/$APPNAME-key.pem
+       SSLCertificateFile      /etc/pki/$APPNAME/$APPNAME-cert.pem
+       SSLProtocol             +TLSv1.3 +TLSv1.2
+       SSLCompression          OFF
+        SSLHonorCipherOrder    ON
+       #----------------------------------------------
+        ErrorLog logs/mailerr-ssl.log
+        TransferLog logs/maillog-ssl.log
+</VirtualHost>
+
+#$APPNAME STOP
+!EOT
+}
+
+#-----------------------------------------------------------------
+#setting name virtual
+
+#to setup the IPV4 WEB interface
+if [ -n "$hostipv4" ] ; then
+  add_web $hostipv4
+  fi
+
+#-----------------------------------------------------------------
+#to setup the IPV6 WEB interface
+if [ -n "$hostipv6" ] ; then
+  add_web $hostipv6
+  fi
+
+#restarting httpd server
+service httpd restart
+
diff --git a/support/dummy-cert.sh b/support/dummy-cert.sh
new file mode 100755 (executable)
index 0000000..1655ad3
--- /dev/null
@@ -0,0 +1,43 @@
+#! /usr/bin/bash
+#---------------------------------------------------
+#shell script to create a dummy Certificat.
+#---------------------------------------------------
+umask 077
+
+answers() {
+       echo UN
+       echo "One State in the world"
+       echo "One City Somewhere"
+       echo "An Organization"
+       echo "$APPNAME E-mail filtering"
+       echo `hostname`
+       echo "$APPNAME@"`hostname`
+}
+
+if [ $# -eq 0 ] ; then
+       echo $"Usage: `basename $0` filename [...]"
+       exit 0
+fi
+
+for target in $@
+  do
+  PEM1=`mktemp /tmp/openssl.XXXXXX`
+  PEM2=`mktemp /tmp/openssl.XXXXXX`
+  trap "rm -f $PEM1 $PEM2" SIGINT
+  /usr/bin/openssl ecparam                             \
+       -genkey                                         \
+       -name secp384r1                                 \
+       -out $PEM1
+  answers                              |               \
+       /usr/bin/openssl req                            \
+               -key $PEM1                              \
+               -nodes                                  \
+               -x509                                   \
+               -text                                   \
+               -sha384                                 \
+               -days 3650                              \
+               -out $PEM2      2>      /dev/null
+  cp $PEM1  ${target}-key.pem
+  cp $PEM2  ${target}-cert.pem
+  rm -f $PEM1 $PEM2
+  done
diff --git a/support/mailleur-reset-db.sh b/support/mailleur-reset-db.sh
new file mode 100755 (executable)
index 0000000..a1e5925
--- /dev/null
@@ -0,0 +1,73 @@
+#! /usr/bin/bash
+#---------------------------------------------------------------
+#program to reset the database contents
+#parameters ["doit"] "confdirectory
+#---------------------------------------------------------------
+#start delay
+APPLICATION=mailleur
+DELAY=10
+DOIT=doit
+#---------------------------------------------------------------
+OWNER=`stat -c '%U' $0`
+USER=`id -nu`
+if [ "$USER" != "$OWNER" ] ; then
+  echo ""
+  echo "-----------"
+  echo "ATTENTION! $0 MUST BE executed by $0 file owner, exiting"
+  echo "-----------"
+  exit 1
+  fi
+#---------------------------------------------------------------
+if [ $# -eq 0 -o $# -ge 1  -a  "$1" != "$DOIT" ]  ; then
+  echo ""
+  echo "-----------"
+  echo "ATTENTION!:"
+  echo "- You are requesting a FULL database RESET."
+  echo "- You are requesting spool directory contents erasing."
+  echo ""
+  echo "all data, (DB contents, email, logs) will be GONE forever."
+  echo "Are you sure!"
+  echo "You have $DELAY seconds to cancel (CTRL-C)"
+  echo "-----------"
+  sleep $DELAY
+  fi
+if [ -n "$1" -a "$1" == "$DOIT" ]  ; then
+  shift;
+  fi
+CONF=/etc/$APPLICATION/$APPLICATION.conf
+if [ $# -gt 0 ] ; then
+  CONF=$1
+  fi
+if [ ! -f $CONF ] ; then
+  echo "configuration file $CONF is missing, exiting!"
+  exit 1
+  fi
+. $CONF
+#---------------------------------------------------------------
+#destroying the database
+case "$DB_TYPE" in
+  "POSTGRESQL"         )
+    DB=$(psql -U postgres -tAc "SELECT 1 FROM pg_database WHERE datname='${APPNAME}'")
+    if [ "$DB" = "1" ] ; then
+      echo "drop database $APPNAME;" | psql -q -U postgres template1
+      fi
+    echo "create database $APPNAME;" | psql -q -U postgres template1
+    SQL="psql -q $APPNAME"
+    ;;
+  "MYSQL"              )
+    DB=$(mariadb-show | grep $APPNAME )
+    if [ ! -z "$DB" ] ; then
+      echo "DROP DATABASE mailleur;" | mariadb
+      fi
+    echo "create database $APPNAME;" | mariadb
+    SQL="mariadb -q $APPNAME"
+    ;;
+  *                    )
+    echo $DB_TYPE is not expected!, check configuration file!
+    exit 1
+    ;;
+  esac
+
+#loading database
+cpp -P -D$DB_TYPE $ROOTBASE/usr/share/$APPNAME/sql/$APPNAME.sql | $SQL
+#cpp -P -D$DB_TYPE $ROOTBASE/usr/share/$APPNAME/sql/datatest.sql | $SQL
diff --git a/support/mailleur-settest-db.sh b/support/mailleur-settest-db.sh
new file mode 100755 (executable)
index 0000000..5169cb3
--- /dev/null
@@ -0,0 +1,61 @@
+#! /usr/bin/bash
+#---------------------------------------------------------------
+#program to reset the database contents
+#parameters ["doit"] "confdirectory
+#---------------------------------------------------------------
+#start delay
+APPLICATION=mailleur
+DELAY=10
+DOIT=doit
+#---------------------------------------------------------------
+OWNER=`stat -c '%U' $0`
+USER=`id -nu`
+if [ "$USER" != "$OWNER" ] ; then
+  echo ""
+  echo "-----------"
+  echo "ATTENTION! $0 MUST BE executed by $0 file owner, exiting"
+  echo "-----------"
+  exit 1
+  fi
+#---------------------------------------------------------------
+if [ $# -eq 0 -o $# -ge 1  -a  "$1" != "$DOIT" ]  ; then
+  echo ""
+  echo "-----------"
+  echo "ATTENTION!:"
+  echo "- You are requesting a FULL database PRERESET."
+  echo ""
+  echo "SOME database data will be GONE forever and replace by test value."
+  echo "Are you sure!"
+  echo "You have $DELAY seconds to cancel (CTRL-C)"
+  echo "-----------"
+  sleep $DELAY
+  fi
+if [ -n "$1" -a "$1" == "$DOIT" ]  ; then
+  shift;
+  fi
+CONF=/etc/$APPLICATION/$APPLICATION.conf
+if [ $# -gt 0 ] ; then
+  CONF=$1
+  fi
+if [ ! -f $CONF ] ; then
+  echo "configuration file $CONF is missing, exiting!"
+  exit 1
+  fi
+. $CONF
+#---------------------------------------------------------------
+#destroying the database
+case "$DB_TYPE" in
+  "POSTGRESQL"         )
+    SQL="psql -q $APPNAME"
+    ;;
+  "MYSQL"              )
+    SQL="mariadb -q $APPNAME"
+    ;;
+  *                    )
+    echo $DB_TYPE is not expected!, check configuration file!
+    exit 1
+    ;;
+  esac
+
+#RE loading database
+cpp -P -D$DB_TYPE $ROOTBASE/usr/share/$APPNAME/sql/datatest.sql | $SQL
diff --git a/support/starting.sh b/support/starting.sh
new file mode 100755 (executable)
index 0000000..4161303
--- /dev/null
@@ -0,0 +1,243 @@
+#! /usr/bin/bash
+#---------------------------------------------------------------
+#program to check everything is ready to start clement
+#$1 is either "sysvinit", "systemd" or "osukiss
+#===============================================================
+OS=$1           #set the OS Type
+#Hard coded variables
+export APPNAME=mailleur
+#----------------------------------------------------------------------
+#display code
+CURS_ZERO="\\033[0G"
+
+NORMAL="\\033[0;39m"         # Standard console grey
+SUCCESS="\\033[1;32m"        # Success is green
+WARNING="\\033[1;33m"        # Warnings are yellow
+FAILURE="\\033[1;31m"        # Failures are red
+INFO="\\033[1;36m"           # Information is light cyan
+BRACKET="\\033[1;34m"        # Brackets are blue
+
+# Use a colored prefix
+BMPREFIX="\t "
+SUCCESS_PREFIX="${SUCCESS}  *  ${NORMAL}"
+FAILURE_PREFIX="${FAILURE}*****${NORMAL}"
+WARNING_PREFIX="${WARNING} *** ${NORMAL}"
+SKIP_PREFIX="${INFO}  S  ${NORMAL}"
+
+SUCCESS_SUFFIX="${BRACKET}[${SUCCESS}  OK  ${BRACKET}]${NORMAL}"
+FAILURE_SUFFIX="${BRACKET}[${FAILURE} FAIL ${BRACKET}]${NORMAL}"
+WARNING_SUFFIX="${BRACKET}[${WARNING} WARN ${BRACKET}]${NORMAL}"
+SKIP_SUFFIX="${BRACKET}[${INFO} SKIP ${BRACKET}]${NORMAL}"
+
+#===============================================================
+#restarting a daemon
+#----------------------------------------------------------------------
+#$1 daemon application name
+sh_restart()
+
+{
+case "$OS" in 
+  "osukiss"    )
+    service $1 restart
+    ;;
+  "sysvinit"   )
+    service $1 restart
+    ;;
+  "systemd"    )
+    systemctl restart $1
+    ;;
+  *            )
+    echo "sh_restart: Unexpected OS=$OS, exiting!"
+    exit 1
+    ;;
+  esac
+}
+#----------------------------------------------------------------------
+#reporting a big trouble
+#----------------------------------------------------------------------
+sh_failure()
+
+{
+case "$OS" in
+  "osukiss"    )
+    /usr/bin/echo -n -e "${BMPREFIX}${@}"
+    /usr/bin/echo -e "${CURS_ZERO}${FAILURE_SUFFIX}"
+    ;;
+  "sysvinit"   )
+    /usr/bin/echo -n -e "${BMPREFIX}${@}"
+    /usr/bin/echo -e "${CURS_ZERO}${FAILURE_SUFFIX}"
+    ;;
+  "systemd"       )
+    echo -n $1 ; failure;
+    ;;
+  *             )
+    echo "sh_failure: Unexpected OS=$OS, exiting!"
+    exit 1
+    ;;
+  esac
+}
+#----------------------------------------------------------------------
+#reporting a small problem
+sh_warning()
+
+{
+case "$OS" in 
+  "osukiss"    )
+    /usr/bin/echo -n -e "${BMPREFIX}${@}"
+    /usr/bin/echo -e "${CURS_ZERO}${WARNING_SUFFIX}"
+    ;;
+  "sysvinit"   )
+    /usr/bin/echo -n -e "${BMPREFIX}${@}"
+    /usr/bin/echo -e "${CURS_ZERO}${WARNING_SUFFIX}"
+    ;;
+  "systemd"    )
+    echo -n $2 ; warning;
+    ;;
+  *            )
+    echo "sh_warning: Unexpected OS=$OS, exiting!"
+    exit 1
+    ;;
+  esac
+}
+#----------------------------------------------------------------------
+#reporting a success
+sh_success()
+
+{
+case "$OS" in 
+  "osukiss"    )
+    /usr/bin/echo -n -e "${BMPREFIX}${@}"
+    /usr/bin/echo -e "${CURS_ZERO}${SUCCESS_SUFFIX}"
+    ;;
+  "sysvinit"   )
+    /usr/bin/echo -n -e "${BMPREFIX}${@}"
+    /usr/bin/echo -e "${CURS_ZERO}${SUCCESS_SUFFIX}"
+    ;;
+  "systemd"    )
+    echo -n $1 ; success;
+    ;;
+  *            )
+    echo "sh_warning: Unexpected OS=$OS, exiting!"
+    exit 1
+    ;;
+  esac
+}
+#----------------------------------------------------------------------
+#adding dovecot configuration
+#----------------------------------------------------------------------
+do_doveconf()
+
+{
+service dovecot restart
+}
+
+#----------------------------------------------------------------------
+#adding httpd configuration
+#----------------------------------------------------------------------
+do_webconf()
+
+{
+/usr/lib/$APPNAME/support/do_httpd.sh
+if [ $? = 0 ] ; then
+  sh_success $"Preparing $APPNAME httpd configuration"
+else
+  sh_failure $"Preparing $APPNAME httpd configuration"
+fi 
+}
+#----------------------------------------------------------------------
+#procedure to check if the config is properly done
+#----------------------------------------------------------------------
+chk_config()
+
+{
+if      [ -z "$DB_TYPE" -o "$DB_TYPE" = "DB_TO_BE_DEFINED" ] ; then
+  echo -n "Checking file /etc/$APPNAME/$APPNAME.conf"
+  sh_failure $1 $" ->> not configured"
+  echo -e "\tPlease set DB_TYPE within /etc/$APPNAME/$APPNAME.conf"
+  echo -e "\tAborting start, Exiting at once"
+  exit -1;
+  fi
+}
+#----------------------------------------------------------------------
+#building application data-base
+#----------------------------------------------------------------------
+do_mkdb()
+
+{
+case "$DB_TYPE" in
+  "MYSQL"              |               \
+  "POSTGRESQL"         )
+    /usr/lib/$APPNAME/support/do_database.sh "/tmp"
+     if [ $? != 0 ] ; then
+       exit -1
+       fi
+    ;;
+  "TO_BE_DEFINED"      )
+    echo "You must install $APPNAME with a DB flavor (ex: $APPNAME-postgresql"
+    echo "Exiting now"
+    exit -2;
+    ;;
+  "*"                  )
+    echo "Unexpectd DB_TYPE variable within /etc/sysconfig/$APPNAME, please check"
+    echo "Exiting now"
+    exit -3;
+    ;;
+  esac
+#extracting local configuration value
+}
+#----------------------------------------------------------------------
+#creating mailleur local certificat
+#----------------------------------------------------------------------
+do_mkcert()
+
+{
+mkdir -p /etc/pki/$APPNAME
+if [ ! -f /etc/pki/$APPNAME/$APPNAME-cert.pem ] ; then
+  /usr/lib/$APPNAME/support/dummy-cert.sh /etc/pki/$APPNAME/$APPNAME
+  chown $APPNAME:mail                                  \
+       /etc/pki/$APPNAME/$APPNAME-cert.pem             \
+       /etc/pki/$APPNAME/$APPNAME-key.pem
+  sh_success $"Preparing $APPNAME server self-signed certificate"
+  fi
+}
+#----------------------------------------------------------------------
+#setting local mailleur configuration
+#----------------------------------------------------------------------
+do_mkconf()
+
+{
+if [ ! -f /etc/$APPNAME/config.done ] ; then
+  do_mkcert;
+  /usr/lib/$APPNAME/support/addconfig.sh
+  do_mkdb ;
+  do_doveconf ;
+  do_webconf ;
+  case "$OS" in 
+    osukiss    )
+      ;;
+    sysvinit   )
+      ;;
+    systemd    )
+      ;;
+    *          )
+      echo "sh_mkconf: Unexpected OS=$OS, exiting!"
+      exit -1
+      ;;
+    esac
+  date > /etc/$APPNAME/config.done
+fi
+}
+#===============================================================
+#main script
+#----------------------------------------------------------------------
+#loading all applications variables
+[ -f /etc/$APPNAME/$APPNAME.conf ] && . /etc/$APPNAME/$APPNAME.conf
+#overiding some variable by local setting
+[ -f /etc/sysconfig/$APPNAME ] && . /etc/sysconfig/$APPNAME
+
+export DOMAINNAME=`dnsdomainname`;
+export RPASS=`grep root /etc/shadow | cut -d':' -f2 |sed -e 's:\\\\$:\\\\\\\\$:g'`;
+chk_config $1;
+do_mkconf $1;
+exit 0;
+#---------------------------------------------------------------
diff --git a/sysconfig/mailleur b/sysconfig/mailleur
new file mode 100644 (file)
index 0000000..fc85325
--- /dev/null
@@ -0,0 +1,8 @@
+#-------------------------------------------
+#options available to debug mailleur
+#OPTIONS="-d9"
+#this is to debug a specific function within a specific procedure name
+#Avaliable procedure name are defined within mailleur sources (#define OPEP)
+#OPTIONS="-d9 -Dlvleml.c:checkto,modrec.c:docontact"
+#-------------------------------------------
+
diff --git a/test_area/etc/mailleur/blacklister.conf b/test_area/etc/mailleur/blacklister.conf
new file mode 100644 (file)
index 0000000..ff146c4
--- /dev/null
@@ -0,0 +1,26 @@
+#dnsbl.sorbs.net
+#t1.dnsbl.net.au
+#checked 2025-07-12 by preference order
+#The SpamCop Blocking List (SCBL) lists IP addresses which
+#have transmitted reported email to SpamCop users. SpamCop
+-1     bl.spamcop.net                  127.0.0.2
+#Spamhaus Block List (SBL) is a realtime database of IP addresses
+#of spam sources, including known spammers, spam gangs,
+#spam operations and spam support services. 
+-1     sbl.spamhaus.org                127.0.0.2,127.0.0.3,127.0.0.9
+#b.barracudacentral.org will return the standard
+#127.0.0.2 IP address when queried if the SMTP server is listed.
+-6     b.barracudacentral.org          127.0.0.2
+#www.blocklist.de is a free and voluntary service provided
+#by a Fraud/Abuse-specialist,
+-1     bl.blocklist.de                 127.0.0.9,127.0.0.13
+# ips.backscatterer.org for scoring or rejecting misdirected
+#bounces and misdirected autoresponders 
+-1     ips.backscatterer.org           127.0.0.2
+#DroneBL is a realtime monitor of abusable IPs
+#-1    dnsbl.dronebl.org       
+#The s5h blacklist is a real-time IP-based blacklist that is
+#maintained by the System 5 Hosting (S5H) organization.
+#-1    all.s5h.net                     127.0.0.2
+#Junk Email Filter is a front end email spam filtering 
+-1     hostkarma.junkemailfilter.co    127.0.0.2
diff --git a/test_area/etc/mailleur/mailleur.conf b/test_area/etc/mailleur/mailleur.conf
new file mode 100644 (file)
index 0000000..3fb243b
--- /dev/null
@@ -0,0 +1,80 @@
+#file used to set environment configuration
+#------------------------------------------------
+#Defining application name
+APPNAME=mailleur
+#root working directoy definition for the whole application
+ROOTBASE="/"
+#defining local default domain
+DFLTDOMAIN="localdomain"
+#defining the local REALM
+#Caution! the realm is hardcoded within data-base
+#and USED too to hash the user passwd
+#Changing this value coule be a trouble make 
+#(entering again ALL user password)
+REALM="mailleur-email"
+#------------------------------------------------
+#list of listening port to do SMTP protocole
+#format
+#protocol|ipnum|port|iteration[,protocol|ipnum|port|iteration]++
+#example
+#stmtps|ipnum|465|5    -> protocol smtps|IP number|port 465|5 iterations
+#|ipnum|||             -> smtp|ipnum|25|2
+#||,smtps||465|2       -> smtp|0.0.0.0|25|2 + smtps||465|2
+#||                    -> smtp|0.0.0.0|25|2
+#Examples for test purpose
+#SMTPPORTS="|127.127.10.25|1025|,smtps|127.127.10.25|1465|1"
+#SMTPPORTS="|127.127.10.25|1025|"
+#SMTPPORTS="smtps|127.127.10.25|1465|1"
+#Defaults (production)
+SMTPPORTS="|||5,smtps||465|3,smtp||587|2"
+#------------------------------------------------
+#SSL Security level default value
+#CIPHER_LIST="DEFAULT"
+#SSL_SECURITY=2
+#------------------------------------------------
+#Defining SERVER mode Certificate data
+CA_ROOT_SRV="/etc/pki/tls/make-ca/ca-bundle.crt"
+CA_VERIFY_SRV=0        #to check PEER/client remote certificate
+#If certificate set via do_dns_tlsa.sh (letsencrypt)
+#CA_CERT_SRV="/etc/certbot/mailleur/mailleur-fullchain.pem"
+#CA_KEY_SRV="/etc/certbot/mailleur/mailleur-key.pem"
+#If certificate are self signed (default installation)
+#CA_CERT_SRV="/etc/pki/mailleur/mailleur-cert.pem"
+#CA_KEY_SRV="/etc/pki/mailleur/mailleur-key.pem"
+#Symbolic link set at installation
+CA_CERT_SRV="/etc/pki/mailleur/mailleur-cert.pem"
+CA_KEY_SRV="/etc/pki/mailleur/mailleur-key.pem"
+#------------------------------------------------
+#Defining CLIENT mode Certificate data
+CA_ROOT_CLT="/etc/pki/tls/make-ca/ca-bundle.crt"
+CA_VERIFY_CLT=0        #to check PEER/server remote certificate
+#If certificate set via do_dns_tlsa.sh (letsencrypt)
+#CA_CERT_CLT="/etc/certbot/mailleur/mailleur-fullchain.pem"
+#CA_KEY_CLT="/etc/certbot/mailleur/mailleur-key.pem"
+#If certificate are self signed (default installation)
+#CA_CERT_CLT="/etc/pki/mailleur/mailleur-cert.pem"
+#CA_KEY_CLT="/etc/pki/mailleur/mailleur-key.pem"
+#Symbolic link set at installation
+CA_CERT_CLT="/etc/mailleur/pki/mailleur-cert.pem"
+CA_KEY_CLT="/etc/mailleur/pki/mailleur-key.pem"
+#------------------------------------------------
+#Configured for Postgresql database
+#DB_TYPE can be either POSTGRESQL,MYSQL, default POSTGRESQL
+DB_TYPE=TO_BE_DEFINED
+DB_NAME=mailleur
+DB_HOST=localhost
+DB_PORT=TO_BE_DEFINED
+DB_LANG="UTF-8"
+#------------------------------------------------
+#Dovecot storage directory
+DOV_MAILDIR="/var/spool/mailleur/mails"
+#------------------------------------------------
+#The list of public blacklist serveur
+BLACKLISTER=/etc/mailleur/blacklister.conf
+#the list of IP from which we accept to relay email
+RELAYABLE=/etc/mailleur/relayed.conf
+#------------------------------------------------
+#Minimal credit level to which remote is rejected at once
+#if credit is equal or below that level
+RJCTCRED=-50
+#------------------------------------------------
diff --git a/test_area/etc/mailleur/mailleur.conf.dvl b/test_area/etc/mailleur/mailleur.conf.dvl
new file mode 100644 (file)
index 0000000..0389242
--- /dev/null
@@ -0,0 +1,66 @@
+#file used to set environment configuration
+#Used for developpement purpose ONLY
+#------------------------------------------------
+#Defining application name
+APPNAME=mailleur
+#root working directoy definition for the whole application
+ROOTBASE="/home/jmp/safe-mailleur/mailleur/test_area/"
+#defining local default domain
+DFLTDOMAIN="example.com"
+#defining the local REALM
+#Caution! the realm is hardcoded within data-base
+#and USED too to hash the user passwd
+#Changing this value coule be a trouble make 
+#(entering again ALL user password)
+REALM="mailleur-email"
+#------------------------------------------------
+#list of listening port to do SMTP protocole
+#format
+#protocol|ipnum|port|iteration[,protocol|ipnum|port|iteration]++
+#example
+#stmtps|ipnum|465|5    -> protocol smtps|IP number|port 465|5 iterations
+#|ipnum|||             -> smtp|ipnum|25|2
+#||,smtps||465|2       -> smtp|0.0.0.0|25|2 + smtps||465|2
+#||                    -> smtp|0.0.0.0|25|2
+#SMTPPORTS="||1025"
+#SMTPPORTS="|127.127.10.25|1025|,smtps|127.127.10.25|1026|1"
+#SMTPPORTS="|127.127.10.25|1025|"
+#SMTPPORTS="smtps|127.127.10.25|1065|1"
+#------------------------------------------------
+#SSL Security level
+#CIPHER_LIST="ECDHE-ECDSA-AES256-GCM-SHA384"
+#SSL_SECURITY=3
+#------------------------------------------------
+#Defining SERVER mode Certificate data
+CA_ROOT_SRV="/etc/pki/tls/make-ca/ca-bundle.crt"
+CA_CERT_SRV="./certs/mailleur_server-chain-cert_x509.pem"
+CA_KEY_SRV="./certs/mailleur_server-key.pem"
+CA_VERIFY_SRV=0        #to check PEER/client remote certificate
+#------------------------------------------------
+#Defining CLIENT mode Certificate data
+CA_ROOT_CLT="/etc/pki/tls/make-ca/ca-bundle.crt"
+CA_CERT_CLT="./certs/localhost-chain-cert.pem"
+CA_KEY_CLT="./certs/localhost-key.pem"
+CA_VERIFY_CLT=0        #to check PEER/server remote certificate
+#------------------------------------------------
+#Configured for Postgresql database
+#DB_TYPE can be either POSTGRESQL,MYSQL, default POSTGRESQL
+#DB_NAME=mailleur
+#DB_HOST=localhost
+#DB_LANG="UTF-8"
+#DB_TYPE=POSTGRESQL
+#DB_PORT=5432
+#DB_TYPE=MYSQL
+#DB_PORT=3306
+
+#------------------------------------------------
+#Dovecot storage directory
+DOV_MAILDIR="/var/spool/mailleur/mails"
+#------------------------------------------------
+SMTPPORTS="|127.127.10.25|1025|5,smtps|127.127.10.26|1465|3,smtp|127.127.10.26|1587|2"
+#------------------------------------------------
+#The list of public blacklist serveur
+BLACKLISTER=/etc/mailleur/blacklister.conf
+#the list of IP from which we accept to relay email
+RELAYABLE=/etc/mailleur/relayed.conf.dvl
+#------------------------------------------------
diff --git a/test_area/etc/mailleur/relayed.conf b/test_area/etc/mailleur/relayed.conf
new file mode 100644 (file)
index 0000000..114af58
--- /dev/null
@@ -0,0 +1,5 @@
+#--------------------------------------------------------
+##Private address block which email can be relayed from
+127.0.0.0/8                    #local loop number
+#--------------------------------------------------------
+#Setup you own list
diff --git a/test_area/etc/mailleur/relayed.conf.dvl b/test_area/etc/mailleur/relayed.conf.dvl
new file mode 100644 (file)
index 0000000..5c217dc
--- /dev/null
@@ -0,0 +1,6 @@
+#--------------------------------------------------------
+##Private address block which email can be relayed from
+127.0.0.0/8                    #local loop number
+192.168.254.0/24               #local network
+#--------------------------------------------------------
+#Setup you own list
diff --git a/test_area/usr/share/mailleur/sql/.gitignore b/test_area/usr/share/mailleur/sql/.gitignore
new file mode 100644 (file)
index 0000000..79d5f73
--- /dev/null
@@ -0,0 +1 @@
+popul.sql
diff --git a/test_area/usr/share/mailleur/sql/Makefile b/test_area/usr/share/mailleur/sql/Makefile
new file mode 100644 (file)
index 0000000..86d3b8f
--- /dev/null
@@ -0,0 +1,100 @@
+#--------------------------------------------------------------------
+#Generate database for postgresql and mysql
+#--------------------------------------------------------------------
+prod                                           \
+debug                                          \
+       :  mailleur.postg mailleur.mysql
+
+allclean                                       \
+clean  :  
+          @ rm -fr mailleur.mysql mailleur.postg
+
+#--------------------------------------------------------------------
+
+prepdb :  newpop debug deltest newtest
+          
+mailleur.postg                                 \
+       :  mailleur.sql
+          @ cpp -P -DPOSTGRESQL                \
+               mailleur.sql > mailleur.postg
+
+mailleur.mysql                                 \
+       :  mailleur.sql
+          @ cpp -P -DMYSQL                     \
+               mailleur.sql > mailleur.mysql
+
+dbmysql        :  mailleur.mysql
+          @ - (                                        \
+            echo "CREATE USER `id -un`@localhost;";    \
+            echo "CREATE USER apache@localhost;";      \
+            echo "CREATE USER dovecot@localhost;";     \
+            echo "GRANT ALL ON mailleur.*              \
+                       TO `id -un`@localhost           \
+                       WITH GRANT OPTION;";            \
+            echo "FLUSH PRIVILEGES;";                  \
+            echo "CREATE DATABASE mailleur;";          \
+            echo "ALTER DATABASE mailleur              \
+                        CHARACTER SET utf8;";          \
+            ) | sudo mariadb -q
+          @ cat mailleur.mysql | mariadb -q mailleur;
+          @ cpp -P -DMYSQL                             \
+               datatest.sql    | mariadb -q mailleur;
+            cpp -P -traditional -DMYSQL                \
+               popul.sql       | mariadb -q mailleur;
+          @ echo "MYSQL database ready"
+
+
+ndbmysql:
+          @-(                                          \
+            echo "DROP DATABASE mailleur;";            \
+            echo "DROP USER dovecot@localhost;";       \
+            echo "DROP USER apache@localhost;";        \
+            echo "DROP USER `id -un`@localhost;";      \
+            ) | sudo mariadb 
+
+dbpostg        :  mailleur.postg
+          @ -(                                         \
+            echo "CREATE ROLE apache WITH LOGIN;";     \
+            echo "CREATE ROLE dovecot WITH LOGIN;";    \
+            echo "CREATE EXTENSION pgcrypto;";         \
+            echo "CREATE DATABASE mailleur ";          \
+            echo "              ENCODING='UTF8';";     \
+            ) | psql -q -U postgres template1
+          @ cat mailleur.postg | psql -q mailleur;
+          @ cpp -DPOSTGRESQL                           \
+               -P                                      \
+               -traditional                            \
+               datatest.sql                    |       \
+                       sed -e"/^\/\//d"        |       \
+                       psql -q mailleur;
+          @ cpp -DPOSTGRESQL                           \
+               -P                                      \
+               -traditional                            \
+               popul.sql                       |       \
+                       sed -e"/^\/\//d"        |       \
+                       psql -q mailleur;
+          @ echo "POSGRESQL database ready"
+
+ndbpostg:
+          @-(                                          \
+            echo "drop database mailleur;";            \
+            echo "DROP ROLE apache;";                  \
+            echo "DROP ROLE dovecot;";                 \
+            echo "DROP EXTENSION pgcrypto;";           \
+            ) | psql -q -U postgres template1
+
+#--------------------------------------------------------------------
+#procedure to delete or create a postgresql and mysql test database
+
+newtest        :  dbpostg dbmysql
+
+deltest        :  ndbpostg ndbmysql
+
+newpop :
+          @ ../tools/genpsdusr.sh 2000 > popul.sql
+          @ echo "New 'popul' database created"
+
+#--------------------------------------------------------------------
+#--------------------------------------------------------------------
+.PHONY:          deltest
+#--------------------------------------------------------------------
diff --git a/test_area/usr/share/mailleur/sql/datatest.def b/test_area/usr/share/mailleur/sql/datatest.def
new file mode 100644 (file)
index 0000000..c09079f
--- /dev/null
@@ -0,0 +1,16 @@
+
+//X------------------------------------------------------------------
+//include file to ajuste result according database
+//Y------------------------------------------------------------------
+#ifdef POSTGRESQL
+#define ADDTIME(adding) 'adding'
+#define CONCAT(A,B)     A||B
+#define INET_ATON(A)    A
+#define ENCRYPT(A,B)    encrypt(A,B,'aes')
+#endif
+#ifdef MYSQL
+#define ADDTIME(adding) INTERVAL adding
+#define CONCAT(A,B)     concat(A,B)
+#define INET_ATON(A)    inet_aton(A)
+#define ENCRYPT(A,B)    encrypt(A,B)
+#endif
diff --git a/test_area/usr/share/mailleur/sql/datatest.sql b/test_area/usr/share/mailleur/sql/datatest.sql
new file mode 100644 (file)
index 0000000..1f72759
--- /dev/null
@@ -0,0 +1,81 @@
+
+#include       "datatest.def"
+
+//First remove ALL Email data
+DELETE FROM emails;
+//password is generated via command line:
+//openssl password
+//present password is crypted as a hash and in password too
+//adding a list of local email
+
+//==============================================================
+//set for local test
+INSERT INTO emails (email,password,lang)                       \
+        values ('jmp@example.com','4cabri','FRA');
+INSERT INTO emails (email,password)                            \
+        values ('root@example.com','4cabri');
+INSERT INTO emails (email,password)                            \
+        values ('postmaster@example.com','postmaster');
+INSERT INTO emails (email,password)                            \
+        values ('webmaster@example.com','webmaster');
+INSERT INTO emails (email,password)                            \
+        values ('utf8-áö_üñ@example.com','utf8-áö_üñ');
+INSERT INTO emails (email,password)                            \
+        values ('user1@mailleur.example.com','user1');
+
+//Set for system with Postgresql database 
+INSERT INTO emails (email,password)                            \
+         values ('user1@posdb.example.com','user1');
+INSERT INTO emails (email,password)                            \
+         values ('user2@posdb.example.com','user2');
+
+//Set for system with MySQL database
+INSERT INTO emails (email,password)                            \
+         values ('user1@mardb.example.com','user1');
+INSERT INTO emails (email,password)                            \
+         values ('user2@mardb.example.com','user2');
+
+//==============================================================
+//List of remote IP status
+//good credit IP
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('127.127.0.25',10,now());
+//---------------------------------------------------------------
+//low credit IP
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('27.151.45.98',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('45.197.14.139',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('213.209.157.107',-5,now()+ADDTIME(200 day));
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('213.209.157.108',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('122.175.193.134',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('176.53.146.143',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('209.85.218.68',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('216.246.113.145',-5,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('193.111.208.122',-5,now());
+
+//==============================================================
+//selected IP from from 'clean' server
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('192.219.254.189',100,now());
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('216.252.69.41',100,now());
+
+//selected IP with with very low credit
+INSERT INTO remotes (remoteip,credit,lastscan)                 \
+         values ('127.127.127.127',-50,now());
+
+//==============================================================
+//defining admin user
+INSERT INTO admins (email)                             \
+         values ('root@example.com');
+INSERT INTO admins (email)                             \
+         values ('jmp@example.com');
+//==============================================================
diff --git a/test_area/usr/share/mailleur/sql/mailleur.mysql b/test_area/usr/share/mailleur/sql/mailleur.mysql
new file mode 100644 (file)
index 0000000..4fca369
--- /dev/null
@@ -0,0 +1,121 @@
+CREATE TABLE emails (
+ email VARCHAR(100) UNIQUE NOT NULL,
+ creation TIMESTAMP NULL
+   DEFAULT NOW(),
+ lastemail TIMESTAMP NULL
+   DEFAULT NOW(),
+ lastscan TIMESTAMP NULL
+   DEFAULT NOW(),
+ password TEXT
+   DEFAULT '!',
+ hash TEXT,
+ lang TEXT
+   DEFAULT 'ENG',
+ space INTEGER
+   DEFAULT 0,
+ mxspace INTEGER
+   DEFAULT 20000,
+ locked INTEGER
+   DEFAULT 0
+ );
+GRANT SELECT,INSERT,UPDATE,DELETE ON emails TO 'apache'@'localhost';
+GRANT SELECT ON emails TO 'dovecot'@'localhost';
+CREATE TABLE sessions (
+ creation TIMESTAMP NULL
+   DEFAULT NOW(),
+ sessid VARCHAR(100) UNIQUE NOT NULL,
+ sesstitle TEXT,
+ sessfrom TEXT,
+ emailfrom TEXT,
+ taille INTEGER
+   DEFAULT 0,
+ duration TEXT
+   DEFAULT NULL
+ );
+GRANT SELECT ON sessions TO 'apache'@'localhost';
+CREATE TABLE actions (
+ creation TIMESTAMP NULL
+   DEFAULT NOW(),
+ code VARCHAR(30),
+ sessid TEXT,
+ remoteip TEXT
+   DEFAULT NULL,
+ reverse TEXT
+   DEFAULT 'Unknown IP',
+ smtpfrom TEXT,
+ rcptto TEXT,
+ emailfrom TEXT,
+ subject TEXT,
+ status INTEGER,
+ numline INTEGER,
+ info TEXT
+ );
+GRANT SELECT ON sessions TO 'apache'@'localhost';
+CREATE TABLE remotes (
+ remoteip VARCHAR(100) UNIQUE NOT NULL,
+ lastscan TIMESTAMP NULL
+   DEFAULT '1970-01-01',
+ lastupdate TIMESTAMP NULL
+   DEFAULT NOW(),
+ credit INTEGER
+   DEFAULT 5,
+ reverse TEXT
+   DEFAULT NULL,
+ listing TEXT
+   DEFAULT NULL,
+ links INTEGER
+   DEFAULT 1
+ );
+CREATE INDEX remotes_ndx ON remotes(lastscan,lastupdate);
+INSERT INTO remotes (remoteip,reverse,credit,lastscan) values ('127.0.0.1','localhost.localldomain',99,now());
+CREATE TABLE events (
+ creation TIMESTAMP NULL
+   DEFAULT NOW(),
+ serial INTEGER AUTO_INCREMENT UNIQUE PRIMARY KEY,
+ sessid VARCHAR(100) UNIQUE NOT NULL,
+ start INTEGER,
+ stop INTEGER
+ );
+CREATE INDEX event_id_ndx ON events(sessid);
+CREATE INDEX event_date_ndx ON events(creation);
+CREATE TABLE aliases (
+ email TEXT,
+ alias TEXT
+ );
+CREATE INDEX aliases_id_ndx ON aliases(email);
+CREATE TABLE cookies (
+ cookuuid VARCHAR(100) UNIQUE NOT NULL,
+ email TEXT,
+ expire TIMESTAMP NULL
+ );
+CREATE TABLE admins (
+ email TEXT
+ );
+DELIMITER $$
+CREATE TRIGGER sethash
+  BEFORE UPDATE ON emails FOR EACH ROW
+  BEGIN
+    IF NEW.password IS NULL OR
+       LENGTH(TRIM(NEW.password))=0 OR
+       LEFT(NEW.password,1) = '$' THEN
+      set NEW.hash=NULL;
+      set NEW.password=NEW.password;
+    else
+      set NEW.hash=MD5(concat(NEW.email,':','mailleur-email',':',NEW.password));
+      set NEW.password=encrypt(NEW.password,concat('$1$',md5(rand())));
+    END IF;
+  END$$
+CREATE TRIGGER updhash
+  BEFORE INSERT ON emails FOR EACH ROW
+  BEGIN
+    IF NEW.password IS NULL OR
+       LENGTH(TRIM(NEW.password))=0 OR
+       LEFT(NEW.password,1) = '$' THEN
+      set NEW.hash=NULL;
+      set NEW.password=NEW.password;
+    else
+      set NEW.hash=MD5(concat(NEW.email,':','mailleur-email',':',NEW.password));
+      set NEW.password=encrypt(NEW.password,concat('$1$',md5(rand())));
+    END IF;
+  END$$
+DELIMITER ;
diff --git a/test_area/usr/share/mailleur/sql/mailleur.postg b/test_area/usr/share/mailleur/sql/mailleur.postg
new file mode 100644 (file)
index 0000000..922e501
--- /dev/null
@@ -0,0 +1,111 @@
+CREATE TABLE emails (
+ email TEXT UNIQUE NOT NULL,
+ creation TIMESTAMP(0) WITH TIME ZONE
+   DEFAULT NOW(),
+ lastemail TIMESTAMP(0) WITH TIME ZONE
+   DEFAULT NOW(),
+ lastscan TIMESTAMP(0) WITH TIME ZONE
+   DEFAULT NOW(),
+ password TEXT
+   DEFAULT '!',
+ hash TEXT,
+ lang TEXT
+   DEFAULT 'ENG',
+ space INTEGER
+   DEFAULT 0,
+ mxspace INTEGER
+   DEFAULT 20000,
+ locked INTEGER
+   DEFAULT 0
+ );
+GRANT SELECT,INSERT,UPDATE,DELETE ON emails TO apache;
+GRANT SELECT ON emails TO dovecot;
+CREATE TABLE sessions (
+ creation TIMESTAMP(0) WITH TIME ZONE
+   DEFAULT NOW(),
+ sessid TEXT UNIQUE NOT NULL,
+ sesstitle TEXT,
+ sessfrom TEXT,
+ emailfrom TEXT,
+ taille INTEGER
+   DEFAULT 0,
+ duration TEXT
+   DEFAULT NULL
+ );
+GRANT SELECT ON sessions TO apache;
+CREATE TABLE actions (
+ creation TIMESTAMP(0) WITH TIME ZONE
+   DEFAULT NOW(),
+ code TEXT,
+ sessid TEXT,
+ remoteip TEXT
+   DEFAULT NULL,
+ reverse TEXT
+   DEFAULT 'Unknown IP',
+ smtpfrom TEXT,
+ rcptto TEXT,
+ emailfrom TEXT,
+ subject TEXT,
+ status INTEGER,
+ numline INTEGER,
+ info TEXT
+ );
+GRANT SELECT ON sessions TO apache;
+CREATE TABLE remotes (
+ remoteip TEXT UNIQUE NOT NULL,
+ lastscan TIMESTAMP(0) WITH TIME ZONE
+   DEFAULT '1970-01-01',
+ lastupdate TIMESTAMP(0) WITH TIME ZONE
+   DEFAULT NOW(),
+ credit INTEGER
+   DEFAULT 5,
+ reverse TEXT
+   DEFAULT NULL,
+ listing TEXT
+   DEFAULT NULL,
+ links INTEGER
+   DEFAULT 1
+ );
+CREATE INDEX remotes_ndx ON remotes(lastscan,lastupdate);
+INSERT INTO remotes (remoteip,reverse,credit,lastscan) values ('127.0.0.1','localhost.localldomain',99,now());
+CREATE TABLE events (
+ creation TIMESTAMP(0) WITH TIME ZONE
+   DEFAULT NOW(),
+ serial SERIAL UNIQUE,
+ sessid TEXT UNIQUE NOT NULL,
+ start INTEGER,
+ stop INTEGER
+ );
+CREATE INDEX event_id_ndx ON events(sessid);
+CREATE INDEX event_date_ndx ON events(creation);
+CREATE TABLE aliases (
+ email TEXT,
+ alias TEXT
+ );
+CREATE INDEX aliases_id_ndx ON aliases(email);
+CREATE TABLE cookies (
+ cookuuid TEXT UNIQUE NOT NULL,
+ email TEXT,
+ expire TIMESTAMP(0) WITH TIME ZONE
+ );
+CREATE TABLE admins (
+ email TEXT
+ );
+CREATE FUNCTION updpass()
+  RETURNS trigger AS $$
+  BEGIN
+    IF NEW.password IS NULL OR
+       length(trim(NEW.password)) = 0 OR
+       substring(NEW.password,1,1) = '$' THEN
+      NEW.hash=NULL;
+      NEW.password=NEW.password;
+    ELSE
+      NEW.hash = md5(concat(NEW.email,':','mailleur-email',':',NEW.password));
+      NEW.password = crypt (NEW.password,gen_salt('md5'));
+    END IF;
+  RETURN NEW;
+  END;
+  $$ LANGUAGE 'plpgsql';
+CREATE TRIGGER keephash
+ BEFORE INSERT OR UPDATE on emails
+ FOR EACH ROW EXECUTE PROCEDURE updpass();
diff --git a/test_area/usr/share/mailleur/sql/mailleur.sql b/test_area/usr/share/mailleur/sql/mailleur.sql
new file mode 100644 (file)
index 0000000..bd7faed
--- /dev/null
@@ -0,0 +1,232 @@
+/********************************************************/
+/*                                                     */
+/*     This file define the mailleur data base         */
+/*     structure                                       */
+/*                                                     */
+/********************************************************/
+/*Specific PostgreSQL data base definitions            */
+#ifdef POSTGRESQL
+#define AUTOSERIAL     SERIAL UNIQUE
+#define DURATION       TIMESPAN
+#define DBTIMESTAMP    TIMESTAMP(0) WITH TIME ZONE
+#define DBNAN          'NaN'
+#define        DFLT            DEFAULT
+#define        SHORTTEXT       TEXT
+#define        TEXTUNIQUE      TEXT UNIQUE NOT NULL
+#define        INS(name)       name            /*index size    */
+#define        ESCLIT          E
+#define        mailapache      apache
+#define        maildove        dovecot
+#define        CASESENSITIVE
+#define ADDTIME(adding)        'adding'
+#define        INET            inet
+#define CRYPTED         bytea
+#endif
+
+/*Specific MySQL type data base definitions                    */
+#if defined(MYSQL) || defined(MYSQLI)
+#define AUTOSERIAL     INTEGER AUTO_INCREMENT UNIQUE PRIMARY KEY
+#define DURATION       INT8
+#define DBTIMESTAMP    TIMESTAMP NULL
+#define DBNAN          NULL
+#define        DFLT            DEFAULT
+#define        SHORTTEXT       VARCHAR(30)
+#define        TEXTUNIQUE      VARCHAR(100) UNIQUE NOT NULL
+#define        INS(name)       name(80)        /*index size    */
+#define        ESCLIT
+#define        mailapache      'apache'@'localhost'
+#define        maildove        'dovecot'@'localhost'
+#define        CASESENSITIVE   CHARACTER SET latin1 COLLATE latin1_bin
+#define ADDTIME(adding)        INTERVAL adding
+#define        INET            VARBINARY(16)
+#define CRYPTED         MEDIUMBLOB
+#endif
+
+/********************************************************/
+/********************************************************/
+/*                                                     */
+/*     User available email definition table           */
+/*                                                     */
+/********************************************************/
+//table to define LOCAL USER
+CREATE TABLE emails    (
+       email           TEXTUNIQUE,     //User email
+       creation        DBTIMESTAMP     //record creation
+                       DFLT NOW(),
+       lastemail       DBTIMESTAMP     //last email received
+                       DFLT NOW(),
+       lastscan        DBTIMESTAMP     //last scaning date
+                       DFLT NOW(),
+       password        TEXT            //user (encrypted) password
+                       DFLT '!',
+       hash            TEXT,           //'email:realm:password' MD5
+       lang            TEXT            //user prefered language
+                       DFLT 'ENG',     //English by default
+       space           INTEGER         //space used by user email      
+                       DFLT 0,
+       mxspace         INTEGER         //Maximun space available
+                       DFLT 20000,     //to user (20 Gig).
+       locked          INTEGER         //The account is locked out
+                       DFLT 0
+       );
+GRANT SELECT,INSERT,UPDATE,DELETE      ON emails TO mailapache;
+GRANT SELECT                           ON emails TO maildove;
+
+
+//table about session
+CREATE TABLE sessions  (
+       creation        DBTIMESTAMP     //record creation
+                       DFLT NOW(),
+       sessid          TEXTUNIQUE,     //Session id
+       sesstitle       TEXT,           //The session email title
+       sessfrom        TEXT,           //The session from
+       emailfrom       TEXT,           //The from within the email itself
+       taille          INTEGER         //The email size
+                       DFLT 0,
+       duration        TEXT            //email transfer duration
+                       DFLT NULL
+       );
+GRANT SELECT                           ON sessions TO mailapache;
+
+//table about recipient email information
+//summerize all emails received actions status according sessions and recipient
+CREATE TABLE actions   (
+       creation        DBTIMESTAMP     //record creation
+                       DFLT NOW(),
+       code            SHORTTEXT,      //action status (one char)
+       sessid          TEXT,           //Session id
+       remoteip        TEXT            //Remote serveur IP number
+                       DFLT NULL,
+       reverse         TEXT            //remote server reverse address name
+                       DFLT 'Unknown IP',
+       smtpfrom        TEXT,           //SMTP Connection "MAIL FROM:"
+       rcptto          TEXT,           //SMTP Connection "RCPT TO:"
+       emailfrom       TEXT,           //email contents "From:"
+       subject         TEXT,           //email contents "Subject:"
+       status          INTEGER,        //info status
+       numline         INTEGER,        //status line
+       info            TEXT            //line info
+       );
+GRANT SELECT                           ON sessions TO mailapache;
+
+//defining table about remote server
+CREATE TABLE remotes   (
+       remoteip        TEXTUNIQUE,     //remote IP number
+       lastscan        DBTIMESTAMP     //record last update
+                       DFLT '1970-01-01',
+       lastupdate      DBTIMESTAMP     //record creation
+                       DFLT NOW(),
+       credit          INTEGER         //Remote IP current credit (-100..+100)
+                       DFLT 5,
+       reverse         TEXT            //Remote official revers address
+                       DFLT NULL,
+       listing         TEXT            //explaination about black listing
+                       DFLT NULL,
+       links           INTEGER
+                       DFLT 1          //how many time the remote connected
+       );
+CREATE INDEX remotes_ndx ON remotes(lastscan,lastupdate);
+//set a local IP
+INSERT INTO remotes (remoteip,reverse,credit,lastscan)         \
+         values ('127.0.0.1','localhost.localldomain',99,now());
+
+//defining table about eventlog reference
+CREATE TABLE events    (
+       creation        DBTIMESTAMP     //record creation
+                       DFLT NOW(),
+       serial          AUTOSERIAL,     //record unique number
+       sessid          TEXTUNIQUE,     //Session id
+       start           INTEGER,        //Event log starting point
+       stop            INTEGER         //Event log ending point
+       );
+CREATE INDEX event_id_ndx ON events(sessid);
+CREATE INDEX event_date_ndx ON events(creation);
+
+
+//defining table about email aliases
+//list of aliases related to ONE email
+CREATE TABLE aliases   (
+       email           TEXT,           //Email address
+       alias           TEXT            //One aliases
+       );
+CREATE INDEX aliases_id_ndx ON aliases(email);
+
+//List all currently opened cookies
+CREATE TABLE cookies   (
+       cookuuid        TEXTUNIQUE,     //cookies unique id
+       email           TEXT,           //user email address
+       expire          DBTIMESTAMP     //cookies date limit
+       );
+
+//List emails address with admin capablities
+CREATE TABLE admins    (
+       email           TEXT            //user email address
+       );
+
+//--------------------------------------------------------------
+//Defining TRIGGER fonctions according database type
+//--------------------------------------------------------------
+//Caution this REALM value MUST be the same as defined
+//in mailleur.conf
+#define        REALM   'mailleur-email'
+#define        HASHING concat(NEW.email,':',REALM,':',NEW.password)
+
+
+#ifdef POSTGRESQL
+CREATE FUNCTION updpass()
+  RETURNS trigger AS $$
+  BEGIN
+    IF NEW.password IS NULL OR 
+       length(trim(NEW.password)) = 0 OR
+       substring(NEW.password,1,1) = '$' THEN
+      NEW.hash=NULL;
+      NEW.password=NEW.password;
+    ELSE
+      NEW.hash = md5(HASHING);
+      NEW.password = crypt (NEW.password,gen_salt('md5'));
+    END IF;
+  RETURN NEW;
+  END;
+  $$ LANGUAGE 'plpgsql';
+
+CREATE TRIGGER keephash
+       BEFORE INSERT OR UPDATE on emails
+       FOR EACH ROW EXECUTE PROCEDURE updpass();
+#endif
+
+#if defined(MYSQL) || defined(MYSQLI)
+
+DELIMITER $$
+
+CREATE TRIGGER sethash 
+  BEFORE UPDATE ON emails FOR EACH ROW
+  BEGIN 
+    IF NEW.password IS NULL OR 
+       LENGTH(TRIM(NEW.password))=0 OR
+       LEFT(NEW.password,1) = '$' THEN
+      set NEW.hash=NULL;
+      set NEW.password=NEW.password;
+    else 
+      set NEW.hash=MD5(HASHING);
+      set NEW.password=encrypt(NEW.password,concat('$1$',md5(rand())));
+    END IF;
+  END$$
+    
+  
+CREATE TRIGGER updhash
+  BEFORE INSERT ON emails FOR EACH ROW
+  BEGIN
+    IF NEW.password IS NULL OR 
+       LENGTH(TRIM(NEW.password))=0 OR
+       LEFT(NEW.password,1) = '$' THEN
+      set NEW.hash=NULL;
+      set NEW.password=NEW.password;
+    else 
+      set NEW.hash=MD5(HASHING);
+      set NEW.password=encrypt(NEW.password,concat('$1$',md5(rand())));
+    END IF;
+  END$$
+
+DELIMITER ;
+
+#endif
diff --git a/test_area/usr/share/mailleur/sql/preset.sql b/test_area/usr/share/mailleur/sql/preset.sql
new file mode 100644 (file)
index 0000000..7088111
--- /dev/null
@@ -0,0 +1,27 @@
+
+/********************************************************/
+/*                                                     */
+/*     This file define the mailleur data base         */
+/*     local preset value                              */
+/*                                                     */
+/********************************************************/
+
+/********************************************************/
+/*                                                     */
+/*     Creating the bare minimun user configuration    */
+/*                                                     */
+/********************************************************/
+//root is the default preset admin
+INSERT INTO admins (email)
+       VALUES (ROOTDOMAINNAME);
+
+//inserting the mail account
+INSERT INTO emails (email,password)
+       VALUES (ROOTDOMAINNAME,RPASS);
+
+/********************************************************/
+/*                                                     */
+/*     Preset value completed                          */
+/*                                                     */
+/********************************************************/
+
diff --git a/test_area/var/run/mailleur/receiver.lock b/test_area/var/run/mailleur/receiver.lock
new file mode 100644 (file)
index 0000000..50c88fb
--- /dev/null
@@ -0,0 +1 @@
+23289
diff --git a/test_area/var/run/mailleur/scanner.lock b/test_area/var/run/mailleur/scanner.lock
new file mode 100644 (file)
index 0000000..8bba3a5
--- /dev/null
@@ -0,0 +1 @@
+23299
diff --git a/test_area/var/run/mailleur/sender.lock b/test_area/var/run/mailleur/sender.lock
new file mode 100644 (file)
index 0000000..a468ec4
--- /dev/null
@@ -0,0 +1 @@
+23303
diff --git a/test_area/var/run/mailleur/sorter.lock b/test_area/var/run/mailleur/sorter.lock
new file mode 100644 (file)
index 0000000..a6d3a20
--- /dev/null
@@ -0,0 +1 @@
+23285
diff --git a/test_area/var/spool/mailleur/mails/example.com/postmaster/dovecot/new/23292-20251210053923-0979-0000 b/test_area/var/spool/mailleur/mails/example.com/postmaster/dovecot/new/23292-20251210053923-0979-0000
new file mode 100644 (file)
index 0000000..a14f0b7
--- /dev/null
@@ -0,0 +1,20 @@
+Received: from localhost.localdomain (IP=[127.0.0.1], originator=<postmaster@example.com>)
+       by mailleur.example.com ([127.127.10.25:1025]/receiver-0.18.30-dvl) with ESMTP
+       id <23292-20251210053923-0979-0000@mailleur.example.com>;
+       Wed, 10 Dec 2025 05:39:24 -0500
+Subject: (feed000) Very Simple email contents
+Date: Wed, 10 Dec 2025 05:39:24 -0500
+From: Maitre Post <postmaster@example.com>
+To: Maitre WEB <webmaster@example.com>
+
+this email is for local test purpose only
+with 2 known recipients beeing as:
+postmaster and webmaster.
+
+Checking line with one '.' transmissioon
+.
+Previous line is one single dot
+
+you should have character '|' as last characeter 
+of this email (after End Of Text).
+End Of Text|
diff --git a/test_area/var/spool/mailleur/mails/example.com/utf8-áö_üñ/dovecot/new/23387-20251210053926-0983-0000 b/test_area/var/spool/mailleur/mails/example.com/utf8-áö_üñ/dovecot/new/23387-20251210053926-0983-0000
new file mode 100644 (file)
index 0000000..cfebc3b
--- /dev/null
@@ -0,0 +1,15 @@
+Received: from localhost.localdomain (IP=[127.0.0.1], originator=<postmaster@example.com>)
+       by mailleur.example.com ([127.127.10.25:1025]/receiver-0.18.30-dvl) with ESMTP
+       id <23387-20251210053926-0983-0000@mailleur.example.com>;
+       Wed, 10 Dec 2025 05:39:27 -0500
+Subject: (feed004) Email using UTF-8 characters
+Date: Wed, 10 Dec 2025 05:39:27 -0500
+From: Maitre Post <postmaster@example.com>
+To: Maitre WEB <webmaster@example.com>
+
+this email is for local test purpose only
+with 2 known recipients beeing as:
+postmaster and webmaster.
+
+This line include accentued latin char
+via the reciption email address <áöüñ@example.com>
diff --git a/test_area/var/spool/mailleur/mails/example.com/webmaster/dovecot/new/23292-20251210053923-0979-0000 b/test_area/var/spool/mailleur/mails/example.com/webmaster/dovecot/new/23292-20251210053923-0979-0000
new file mode 100644 (file)
index 0000000..a14f0b7
--- /dev/null
@@ -0,0 +1,20 @@
+Received: from localhost.localdomain (IP=[127.0.0.1], originator=<postmaster@example.com>)
+       by mailleur.example.com ([127.127.10.25:1025]/receiver-0.18.30-dvl) with ESMTP
+       id <23292-20251210053923-0979-0000@mailleur.example.com>;
+       Wed, 10 Dec 2025 05:39:24 -0500
+Subject: (feed000) Very Simple email contents
+Date: Wed, 10 Dec 2025 05:39:24 -0500
+From: Maitre Post <postmaster@example.com>
+To: Maitre WEB <webmaster@example.com>
+
+this email is for local test purpose only
+with 2 known recipients beeing as:
+postmaster and webmaster.
+
+Checking line with one '.' transmissioon
+.
+Previous line is one single dot
+
+you should have character '|' as last characeter 
+of this email (after End Of Text).
+End Of Text|
diff --git a/test_area/var/spool/mailleur/mails/example.com/webmaster/dovecot/new/23383-20251210053924-0903-0000 b/test_area/var/spool/mailleur/mails/example.com/webmaster/dovecot/new/23383-20251210053924-0903-0000
new file mode 100644 (file)
index 0000000..bb6ce5e
--- /dev/null
@@ -0,0 +1,12 @@
+Received: from localhost.localdomain (IP=[127.0.0.1], originator=<trouble@mailref1.example.com>)
+       by mailleur.example.com ([127.127.10.25:1025]/receiver-0.18.30-dvl) with ESMTP
+       id <23383-20251210053924-0903-0000@mailleur.example.com>;
+       Wed, 10 Dec 2025 05:39:25 -0500
+Subject: (feed002) Very Simple email contents 2 domains
+From: Maitre Post <postmaster@example.com>
+To: Maitre WEB <webmaster@example.com>
+
+.single dot, next is an empty line starting with a '.'
+.
+This email is sent to 3 domain X 1 users
+
diff --git a/test_area/var/spool/mailleur/mails/example.com/webmaster/dovecot/new/23396-20251210053928-0663-0000 b/test_area/var/spool/mailleur/mails/example.com/webmaster/dovecot/new/23396-20251210053928-0663-0000
new file mode 100644 (file)
index 0000000..5c42ea2
--- /dev/null
@@ -0,0 +1,12 @@
+Received: from keeper-us-east-1d.mxtoolbox.com (IP=[18.209.86.113], originator=<supertool@mxtoolboxsmtpdiag.com>)
+       by mailleur.example.com ([127.127.10.25:1025]/receiver-0.18.30-dvl) with ESMTP
+       id <23396-20251210053928-0663-0000@mailleur.example.com>;
+       Wed, 10 Dec 2025 05:39:32 -0500
+Subject: (feed008) sending email to local only
+From: Maitre Post <postmaster@example.com>
+To: Maitre WEB <webmaster@example.com>
+
+.single dot, next is an empty line starting with a '.'
+.
+This email is sent to 3 domain X 1 users
+
diff --git a/test_area/var/spool/mailleur/mails/mailleur.example.com/user1/dovecot/new/23398-20251210053932-0389-0000 b/test_area/var/spool/mailleur/mails/mailleur.example.com/user1/dovecot/new/23398-20251210053932-0389-0000
new file mode 100644 (file)
index 0000000..e889dab
--- /dev/null
@@ -0,0 +1,11 @@
+Received: from mail-ua1-f45.google.com (IP=[209.85.222.45], originator=<nobody@gmail.com>)
+       by mailleur.example.com ([127.127.10.25:1025]/receiver-0.18.30-dvl) with ESMTP
+       id <23398-20251210053932-0389-0000@mailleur.example.com>;
+       Wed, 10 Dec 2025 05:39:32 -0500
+Subject: (feed008) sending email to local only
+From: A Nobody <nobody@gmail.com>
+To: A simple very local user <user1@mailleur.example.com>
+
+this ia an email "from" gmail.
+test text text
+
diff --git a/test_area/var/spool/mailleur/receiver-logs/event-20251210.jrl b/test_area/var/spool/mailleur/receiver-logs/event-20251210.jrl
new file mode 100644 (file)
index 0000000..f0b6633
--- /dev/null
@@ -0,0 +1,342 @@
+
+#-----------------------------
+#Wed Dec 10 05:39:23 2025
+05:39:23.981 Start CNT=23292-20251210053923-0979
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:23 -0500
+             HELO example.com
+     +00.089 250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+             MAIL FROM: <postmaster@example.com>
+     +00.132 250 2.1.3 postmaster@example.com sender ok
+             RCPT TO: <webmaster@example.com>
+     +00.176 250 5.6.7 Address will be processed <webmaster@example.com>
+             RCPT TO: <postmaster@example.com>
+     +00.219 250 5.6.7 Address will be processed <postmaster@example.com>
+             DATA
+     +00.260 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.301 250-3.5.3 Session ID=<23292-20251210053923-0979-0000>
+     +00.301 250-3.5.3 data stream received: 1 Kbytes within 0.041 seconds)
+     +00.301 250 3.5.3 Message accepted for delivery
+             QUIT
+     +00.342 221 2.0.0 Bye, closing connection CNT=23292-20251210053923-0979
+     +00.342 (Contact terminated, condition=<QUIT received>)
+     +00.344 (127.0.0.1        RVS='localhost.localdomain'     delta='  2' -> new credit='101')
+
+#-----------------------------
+#Wed Dec 10 05:39:24 2025
+05:39:24.333 Start CNT=23294-20251210053924-0332
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:24 -0500
+             EHLO example.com
+     +00.088 250-mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+     +00.088 250-STARTTLS
+     +00.088 250-AUTH DIGEST-MD5
+     +00.088 250-SIZE 52428800
+     +00.088 250-8BITMIME
+     +00.088 250 ENHANCEDSTATUSCODES
+             starttls
+     +00.230 220 2.0.0 Ready to start TLS
+             (crypted link is now set in 'server' mode, security level='2')
+             EHLO example.com
+     +00.297 250-mailleur.example.com, link (crypted) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+     +00.297 250-AUTH PLAIN LOGIN DIGEST-MD5
+     +00.297 250-SIZE 52428800
+     +00.297 250-8BITMIME
+     +00.297 250 ENHANCEDSTATUSCODES
+             MAIL FROM: <postmaster@example.com> SIZE=1024
+     +00.346 250 2.1.3 postmaster@example.com sender ok
+             RCPT TO: <user1@mailref1.example.com>
+     +00.389 250 5.6.7 Address will be processed <user1@mailref1.example.com>
+             RCPT TO: <user2@mailref1.example.com>
+     +00.433 250 5.6.7 Address will be processed <user2@mailref1.example.com>
+             DATA
+     +00.474 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.515 250-3.5.3 Session ID=<23294-20251210053924-0332-0000>
+     +00.515 250-3.5.3 data stream received: 1 Kbytes within 0.041 seconds)
+     +00.515 250 3.5.3 Message accepted for delivery
+             QUIT
+     +00.558 221 2.0.0 Bye, closing connection CNT=23294-20251210053924-0332
+     +00.558 (Contact terminated, condition=<QUIT received>)
+     +00.560 (127.0.0.1        RVS='localhost.localdomain'     delta='  2' -> new credit='103')
+
+#-----------------------------
+#Wed Dec 10 05:39:24 2025
+05:39:24.905 Start CNT=23383-20251210053924-0903
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:24 -0500
+             HELO example.com
+     +00.088 250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+             MAIL FROM: <trouble@mailref1.example.com>
+     +00.130 250 2.1.3 trouble@mailref1.example.com sender ok
+             RCPT TO: <webmaster@example.com>
+     +00.172 250 5.6.7 Address will be processed <webmaster@example.com>
+             RCPT TO: <user1@mailref1.example.com>
+     +00.215 250 5.6.7 Address will be processed <user1@mailref1.example.com>
+             RCPT TO: <user1@posdb.example.com>
+     +00.258 250 5.6.7 Address will be processed <user1@posdb.example.com>
+             RCPT TO: <user1@mardb.example.com>
+     +00.301 250 5.6.7 Address will be processed <user1@mardb.example.com>
+             DATA
+     +00.342 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.383 250-3.5.3 Session ID=<23383-20251210053924-0903-0000>
+     +00.383 250-3.5.3 data stream received: 1 Kbytes within 0.041 seconds)
+     +00.383 250 3.5.3 Message accepted for delivery
+             QUIT
+     +00.424 221 2.0.0 Bye, closing connection CNT=23383-20251210053924-0903
+     +00.424 (Contact terminated, condition=<QUIT received>)
+     +00.425 (127.0.0.1        RVS='localhost.localdomain'     delta='  2' -> new credit='105')
+
+#-----------------------------
+#Wed Dec 10 05:39:25 2025
+05:39:25.339 Start CNT=23385-20251210053925-0338
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:25 -0500
+             HELO example.com
+     +00.088 250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+             MAIL FROM: <trouble@mailref1.example.com>
+     +00.129 250 2.1.3 trouble@mailref1.example.com sender ok
+             RCPT TO: <user1@mailref1.example.com>
+     +00.173 250 5.6.7 Address will be processed <user1@mailref1.example.com>
+             RCPT TO: <user2@mailref1.example.com>
+     +00.216 250 5.6.7 Address will be processed <user2@mailref1.example.com>
+             RCPT TO: <no-one@example.com>
+     +00.259 551 5.6.0 <no-one@example.com> unknown user
+             DATA
+     +01.259 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +01.259 250-3.5.3 Session ID=<23385-20251210053925-0338-0000>
+     +01.259 250-3.5.3 data stream received: 1 Kbytes within 0.000 seconds)
+     +01.259 250 3.5.3 Message accepted for delivery
+             RSET
+     +01.300 250-2.1.0 flushed session 23385-20251210053925-0338-0000
+     +01.301 250 2.1.0 opening new session 23385-20251210053925-0338-0001
+             HELO example.com
+     +01.382 250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+             MAIL FROM: <postmaster@mailref1.example.com>
+     +01.423 250 2.1.3 postmaster@mailref1.example.com sender ok
+             RCPT TO: <user2@mailref1.example.com>
+     +01.467 250 5.6.7 Address will be processed <user2@mailref1.example.com>
+             RCPT TO: <user1@mailref1.example.com>
+     +01.510 250 5.6.7 Address will be processed <user1@mailref1.example.com>
+             DATA
+     +01.551 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +01.592 250-3.5.3 Session ID=<23385-20251210053925-0338-0001>
+     +01.592 250-3.5.3 data stream received: 1 Kbytes within 0.041 seconds)
+     +01.592 250 3.5.3 Message accepted for delivery
+             QUIT
+     +01.633 221 2.0.0 Bye, closing connection CNT=23385-20251210053925-0338
+     +01.633 (Contact terminated, condition=<QUIT received>)
+     +01.635 (127.0.0.1        RVS='localhost.localdomain'     delta='  2' -> new credit='107')
+
+#-----------------------------
+#Wed Dec 10 05:39:26 2025
+05:39:26.985 Start CNT=23387-20251210053926-0983
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:26 -0500
+             HELO example.com
+     +00.089 250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+             MAIL FROM: <postmaster@example.com>
+     +00.133 250 2.1.3 postmaster@example.com sender ok
+             RCPT TO: <utf8-áö_üñ@example.com>
+     +00.176 250 5.6.7 Address will be processed <utf8-áö_üñ@example.com>
+             DATA
+     +00.217 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.258 250-3.5.3 Session ID=<23387-20251210053926-0983-0000>
+     +00.258 250-3.5.3 data stream received: 1 Kbytes within 0.041 seconds)
+     +00.258 250 3.5.3 Message accepted for delivery
+             QUIT
+     +00.299 221 2.0.0 Bye, closing connection CNT=23387-20251210053926-0983
+     +00.299 (Contact terminated, condition=<QUIT received>)
+     +00.301 (127.0.0.1        RVS='localhost.localdomain'     delta='  2' -> new credit='109')
+
+#-----------------------------
+#Wed Dec 10 05:39:27 2025
+05:39:27.295 Start CNT=23390-20251210053927-0293
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:27 -0500
+             ORGN: 213.209.157.107
+     +00.091 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:27 -0500
+             helo example.com
+     +00.133 250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[213.209.157.107/No.Reverse]
+             MAIL FROM: <trouble@mailref1.example.com>
+             (Remote No.Reverse[213.209.157.107] credit='-5' too low)
+     +00.174 556-4.5.7 Originator server IP [213.209.157.107] black listed <credit too low>
+     +00.174 556 5.5.4 Closing connection
+     +00.175 (Contact terminated, condition=<Remote server credit is too low>)
+     +00.176 (213.209.157.107  RVS='No.Reverse'        delta=' -1' -> new credit=' -6')
+
+#-----------------------------
+#Wed Dec 10 05:39:27 2025
+05:39:27.483 Start CNT=23392-20251210053927-0482
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:27 -0500
+             ORGN: 213.209.157.108
+     +00.091 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:27 -0500
+             starttls
+     +00.234 220 2.0.0 Ready to start TLS
+             (crypted link is now set in 'server' mode, security level='2')
+             EHLO example.com
+     +00.300 250-mailleur.example.com, link (crypted) ready, your IP/FQDN=[213.209.157.108/No.Reverse]
+     +00.300 250-AUTH PLAIN LOGIN DIGEST-MD5
+     +00.300 250-SIZE 52428800
+     +00.300 250-8BITMIME
+     +00.301 250 ENHANCEDSTATUSCODES
+             AUTH LOGIN
+     +00.346 334 VXNlcm5hbWU6
+             dXRmOC3DocO2X8O8w7FAZXhhbXBsZS5jb20=
+     +00.388 334 UGFzc3dvcmQ6
+             dXRmOC3DocO2X8O8w7E=
+     +00.429 ---- Auth accepted for user=<utf8-áö_üñ@example.com>
+     +00.430 235 5.7.5 authentication successful
+             MAIL FROM: <trouble@mailref1.example.com>
+     +00.472 250 2.1.3 trouble@mailref1.example.com sender ok
+             RCPT TO: <user1@posdb.example.com>
+     +00.515 250 5.6.7 Address will be processed <user1@posdb.example.com>
+             DATA
+     +00.558 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.599 250-3.5.3 Session ID=<23392-20251210053927-0482-0000>
+     +00.599 250-3.5.3 data stream received: 1 Kbytes within 0.041 seconds)
+     +00.599 250 3.5.3 Message accepted for delivery
+             QUIT
+     +00.642 221 2.0.0 Bye, closing connection CNT=23392-20251210053927-0482
+     +00.642 (Contact terminated, condition=<QUIT received>)
+     +00.644 (213.209.157.108  RVS='No.Reverse'        delta='  6' -> new credit='  1')
+
+#-----------------------------
+#Wed Dec 10 05:39:28 2025
+05:39:28.137 Start CNT=23394-20251210053928-0135
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:28 -0500
+             ORGN: 192.168.254.25
+     +00.091 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:28 -0500
+             starttls
+     +00.233 220 2.0.0 Ready to start TLS
+             (crypted link is now set in 'server' mode, security level='2')
+             EHLO example.com
+     +00.299 250-mailleur.example.com, link (crypted) ready, your IP/FQDN=[192.168.254.25/No.Reverse]
+     +00.299 250-AUTH PLAIN LOGIN DIGEST-MD5
+     +00.299 250-SIZE 52428800
+     +00.300 250-8BITMIME
+     +00.300 250 ENHANCEDSTATUSCODES
+             MAIL FROM: <trouble@mailref1.example.com>
+     +00.346 250 2.1.3 trouble@mailref1.example.com sender ok
+             RCPT TO: <user1@posdb.example.com>
+     +00.389 250 5.6.7 Address will be processed <user1@posdb.example.com>
+             DATA
+     +00.431 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.472 250-3.5.3 Session ID=<23394-20251210053928-0135-0000>
+     +00.472 250-3.5.3 data stream received: 1 Kbytes within 0.041 seconds)
+     +00.472 250 3.5.3 Message accepted for delivery
+             QUIT
+     +00.515 221 2.0.0 Bye, closing connection CNT=23394-20251210053928-0135
+     +00.515 (Contact terminated, condition=<QUIT received>)
+     +00.517 (192.168.254.25   RVS='No.Reverse'        delta='  2' -> new credit='  7')
+
+#-----------------------------
+#Wed Dec 10 05:39:28 2025
+05:39:28.665 Start CNT=23396-20251210053928-0663
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:28 -0500
+             ORGN: 18.209.86.113
+     +00.209 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:28 -0500
+             EHLO keeper-us-east-1d.mxtoolbox.com
+     +00.209 250-mailleur.example.com, link (cleartext) ready, your IP/FQDN=[18.209.86.113/keeper-us-east-1d.mxtoolbox.com]
+     +00.209 250-STARTTLS
+     +00.209 250-AUTH DIGEST-MD5
+     +00.209 250-SIZE 52428800
+     +00.209 250-8BITMIME
+     +00.209 250 ENHANCEDSTATUSCODES
+             MAIL FROM:<supertool@mxtoolboxsmtpdiag.com>
+     +00.380 250 2.1.3 supertool@mxtoolboxsmtpdiag.com sender ok
+             RCPT TO: <webmaster@example.com>
+     +00.382 250 5.6.7 Address will be processed <webmaster@example.com>
+             RCPT TO:<test@mxtoolboxsmtpdiag.com>
+     +00.669 555 2.8.0 No MX nor IP for for domain <mxtoolboxsmtpdiag.com>
+             RCPT TO: <user1@posdb.example.com>
+     +01.673 555 2.8.0 No relay accepted for domain <posdb.example.com>
+             DATA
+     +03.673 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +03.673 250-3.5.3 Session ID=<23396-20251210053928-0663-0000>
+     +03.673 250-3.5.3 data stream received: 1 Kbytes within 0.000 seconds)
+     +03.673 250 3.5.3 Message accepted for delivery
+             QUIT
+     +03.714 221 2.0.0 Bye, closing connection CNT=23396-20251210053928-0663
+     +03.715 (Contact terminated, condition=<QUIT received>)
+     +03.717 (18.209.86.113    RVS='keeper-us-east-1d.mxtoolbox.com'   delta='  0' -> new credit='  5')
+
+#-----------------------------
+#Wed Dec 10 05:39:32 2025
+05:39:32.391 Start CNT=23398-20251210053932-0389
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:32 -0500
+             ORGN: 209.85.222.45
+     +00.090 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:32 -0500
+             EHLO keeper-us-east-1d.mxtoolbox.com
+     +00.131 250-mailleur.example.com, link (cleartext) ready, your IP/FQDN=[209.85.222.45/mail-ua1-f45.google.com]
+     +00.131 250-STARTTLS
+     +00.131 250-AUTH DIGEST-MD5
+     +00.131 250-SIZE 52428800
+     +00.131 250-8BITMIME
+     +00.131 250 ENHANCEDSTATUSCODES
+             MAIL FROM:<nobody@gmail.com> SIZE=2958
+     +00.266 250 2.1.3 nobody@gmail.com sender ok
+             RCPT TO:<user1@mailleur.example.com>
+     +00.269 250 5.6.7 Address will be processed <user1@mailleur.example.com>
+             DATA
+     +00.310 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.351 250-3.5.3 Session ID=<23398-20251210053932-0389-0000>
+     +00.351 250-3.5.3 data stream received: 1 Kbytes within 0.041 seconds)
+     +00.351 250 3.5.3 Message accepted for delivery
+             QUIT
+     +00.392 221 2.0.0 Bye, closing connection CNT=23398-20251210053932-0389
+     +00.392 (Contact terminated, condition=<QUIT received>)
+     +00.394 (209.85.222.45    RVS='mail-ua1-f45.google.com'   delta='  2' -> new credit='  7')
+
+#-----------------------------
+#Wed Dec 10 05:39:32 2025
+05:39:32.794 Start CNT=23404-20251210053932-0793
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:32 -0500
+             HELO      [192.219.254.34]     
+     +00.088 250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+             HELO      192.219.254.35     
+     +00.129 250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+             HELO          domain
+     +00.170 250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+             HELO 8.8.8.8
+     +00.212 250 mailleur.example.com, link (cleartext) ready, your IP/FQDN=[127.0.0.1/localhost.localdomain]
+             EHLO .com
+     +00.254 501 5.5.4 HELO argument is incorrect, closing connection.
+     +00.254 (Contact terminated, condition=<HELO parameter incorrect>)
+     +00.255 (127.0.0.1        RVS='localhost.localdomain'     delta=' -1' -> new credit='108')
+
+#-----------------------------
+#Wed Dec 10 05:39:33 2025
+05:39:33.159 Start CNT=23408-20251210053933-0158
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:33 -0500
+             EHLO domain..com
+     +00.002 501 5.5.4 HELO argument is incorrect, closing connection.
+     +00.002 (Contact terminated, condition=<HELO parameter incorrect>)
+     +00.003 (127.0.0.1        RVS='localhost.localdomain'     delta=' -1' -> new credit='107')
+
+#-----------------------------
+#Wed Dec 10 05:39:33 2025
+05:39:33.273 Start CNT=23410-20251210053933-0272
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:33 -0500
+             EHLO [.192.219.254.1]
+     +00.002 501 5.5.4 HELO argument is incorrect, closing connection.
+     +00.002 (Contact terminated, condition=<HELO parameter incorrect>)
+     +00.003 (127.0.0.1        RVS='localhost.localdomain'     delta=' -1' -> new credit='106')
+
+#-----------------------------
+#Wed Dec 10 05:39:33 2025
+05:39:33.287 Start CNT=23412-20251210053933-0286
+             (Contact open from [127.0.0.1] to [127.127.10.25:1025])
+     +00.001 220 mailleur.example.com, ESMTP (cleartext) mailleur-0.18.30-dvl; Wed, 10 Dec 2025 05:39:33 -0500
+             AUTH DIGEST-MD5
+     +00.088 334 cmVhbG09Im1haWxsZXVyLWVtYWlsIixub25jZT0iTlRRd1VGRlNSRTVyYlc4MmNHUm9aVmM9Iixxb3A9YXV0aCxhbGdvcml0aG09bWQ1LXNlc3MsY2hhcnNldD11dGYtOA==
+     +00.129 ---- Auth Rejected status='504' for user=<digi> pass=<$1(a_digest_md5)>
+     +02.130 504 5.7.4 wrong authentication
+     +02.131 (Contact terminated, condition=<Authentication failure>)
+     +02.132 (127.0.0.1        RVS='localhost.localdomain'     delta='  0' -> new credit='106')
diff --git a/test_area/var/spool/mailleur/scanner-logs/event-20251210.jrl b/test_area/var/spool/mailleur/scanner-logs/event-20251210.jrl
new file mode 100644 (file)
index 0000000..2e79902
--- /dev/null
@@ -0,0 +1,32 @@
+
+#-----------------------------
+#Wed Dec 10 05:39:29 2025
+#2 remote IP to scan
+05:39:29.591 IP[01]: 192.168.254.25    bl.spamcop.net -> NOT listed
+     +00.076 IP[01]: 192.168.254.25    sbl.spamhaus.org -> NOT listed
+     +00.183 IP[01]: 192.168.254.25    b.barracudacentral.org -> NOT listed
+     +00.460 IP[01]: 192.168.254.25    bl.blocklist.de -> NOT listed
+     +00.460 IP[01]: 192.168.254.25    ips.backscatterer.org -> NOT listed
+     +00.525 IP[01]: 192.168.254.25    hostkarma.junkemailfilter.co -> NOT listed
+     +00.525 IP[01]: 192.168.254.25    RVS='No.Reverse'        delta='  0' -> new credit='  7'
+             
+     +00.595 IP[02]: 18.209.86.113     bl.spamcop.net -> NOT listed
+     +00.664 IP[02]: 18.209.86.113     sbl.spamhaus.org -> NOT listed
+     +00.748 IP[02]: 18.209.86.113     b.barracudacentral.org -> NOT listed
+     +00.987 IP[02]: 18.209.86.113     bl.blocklist.de -> NOT listed
+     +00.987 IP[02]: 18.209.86.113     ips.backscatterer.org -> NOT listed
+     +01.111 IP[02]: 18.209.86.113     hostkarma.junkemailfilter.co -> NOT listed
+     +01.111 IP[02]: 18.209.86.113     RVS='keeper-us-east-1d.mxtoolbox.com'   delta='  0' -> new credit='  5'
+             
+
+#-----------------------------
+#Wed Dec 10 05:39:36 2025
+#1 remote IP to scan
+05:39:36.787 IP[01]: 209.85.222.45     bl.spamcop.net -> NOT listed
+     +00.060 IP[01]: 209.85.222.45     sbl.spamhaus.org -> NOT listed
+     +00.146 IP[01]: 209.85.222.45     b.barracudacentral.org -> NOT listed
+     +00.388 IP[01]: 209.85.222.45     bl.blocklist.de -> NOT listed
+     +00.388 IP[01]: 209.85.222.45     ips.backscatterer.org -> NOT listed
+     +00.459 IP[01]: 209.85.222.45     hostkarma.junkemailfilter.co -> NOT listed
+     +00.459 IP[01]: 209.85.222.45     RVS='mail-ua1-f45.google.com'   delta='  0' -> new credit='  7'
+             
diff --git a/test_area/var/spool/mailleur/scanner-logs/event-20251211.jrl b/test_area/var/spool/mailleur/scanner-logs/event-20251211.jrl
new file mode 100644 (file)
index 0000000..bdddece
--- /dev/null
@@ -0,0 +1,32 @@
+
+#-----------------------------
+#Thu Dec 11 05:37:50 2025
+#2 remote IP to scan
+05:37:50.625 IP[01]: 213.209.157.108   bl.spamcop.net -> NOT listed
+     +00.143 IP[01]: 213.209.157.108   sbl.spamhaus.org -> Listed by SBL, see https://check.spamhaus.org/sbl/query/SBL689956
+     +00.227 IP[01]: 213.209.157.108   b.barracudacentral.org -> NOT listed
+     +00.623 IP[01]: 213.209.157.108   bl.blocklist.de -> NOT listed
+     +00.923 IP[01]: 213.209.157.108   ips.backscatterer.org -> NOT listed
+     +01.052 IP[01]: 213.209.157.108   hostkarma.junkemailfilter.co -> NOT listed
+     +01.052 IP[01]: 213.209.157.108   RVS='No.Reverse'        delta=' -1' -> new credit='  0'
+             
+     +01.128 IP[02]: 127.0.0.1 bl.spamcop.net -> NOT listed
+     +01.179 IP[02]: 127.0.0.1 sbl.spamhaus.org -> NOT listed
+     +01.264 IP[02]: 127.0.0.1 b.barracudacentral.org -> NOT listed
+     +01.504 IP[02]: 127.0.0.1 bl.blocklist.de -> NOT listed
+     +01.650 IP[02]: 127.0.0.1 ips.backscatterer.org -> NOT listed
+     +01.750 IP[02]: 127.0.0.1 hostkarma.junkemailfilter.co -> NOT listed
+     +01.750 IP[02]: 127.0.0.1 RVS='localhost' delta='  0' -> new credit='106'
+             
+
+#-----------------------------
+#Thu Dec 11 05:39:34 2025
+#1 remote IP to scan
+05:39:35.075 IP[01]: 18.209.86.113     bl.spamcop.net -> NOT listed
+     +00.067 IP[01]: 18.209.86.113     sbl.spamhaus.org -> NOT listed
+     +00.179 IP[01]: 18.209.86.113     b.barracudacentral.org -> NOT listed
+     +00.419 IP[01]: 18.209.86.113     bl.blocklist.de -> NOT listed
+     +00.637 IP[01]: 18.209.86.113     ips.backscatterer.org -> NOT listed
+     +00.707 IP[01]: 18.209.86.113     hostkarma.junkemailfilter.co -> NOT listed
+     +00.707 IP[01]: 18.209.86.113     RVS='keeper-us-east-1d.mxtoolbox.com'   delta='  0' -> new credit='  5'
+             
diff --git a/test_area/var/spool/mailleur/sender-logs/event-20251210.jrl b/test_area/var/spool/mailleur/sender-logs/event-20251210.jrl
new file mode 100644 (file)
index 0000000..3d116bc
--- /dev/null
@@ -0,0 +1,172 @@
+
+#-----------------------------
+#Wed Dec 10 05:39:32 2025
+#main-session-id=23294-20251210053924-0332
+05:39:32.470 Now Connected to MX <smtp1.example.com>
+     +00.001 220 smtp1.example.com ESMTP clement-2.8.1-91; Wed, 10 Dec 2025 05:39:32 -0500
+             EHLO example.com
+     +00.006 250-smtp1.example.com Hello devel5.safe.ca [192.219.254.70], please to meet you
+     +00.047 250-ENHANCEDSTATUSCODES
+     +00.047 250-8BITMIME
+     +00.047 250-SIZE
+     +00.047 250-AUTH PLAIN LOGIN
+     +00.047 250-STARTTLS
+     +00.047 250 HELP
+             STARTTLS
+     +00.048 220 Ready to start TLS
+             (crypted link is now set in 'client' mode, security level='2')
+             EHLO example.com
+     +00.122 250-smtp1.example.com Hello devel5.safe.ca [192.219.254.70], please to meet you
+     +00.122 250-ENHANCEDSTATUSCODES
+     +00.122 250-8BITMIME
+     +00.122 250-SIZE
+     +00.122 250-AUTH PLAIN LOGIN
+     +00.122 250 HELP
+             MAIL FROM: <postmaster@example.com>
+     +00.128 250 <postmaster@example.com>... sender ok
+             RCPT TO: <user1@mailref1.example.com>
+     +00.730 250 Address accepted
+             RCPT TO: <user2@mailref1.example.com>
+     +00.736 250 Address accepted
+             DATA
+     +00.738 354 Enter mail, end with "." on a line by itself
+     +00.788 250 Message accepted for delivery (Session ID=<00737-20251210053932-0cc60ea7>)
+             Data stream Sent: 1 Kbytes within 0.052 seconds
+             Transmission completed successfully
+             RSET
+     +01.250 250 Reset state
+             MAIL FROM: <trouble@mailref1.example.com>
+     +01.254 250 <trouble@mailref1.example.com>... sender ok
+             RCPT TO: <user1@mailref1.example.com>
+     +01.259 250 Address accepted
+             DATA
+     +01.261 354 Enter mail, end with "." on a line by itself
+     +01.311 250 Message accepted for delivery (Session ID=<00737-20251210053933-0cc6138b>)
+             Data stream Sent: 1 Kbytes within 0.051 seconds
+             Transmission completed successfully
+             RSET
+     +01.486 250 Reset state
+             MAIL FROM: <trouble@mailref1.example.com>
+     +01.490 250 <trouble@mailref1.example.com>... sender ok
+             RCPT TO: <user1@mailref1.example.com>
+     +01.494 250 Address accepted
+             RCPT TO: <user2@mailref1.example.com>
+     +01.499 250 Address accepted
+             DATA
+     +01.501 354 Enter mail, end with "." on a line by itself
+     +01.551 250 Message accepted for delivery (Session ID=<00737-20251210053933-0cc61479>)
+             Data stream Sent: 1 Kbytes within 0.052 seconds
+             Transmission completed successfully
+             RSET
+     +01.906 250 Reset state
+             MAIL FROM: <postmaster@mailref1.example.com>
+     +01.910 250 <postmaster@mailref1.example.com>... sender ok
+             RCPT TO: <user2@mailref1.example.com>
+     +01.915 250 Address accepted
+             RCPT TO: <user1@mailref1.example.com>
+     +01.920 250 Address accepted
+             DATA
+     +01.922 354 Enter mail, end with "." on a line by itself
+     +01.972 250 Message accepted for delivery (Session ID=<00737-20251210053934-0cc6161e>)
+             Data stream Sent: 1 Kbytes within 0.051 seconds
+             Transmission completed successfully
+             QUIT
+     +02.045 221 smtp1.example.com closing connection
+
+#-----------------------------
+#Wed Dec 10 05:39:34 2025
+#main-session-id=23383-20251210053924-0903
+05:39:34.531 Now Connected to MX <mailpostg.example.com>
+     +00.002 220 mailpostg.example.com, ESMTP (cleartext) mailleur-0.13.88-dvl; Wed, 10 Dec 2025 05:39:34 -0500
+             EHLO mailref1.example.com
+     +00.003 250-mailpostg.example.com, link (cleartext) ready, your IP/FQDN=[192.219.254.70/devel5.safe.ca]
+     +00.003 250-STARTTLS
+     +00.003 250-AUTH DIGEST-MD5
+     +00.003 250-SIZE 52428800
+     +00.003 250-8BITMIME
+     +00.003 250 ENHANCEDSTATUSCODES
+             STARTTLS
+     +00.103 220 2.0.0 Ready to start TLS
+             (crypted link is now set in 'client' mode, security level='2')
+             EHLO mailref1.example.com
+     +00.148 250-mailpostg.example.com, link (crypted) ready, your IP/FQDN=[192.219.254.70/devel5.safe.ca]
+     +00.149 250-AUTH PLAIN LOGIN DIGEST-MD5
+     +00.150 250-SIZE 52428800
+     +00.151 250-8BITMIME
+     +00.153 250 ENHANCEDSTATUSCODES
+             MAIL FROM: <trouble@mailref1.example.com>
+     +00.154 250 2.1.3 trouble@mailref1.example.com.. sender ok
+             RCPT TO: <user1@posdb.example.com>
+     +00.156 250 2.6.4 Address accepted <user1@posdb.example.com>
+             DATA
+     +00.157 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.199 250-3.5.3 Session ID=<00522-20251210053934-0531-0000>
+     +00.200 250-3.5.3 data stream received: 1 Kbytes within 0.041 seconds)
+     +00.201 250 3.5.3 Message accepted for delivery
+             Data stream Sent: 1 Kbytes within 0.044 seconds
+             Transmission completed successfully
+             RSET
+     +00.202 250-2.1.0 flushed session 00522-20251210053934-0531-0000
+     +00.242 250 2.1.0 opening new session 00522-20251210053934-0531-0001
+             MAIL FROM: <trouble@mailref1.example.com>
+     +00.244 250 2.1.3 trouble@mailref1.example.com.. sender ok
+             RCPT TO: <user1@posdb.example.com>
+     +00.246 250 2.6.4 Address accepted <user1@posdb.example.com>
+             DATA
+     +00.247 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.289 250-3.5.3 Session ID=<00522-20251210053934-0531-0001>
+     +00.290 250-3.5.3 data stream received: 1 Kbytes within 0.042 seconds)
+     +00.291 250 3.5.3 Message accepted for delivery
+             Data stream Sent: 1 Kbytes within 0.045 seconds
+             Transmission completed successfully
+             RSET
+     +00.292 250-2.1.0 flushed session 00522-20251210053934-0531-0001
+     +00.333 250 2.1.0 opening new session 00522-20251210053934-0531-0002
+             MAIL FROM: <trouble@mailref1.example.com>
+     +00.334 250 2.1.3 trouble@mailref1.example.com.. sender ok
+             RCPT TO: <user1@posdb.example.com>
+     +00.336 250 2.6.4 Address accepted <user1@posdb.example.com>
+             DATA
+     +00.338 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.380 250-3.5.3 Session ID=<00522-20251210053934-0531-0002>
+     +00.381 250-3.5.3 data stream received: 1 Kbytes within 0.042 seconds)
+     +00.382 250 3.5.3 Message accepted for delivery
+             Data stream Sent: 1 Kbytes within 0.046 seconds
+             Transmission completed successfully
+             QUIT
+     +00.383 221 2.0.0 Bye, closing connection CNT=00522-20251210053934-0531
+
+#-----------------------------
+#Wed Dec 10 05:39:34 2025
+#main-session-id=23383-20251210053924-0903
+05:39:34.922 Now Connected to MX <mailmysql.example.com>
+     +00.026 220 mailmysql.example.com, ESMTP (cleartext) mailleur-0.13.95-dvl; Wed, 10 Dec 2025 05:39:34 -0500
+             EHLO mailref1.example.com
+     +00.026 250-mailmysql.example.com, link (cleartext) ready, your IP/FQDN=[192.219.254.70/devel5.safe.ca]
+     +00.026 250-STARTTLS
+     +00.026 250-AUTH DIGEST-MD5
+     +00.026 250-SIZE 52428800
+     +00.026 250-8BITMIME
+     +00.026 250 ENHANCEDSTATUSCODES
+             STARTTLS
+     +00.129 220 2.0.0 Ready to start TLS
+             (crypted link is now set in 'client' mode, security level='2')
+             EHLO mailref1.example.com
+     +00.197 250-mailmysql.example.com, link (crypted) ready, your IP/FQDN=[192.219.254.70/devel5.safe.ca]
+     +00.198 250-AUTH PLAIN LOGIN DIGEST-MD5
+     +00.200 250-SIZE 52428800
+     +00.201 250-8BITMIME
+     +00.202 250 ENHANCEDSTATUSCODES
+             MAIL FROM: <trouble@mailref1.example.com>
+     +00.205 250 2.1.3 trouble@mailref1.example.com.. sender ok
+             RCPT TO: <user1@mardb.example.com>
+     +00.212 250 2.6.4 Address accepted <user1@mardb.example.com>
+             DATA
+     +00.214 354 3.5.0 End data with <CR><LF>.<CR><LF>
+     +00.255 250-3.5.3 Session ID=<01634-20251210053934-0922-0000>
+     +00.257 250-3.5.3 data stream received: 1 Kbytes within 0.042 seconds)
+     +00.258 250 3.5.3 Message accepted for delivery
+             Data stream Sent: 1 Kbytes within 0.045 seconds
+             Transmission completed successfully
+             QUIT
+     +00.259 221 2.0.0 Bye, closing connection CNT=01634-20251210053934-0922
diff --git a/tools/DIGEST-MD5 b/tools/DIGEST-MD5
new file mode 100644 (file)
index 0000000..c797e0a
--- /dev/null
@@ -0,0 +1,88 @@
+Sequence pour "calculer la reponse digest-MD5
+#-----------------------------------------
+https://stackoverflow.com/questions/40505711/digest-md5-response-generation
+
+chris:elwood.innosoft.com:secret
+       -> eb5a750053e4d2c34aa84bbc9b0b6ee7
+
+A1:
+chris+secret                           nonce    cnonce
+eb5a750053e4d2c34aa84bbc9b0b6ee7:OA6MG9tEQGm2hh:OA6MHXh6VqTrRk
+       -> 54442ff1f394d9d0de1205cef4d9cebe
+
+A2  digest-uri="imap/elwood.innosoft.com"
+AUTHENTICATE:imap/elwood.innosoft.com
+       -> 15e3594677e51ade69715d1cb7d207ba
+
+RESPONSE
+       A1                        nonce         nc-value  cnonce               qpop    A2
+54442ff1f394d9d0de1205cef4d9cebe:OA6MG9tEQGm2hh:00000001:OA6MHXh6VqTrRk:auth:15e3594677e51ade69715d1cb7d207ba
+       -> 26ef1190b643a36e879673066098379c
+OK.
+#-----------------------------------------
+RFC-2831
+example 1 (B64)
+X      chris:elwood.innosoft.com:secret
+       Y -> eb5a750053e4d2c34aa84bbc9b0b6ee7
+
+A1
+chris+secret                           nonce    cnonce
+eb5a750053e4d2c34aa84bbc9b0b6ee7:OA6MG9tEQGm2hh:OA6MHXh6VqTrRk
+       HA1 -> 54442ff1f394d9d0de1205cef4d9cebe
+
+A2     digest-uri="imap/elwood.innosoft.com"
+AUTHENTICATE:imap/elwood.innosoft.com
+       HA2 -> 15e3594677e51ade69715d1cb7d207ba
+
+RESPONSE
+       HA1                       nonce         nc-value  cnonce               qpop    HA2
+54442ff1f394d9d0de1205cef4d9cebe:OA6MG9tEQGm2hh:00000001:OA6MHXh6VqTrRk:auth:15e3594677e51ade69715d1cb7d207ba
+       -> 26ef1190b643a36e879673066098379c
+expected-> d388dad90d4bbd760a152321f2143af7
+       NO Match
+#-------
+example 2 (ACAP)
+chris:elwood.innosoft.com:secret
+       -> eb5a750053e4d2c34aa84bbc9b0b6ee7
+A1
+chris+secret                           nonce    cnonce
+eb5a750053e4d2c34aa84bbc9b0b6ee7:OA9BSXrbuRhWay:OA9BSuZWMSpW8m
+       -> da2ad3976e8e37a03ecd844797a82ae7
+
+A2     digest-uri="acap/elwood.innosoft.com"
+AUTHENTICATE:acap/elwood.innosoft.com
+       -> c557ec746787450143826ac2c55cbabf
+
+RESPONSE
+       A1                        nonce         nc-value  cnonce               qpop    A2
+da2ad3976e8e37a03ecd844797a82ae7:OA9BSXrbuRhWay:00000001:OA9BSuZWMSpW8m:auth:c557ec746787450143826ac2c55cbabf
+       -> 90771dc5643a801bb9a9bcbb1ed3cd34
+               NO Match
+
+
+
+#-----------------------------------------
+#Essais avec mailleur (password xxx)
+webmaster@example.com:mailleur.example.com:xxx
+       -> 841ece3c65e16f381f58f3ee5c28dabc
+
+A1
+webmaster+secret                               nonce    cnonce
+841ece3c65e16f381f58f3ee5c28dabc:ABCDEF:0b5f7bf8d392423e355bd2e2375fcd25
+       HEX=38343165636533633635653136663338316635386633656535633238646162633A4142434445463A3062356637626638643339323432336533353562643265323337356663643235
+       HA1 -> 94a52c447501f0729d2f2a07dfde9c9e
+
+A2     digest-uri="smtp/127.127.10.25"
+AUTHENTICATE:smtp/127.127.10.25
+       HEX=41555448454E5449434154453A736D74702F3132372E3132372E31302E3235
+       HA2 -> df0b39e40b72a0f55dba5c2e153b0b7f
+
+
+RESPONSE
+       A1                        nonce         nc-value  cnonce               qpop    A2
+94a52c447501f0729d2f2a07dfde9c9e:ABCDEF:00000001:0b5f7bf8d392423e355bd2e2375fcd25:auth:df0b39e40b72a0f55dba5c2e153b0b7f
+       HEX=94a52c447501f0729d2f2a07dfde9c9e:ABCDEF:00000001:0b5f7bf8d392423e355bd2e2375fcd25:auth:df0b39e40b72a0f55dba5c2e153b0b7
+
+       -> 3eafcc0a2e5d920e4b3ee691b2cf9e15
+       NO Match
+       caaee0870f16e83f8176e0de724567a1
diff --git a/tools/DIGEST-MD5-1 b/tools/DIGEST-MD5-1
new file mode 100644 (file)
index 0000000..5339d11
--- /dev/null
@@ -0,0 +1,112 @@
+#-----------------------------------------
+#Essais avec mailleur (password xxx)
+webmaster@example.com:mailleur.example.com:xxx
+       -> 841ece3c65e16f381f58f3ee5c28dabc
+
+A1     007862:asterisk:1q2w3e
+       HA1 -> 64e5dd333209a9af67ec11d61b3c6479
+
+A2     REGISTER:sip:192.168.55.167:5060
+       HA2 -> 7df6ad58ee02df3e342fe72722bd624c
+
+
+RESPONSE
+       HA1                       nonce         nc-value  cnonce               qpop    HA2
+64e5dd333209a9af67ec11d61b3c6479:1612176383/ac153881bb7f5c7fafe2179aa4ed5617:00000001:CC77FDAA:auth:7df6ad58ee02df3e342fe72722bd624c
+       HRSP=611d34af182ff56ffec67a04ce8561e5   
+       Match
+
+#-------------------------------#
+X      webmaster@example.com:devel5.safe.ca:mailleur2
+       Y -> 139f041506766c07cab7dc7bd842e535
+
+A1     139f041506766c07cab7dc7bd842e535:ABCDEF:DjrYLBA3zOo=:
+       HA1 -> b4e0e53f97f1ec651567be200ded6216
+
+A2     AUTHENTICATE:smtp/devel5.safe.ca
+       HA2 -> 41054b020583decd30bce12313b60579
+
+       HA1                       nonce         nc-value  cnonce               qpop    HA2
+b4e0e53f97f1ec651567be200ded6216:ABCDEF:00000001:DjrYLBA3zOo=:auth:41054b020583decd30bce12313b60579
+
+#-------------------------------#
+X      webmaster@example.com:devel5.safe.ca:xxx
+       Y -> 9bf22611048932ad8661a24966fcc9da
+
+A1     60dbd555ac12ca306e2a011af850062f:abcdef:f4d9a4fdf85df7bc50d74ab893673f08
+       HA1 -> 2630ba89adf72bddf006949fc68be360
+       HEXA1 -> 3236333062613839616466373262646466303036393439666336386265333630
+
+A2     AUTHENTICATE:smtp/devel5.safe.ca
+       Ha2 -> d0b815ab45006cd0a0ae2a1e44c26311
+       HEXA2 -> 6430623831356162343530303663643061306165326131653434633236333131
+
+
+       HEXA1             nonce         nc-value  cnonce       qpop    HEXA2
+3236333062613839616466373262646466303036393439666336386265333630:abcdef:00000001:f4d9a4fdf85df7bc50d74ab893673f08:auth:6430623831356162343530303663643061306165326131653434633236333131
+       -> d975a253dbeb1463d5e81621c62de806
+       NO Match
+       Expected 1a23eaa7400ca40d732c5518df1c6bf6
+
+
+
+#-------------------------------#
+A1     webmaster@example.com:devel5.safe.ca:xxx
+       HA1 -> 60dbd555ac12ca306e2a011af850062f
+
+A2     AUTHENTICATE:smtp/devel5.safe.ca
+       HA2 -> d0b815ab45006cd0a0ae2a1e44c26311
+
+md5-sess
+MHA2   60dbd555ac12ca306e2a011af850062f:abcdef:491ea65a17ebf2375c6de7b89089a0c6
+       HMHA2-> 72926dc581112b30f0fb5835b16eb98f
+
+AU     72926dc581112b30f0fb5835b16eb98f:abcdef:00000001:491ea65a17ebf2375c6de7b89089a0c6:auth:d0b815ab45006cd0a0ae2a1e44c26311
+       a8b1d5e4d239a7342ba25edf07a92161
+
+
+R      23734732d5893f5f4c8db0d11afdb16f
+
+
+#-------------------------------#
+#RFC 2831
+A1     chris:elwood.innosoft.com:secret
+       HA1 -> eb5a750053e4d2c34aa84bbc9b0b6ee7
+       
+A3     eb5a750053e4d2c34aa84bbc9b0b6ee7:OA6MG9tEQGm2hh:OA6MHXh6VqTrRk
+       HA3 -> 54442ff1f394d9d0de1205cef4d9cebe
+
+A5     chris:elwood.innosoft.com:secret:OA6MG9tEQGm2hh:OA6MHXh6VqTrRk
+       HA5 -> 64fab581c08cd3607db0e2cb7448f8fc
+
+#Using 16 octet MD5 hash
+A7     chris:elwood.innosoft.com:secret
+       MA7 -> 53e4d2c34aa84bbc
+
+A9     53E4D2C34AA84BBC:OA6MG9tEQGm2hh:OA6MHXh6VqTrRk
+       HA9 -> ed24c93d123ef0a9f86b6284e21f9795 (lower)
+       HA9 -> 808ab19ea0b8021813668e78659d4a61 (Upper)
+
+#using MD4
+A11    chris:elwood.innosoft.com:secret
+       MD4 -> efc8655e58a71662cd7e05321207a166
+A13    efc8655e58a71662cd7e05321207a166:OA6MG9tEQGm2hh:OA6MHXh6VqTrRk
+       HA13-> f36feb336734edd02e66eefbe2bc410d
+
+#
+A2     AUTHENTICATE:imap/elwood.innosoft.com
+       HA2 -> 15e3594677e51ade69715d1cb7d207ba
+
+AU3    54442ff1f394d9d0de1205cef4d9cebe:OA6MG9tEQGm2hh:00000001:OA6MHXh6VqTrRk:auth:15e3594677e51ade69715d1cb7d207ba
+       HA2 -> 26ef1190b643a36e879673066098379c
+       (MAtch Question
+        https://stackoverflow.com/questions/40505711/digest-md5-response-generation)
+
+
+AU5    64fab581c08cd3607db0e2cb7448f8fc:OA6MG9tEQGm2hh:00000001:OA6MHXh6VqTrRk:auth:15e3594677e51ade69715d1cb7d207ba
+       HAU4-> d1e92278b0fe2998049370e46965d610
+
+AU9    808ab19ea0b8021813668e78659d4a61:OA6MG9tEQGm2hh:00000001:OA6MHXh6VqTrRk:auth:15e3594677e51ade69715d1cb7d207ba
+
+AU13   f36feb336734edd02e66eefbe2bc410d:OA6MG9tEQGm2hh:00000001:OA6MHXh6VqTrRk:auth:15e3594677e51ade69715d1cb7d207ba
+
diff --git a/tools/DIGEST-MD5-2 b/tools/DIGEST-MD5-2
new file mode 100644 (file)
index 0000000..d72f530
--- /dev/null
@@ -0,0 +1,25 @@
+#----------------------------
+#RFC 2831
+
+A1     chris:elwood.innosoft.com:secret
+       MD5 -> eb5a750053e4d2c34aa84bbc9b0b6ee7
+       ASC ëZuSäÒÃJ¨K¼�␋nç
+
+HA1    ëZuSäÒÃJ¨K¼�␋nç:OA6MG9tEQGm2hh:OA6MHXh6VqTrRk
+       MD5 -> 31eff9910059e25fc196bb1fd4f3b6a2
+
+A2     AUTHENTICATE:imap/elwood.innosoft.com
+       HA2 -> 15e3594677e51ade69715d1cb7d207ba
+
+Reponse        31eff9910059e25fc196bb1fd4f3b6a2:A6MG9tEQGm2hh:00000001:OA6MHXh6VqTrRk:auth:15e3594677e51ade69715d1cb7d207ba
+       MD5 -> 
+
+
+
+#----------------
+#
+USR    webmaster@example.com:devel5.safe.ca:xxx
+       MD5 -> 60dbd555ac12ca306e2a011af850062f
+
+A1     `��U��0n*��P/:abcdef:241d0e88112556c60f8154a8da8f3ce7
+       MD5 -> 
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644 (file)
index 0000000..7662208
--- /dev/null
@@ -0,0 +1,112 @@
+#--------------------------------------------------------------------
+#Executable generation area
+#--------------------------------------------------------------------
+debug  : 
+           @ $(MAKE) -s OPTIME="-g" DEBUG="-DDEBUG" exe 
+           @ echo "tools compiled in '$@' mode now ready"
+
+prod   :
+          @ $(MAKE) -s OPTIME="-g -O2" exe
+          @ echo "tools compiled in '$@' mode now ready"
+
+exe    :
+          $(MAKE) $(EXE)
+
+allclean                                               \
+clean  :
+          - rm -fr *.o $(EXE) toremake
+          - rm -fr ../bin/*
+
+#--------------------------------------------------------------------
+#Equivalences
+#--------------------------------------------------------------------
+EXE=                                                           \
+       chkhex                                                  \
+       digmd5                                                  \
+
+SRC=                                                           \
+       chkhex.c                                                \
+       digmd5.c                                                \
+
+#--------------------------------------------------------------------
+#definitions
+#--------------------------------------------------------------------
+CC     =  gcc 
+LD     =  gcc -g
+CFLAGS =  -I ../lib -Wall $(OPTIME)
+LIBMAIL        =  ../lib/libmail.a
+
+LIBS   =       $(LIBMAIL)                      \
+               -luuid                          \
+               -lcrypto                        \
+               -lcrypt                         \
+               -lssl                           \
+
+#--------------------------------------------------------------------
+VALKIND        =  "definite,possible,indirect"
+#testing area
+tstmd5 :  digmd5
+          @ ./digmd5 xxx $(BASE64)
+
+valmd5 :  digmd5
+          @ valgrind                           \
+               --leak-check=full               \
+               --show-leak-kinds=$(VALKIND)    \
+               ./digmd5 xxx $(BASE64)
+
+dbgmd5 :  digmd5
+          @ gdb                                \
+                       --args                  \
+                       ./digmd5 xxx $(BASE64)
+
+tsthex :  chkhex
+          @ ./chkhex                           \
+                       $(HEXA)
+
+dbghex :  chkhex
+          @ gdb                                \
+                       --args                  \
+                ./chkhex                       \
+                       $(HEXA)
+
+valhex :  chkhex
+          @ valgrind                           \
+               --leak-check=full               \
+               --show-leak-kinds=$(VALKIND)    \
+                ./chkhex                       \
+                       $(HEXA)
+
+#--------------------------------------------------------------------
+#Dependances
+#--------------------------------------------------------------------
+
+chkhex :  toremake chkhex.o
+          @ $(LD) $(LDFLAGS) -o ../tools/$@ $@.o $(LIBS)
+
+chkhex.o:  chkhex.c                            \
+          ../lib/unidig.h                      \
+          ../lib/subcnv.h                      \
+
+
+digmd5 :  toremake digmd5.o
+          @ $(LD) $(LDFLAGS) -o ../tools/$@ $@.o $(LIBS)
+
+digmd5.o:  digmd5.c                            \
+          ../lib/unidig.h                      \
+          ../lib/unipar.h                      \
+          ../lib/uniprc.h                      \
+          ../lib/subrou.h
+
+toremake:  Makefile
+          touch toremake
+          - rm -f $(EXE) *.o
+
+#--------------------------------------------------------------------
+HEXA   =                                       \
+               e7680cee0538ac00aff721c5c96e3d4b\
+               1d9908089a1ca0c347758b81fe61b6a4\
+               Z7680cee0538ac00aff721c5c96e3d4b\
+               Too_short                       \
+       
+BASE64 =  Y2hhcnNldD11dGYtOCxjbm9uY2U9ImFkZDJhNmM0ODFlZjYzMTA2ZWY4MDNlOGJmMzE4ZGQyIixkaWdlc3QtdXJpPSJzbXRwL2RldmVsNS5zYWZlLmNhIixuYz0wMDAwMDAwMSxub25jZT0iYWJjZGVmIixxb3A9YXV0aCxyZWFsbT0iZGV2ZWw1LnNhZmUuY2EiLHJlc3BvbnNlPTBiM2VkZThjODgyODg4OTFhMDY2MDNkOWQwYjg0ZjZmLHVzZXJuYW1lPSJ3ZWJtYXN0ZXJAZXhhbXBsZS5jb20i
+#--------------------------------------------------------------------
diff --git a/tools/chkhex.c b/tools/chkhex.c
new file mode 100644 (file)
index 0000000..6b97bb5
--- /dev/null
@@ -0,0 +1,61 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Validation program to check                     */
+/*              MD5TYP/hexadecimal sequence             */
+/*                                                     */
+/*      Usage:                                          */
+/*      chkhexa an MD5 heaxdecimal number.              */
+/*                                                     */
+/********************************************************/
+#include       <errno.h>
+#include       <stdio.h>
+#include       <stdbool.h>
+#include       <stdlib.h>
+#include       <string.h>
+#include       <unistd.h>
+
+#include       "subcnv.h"
+#include       "unidig.h"
+
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Main routine                                    */
+/*             Read the the hexadecimal number         */
+/*              convert it to binary and convert-it back*/
+/*              to  hexadecimal and compare it.         */
+/*                                                     */
+/********************************************************/
+int main(int argc,char *argv[])
+
+{
+int status;
+
+status=0;
+for (int i=1;i<argc;i++) {
+  _Bool ok;
+  MD5TYP *cnv;
+
+  ok=false;
+  if ((cnv=dig_get_plain_md5(argv[i]))!=(MD5TYP *)0) {
+    char *isgood;
+
+    isgood=cnv_tohexa((char *)cnv,sizeof(MD5TYP));
+    if (isgood!=(char *)0) {
+      if (strcmp(argv[i],isgood)==0)
+        ok=true;
+      (void) free(isgood);
+      }
+    (void) free(cnv);
+    }
+  if (ok==true)
+    (void) fprintf(stdout,"GOOD  MD5 HASH");
+  else
+    (void) fprintf(stdout,"WRONG MD5 HASH");
+  (void) fprintf(stdout," <%s>\n",argv[i]);
+  }
+return status;
+}
diff --git a/tools/digmd5.c b/tools/digmd5.c
new file mode 100644 (file)
index 0000000..a5a6881
--- /dev/null
@@ -0,0 +1,147 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+/********************************************************/
+/*                                                     */
+/*     Validation program to check DIGEST-MD5 Exchange */
+/*                                                     */
+/*      Usage:                                          */
+/*      digmd5 secret_pass base64_string                */
+/*                                                     */
+/********************************************************/
+#include       <errno.h>
+#include       <stdio.h>
+#include       <stdlib.h>
+#include       <string.h>
+#include       <unistd.h>
+
+#include       "subcnv.h"
+#include       "subrou.h"
+#include       "unidig.h"
+#include       "unipar.h"
+#include       "uniprc.h"
+
+#define         DIGMD5  "digmd5"        //application name
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*      Procedure to check the DIGEST response          */
+/*                                                     */
+/********************************************************/
+static int chkresponse(int argc,char *argv[])
+
+{
+#define OPEP    "digmd5.c:chkresponse,"
+
+int status;
+char *cleartext;
+RSPTYP *resp;
+int phase;
+_Bool proceed;
+
+status=-1;
+cleartext=cnv_getb64(argv[1]);
+phase=0;
+proceed=(cleartext!=(char *)0);
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //extracting data
+      (void) rou_alert(0,"cleartext=<%s>",cleartext);
+      resp=dig_parseresp(cleartext);
+      break;
+    case 1      :  {    //checking
+      char *HA0;
+
+      if ((HA0=dig_hashresp(resp,"AUTHENTICATE",argv[0]))!=(char *)0) {
+        if (strcmp(HA0,resp->response)==0) {
+          (void) rou_alert(0,"Expected=<%s>",resp->response);
+          (void) rou_alert(0,"Computed=<%s>",HA0);
+          status=0;
+          }
+        HA0=rou_freestr(HA0);
+        }
+      }
+      break;
+    case 2      :       //free memory
+      resp=dig_freeresp(resp);
+      break;
+    default     :       //SAFE Guard
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+cleartext=rou_freestr(cleartext);
+return status;
+
+#undef  OPEP
+}
+/*
+\f
+*/
+/********************************************************/
+/*                                                     */
+/*     Main routine                                    */
+/*             Read the challenge response in base 64  */
+/*              and check if the challange response is  */
+/*              OK.                                     */
+/*                                                     */
+/********************************************************/
+int main(int argc,char *argv[])
+
+{
+int status;
+int phase;
+ARGTYP *params;
+_Bool proceed;
+
+status=0;
+params=(ARGTYP *)0;
+foreground=true;
+phase=0;
+proceed=true;
+while (proceed==true) {
+  switch (phase) {
+    case 0      :       //checking parameters
+      if ((params=par_getparams(argc,argv,"d:hr:v"))==(ARGTYP *)0) 
+        phase=999;      //no need to go further
+      break;
+    case 1      :       //check if we have file to scan
+      if (params->argc!=2) {
+        (void) rou_alert(0,"Unable to proceed with test");
+        (void) rou_alert(0,"\tthe test pattern number should be 2");
+        (void) rou_alert(0,"\tsee Makefile 'tstmd5:' entry as example");
+        status=-1;
+        phase=999;
+        }
+      break;
+    case 2      :
+      (void) rou_modesubrou(true);
+      (void) rou_setappname(DIGMD5);
+      (void) prc_preptitle(argc,argv,environ);
+      foreground=true;
+      break;
+    case 3      :       //doing main task
+      status=chkresponse(params->argc,params->argv);
+      switch (status) {
+        case 0  :
+          (void) rou_alert(0,"dig_hashresp is working ok");
+          break;
+        default :
+          (void) rou_alert(0,"dig_hashresp is NOT working fine");
+          break;
+        }
+      break;
+    case 4      :       //main task completed
+      (void) prc_cleantitle();
+      params=par_freeparams(params);
+      (void) rou_modesubrou(false);
+      break;
+    default     :       //end of task
+      proceed=false;
+      break;
+    }
+  phase++;
+  }
+(void) exit(status);
+}
diff --git a/tools/genpsdusr.sh b/tools/genpsdusr.sh
new file mode 100755 (executable)
index 0000000..3c2a936
--- /dev/null
@@ -0,0 +1,16 @@
+#! /usr/bin/bash
+#----------------------------------------------------------
+#procedure to generate an ASCII list of user to 
+#populate and test database
+#----------------------------------------------------------
+max=$1;
+if [ -z $1 ] ; then
+  max=1000;
+  fi
+for (( num=0; num<$max; num++ ))
+ do
+ (
+ fnum=$(printf "%05d\n" "$num")
+ echo "INSERT INTO emails (email) values ('z-popusr$fnum@example.com');";
+ )
+ done
diff --git a/www/.lvlmai.php.swp b/www/.lvlmai.php.swp
new file mode 100644 (file)
index 0000000..fe9be34
Binary files /dev/null and b/www/.lvlmai.php.swp differ
diff --git a/www/coloration.js b/www/coloration.js
new file mode 100644 (file)
index 0000000..e286874
--- /dev/null
@@ -0,0 +1,84 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+// to change columne header colors
+//
+let columns = [];
+let currentSearch = "";
+
+// Récupération des éléments
+const searchInput = document.getElementById("searchInput");
+const limitSelect = document.getElementById("limitSelect");
+
+// Header click pour multi-colonnes
+document.querySelectorAll(".filter-col").forEach(th => {
+    th.addEventListener("click", () => {
+        const col = th.getAttribute("trkey");
+        if (columns.includes(col)) {
+            columns = columns.filter(c => c !== col);
+            th.classList.remove("active");
+        } else {
+            columns.push(col);
+            th.classList.add("active");
+        }
+        refresh();
+    });
+});
+
+// Recherche et changement de limit
+if (searchInput) {
+    searchInput.addEventListener("input", refresh);
+}
+if (limitSelect) {
+    limitSelect.addEventListener("change", refresh);
+}
+
+// AJAX vers le même fichier
+function refresh() {
+    const form = new FormData();
+
+    currentSearch = searchInput ? searchInput.value.trim() : "";
+    form.append("search", currentSearch);
+
+    const limitValue = limitSelect ? limitSelect.value : 0;
+    form.append("limit", limitValue);
+
+    const activeColumns = columns.length ? columns : ["remoteip", "reverse", "creation"];
+    activeColumns.forEach(c => form.append("columns[]", c));
+
+    fetch("", { method: "POST", body: form })
+    .then(r => r.text())    // <- récupère le texte brut
+    .then(text => { 
+        console.log("Server response:", text); 
+        try {
+            const json = JSON.parse(text);
+            renderTable(json.rows, json.search);
+        } catch(e) {
+            console.error("JSON parse error:", e);
+        }
+    });
+
+}
+
+// Remplissage tableau avec surbrillance
+function renderTable(rows, search) {
+    console.log("AJAX: nombre de lignes reçues =", rows.length);
+    const tbody = document.querySelector("#dataTable tbody");
+    if (!tbody) return;
+
+    tbody.innerHTML = "";
+    const re = search ? new RegExp(search, "gi") : null;
+
+    for (const row of rows) {
+        const tr = document.createElement("tr");
+        const cells = ["remoteip", "reverse", "creation"].map(c => {
+            let val = row[c] === null ? "NULL" : row[c];
+            if (re) val = val.replace(re, m => `<span class="highlight">${m}</span>`);
+            return `<td>${val}</td>`;
+        });
+        tr.innerHTML = cells.join("");
+        tbody.appendChild(tr);
+    }
+}
+
+// Load initial
+refresh();
+
diff --git a/www/coloration.js.org b/www/coloration.js.org
new file mode 100644 (file)
index 0000000..1468c03
--- /dev/null
@@ -0,0 +1,65 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+// to change columne header colors
+//
+let columns=[];
+let currentSearch="";
+
+const searchInput = document.getElementById("searchInput");
+const limitSelect = document.getElementById("limitSelect");
+
+// Header click pour multi-colonnes
+document.querySelectorAll(".filter-col").forEach(th=>{
+  th.addEventListener("click",()=>{
+  const col = th.getAttribute("trkey");
+  if (columns.includes(col)){
+    columns = columns.filter(c=>c!==col);
+    th.classList.remove("active");
+    }
+  else {
+    columns.push(col);
+    th.classList.add("active");
+    }
+  refresh();
+  });
+});
+
+// Recherche et changement de limit
+searchInput.addEventListener("input",refresh);
+limitSelect.addEventListener("change",refresh);
+
+// AJAX vers le même fichier
+function refresh(){
+currentSearch = searchInput.value.trim();
+const form = new FormData();
+form.append("search", currentSearch);
+form.append("limit", limitSelect.value);
+
+const activeColumns = columns.length ? columns : ["remoteip","reverse","creation"];
+activeColumns.forEach(c=>form.append("columns[]",c));
+
+fetch("", {method:"POST", body:form})
+     .then(r=>r.json())
+     .then(json=>renderTable(json.rows,json.search));
+}
+
+// Remplissage tableau avec surbrillance
+function renderTable(rows,search){
+console.log("AJAX: nombre de lignes reçues =", rows.length);
+const tbody=document.querySelector("#dataTable tbody");
+tbody.innerHTML="";
+const re = search ? new RegExp(search,"gi") : null;
+for(const row of rows){
+  const tr=document.createElement("tr");
+  const cells = ["remoteip","reverse","creation"].map(c=>{
+   // NULL -> "NULL"
+    let val = row[c] === null ? "NULL" : row[c];
+    if(re) val = val.replace(re,m=>`<span class="highlight">${m}</span>`);
+      return `<td>${val}</td>`;
+    });
+  tr.innerHTML=cells.join("");
+  tbody.appendChild(tr);
+  }
+}
+
+// Load initial
+refresh();
diff --git a/www/complex.java b/www/complex.java
new file mode 100644 (file)
index 0000000..65fcb2f
--- /dev/null
@@ -0,0 +1,41 @@
+add_filter( 'the_password_form', 'wpse_12172023_custom_post_password_wrong_msg' );
+
+/**
+ * Add a message to the password form.
+ *
+ * @wp-hook the_password_form
+ * @param   string $form
+ * @return  string
+ */
+function wpse_12172023_custom_post_password_wrong_msg( $form )
+{
+    // No cookie, the user has not sent anything until now.
+    if ( ! isset( $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] ) ) {
+        return $form;
+    }
+
+    // The refresh came from a different page, the user has not sent anything until now.
+    if ( wp_get_raw_referer() != get_permalink() ) {
+        return $form;
+    }
+
+    // Translate and escape.
+    $msg = esc_html__( 'Oops! Looks like your password is incorrect. Just ask your proctor for the right one.', 'your_text_domain' );
+
+    // We have a cookie, but it doesn’t match the password.
+    $msg = "<p class='custom-password-message'>$msg</p>";
+       ?>
+<style>
+    .custom-password-message {
+        background-color: #ff9999; /* Custom background color */
+        color: #990000; /* Custom text color */
+        padding: 10px; /* Custom padding */
+        border: 1px solid #cc0000; /* Custom border */
+        margin-bottom: 20px; /* Custom margin */
+    }
+</style>
+<?php
+
+    return $msg . $form;
+}
+
diff --git a/www/devsql.php b/www/devsql.php
new file mode 100644 (file)
index 0000000..b84501b
--- /dev/null
@@ -0,0 +1,152 @@
+<?php
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+//==============================================================
+//
+//     To do data access basic function
+//
+//==============================================================
+include_once "subrou.php";
+include_once "unienv.php";
+
+class devsql     {
+  public $connection = null;
+  private string $OPEP="devsql.php:class_devsql";
+  
+  // this function is called everytime this class is instantiated
+  // connecting to database
+  public function __construct($dbtype,$dbhost,$dbname,$dbuser) {
+    try {
+      $this->connection=new PDO("$dbtype:host={$dbhost};dbname={$dbname};",$dbuser);
+      $this->connection->setAttribute(PDO::ATTR_PERSISTENT,true);
+      $this->connection->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
+      $this->connection->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
+      } catch(Exception $e) {
+        throw new Exception($e->getMessage());
+        }
+    }
+
+  // Insert a row/s in a Database Table
+  public function Insert($statement="" ,$parameters=[]) {
+    try {
+      $stmt=$this->connection->prepare($statement);
+      return $stmt->execute($parameters);
+    } catch(Exception $e) {
+      throw new Exception($e->getMessage());
+      }
+    }  
+
+  // Select a row/s in a Database Table
+  public function Select($statement="",$parameters=[]) {
+    try {
+      rou_alert(0,"$this->OPEP JMPDBG select=<$statement>");
+      return $this->executeStatement($statement,$parameters);
+    } catch(Exception $e) {
+      throw new Exception($e->getMessage());
+      }
+    }
+
+  
+// Update a row/s in a Database Table
+  public function Update( )     {
+    }    
+
+  // Remove a row/s in a Database Table
+  public function Remove( )     {
+    }  
+
+  // execute statement
+  private function executeStatement($statement="",$parameters=[]) {
+    try {
+      $stmt=$this->connection->prepare($statement);
+      $stmt->execute($parameters);
+      return $stmt;
+      }catch(Exception $e) {
+        throw new Exception($e->getMessage());
+        }
+    }
+
+  //      Quote a string safely for SQL
+  public function quote(string $str): string {
+    return $this->connection->quote($str);
+    }
+  }
+
+
+//==============================================================
+//
+//     To open (according DBTYP) A database acces
+//
+//==============================================================
+function sql_connect() 
+
+{
+$OPEP="devsal.php, sql_connect";
+
+$dbinfo=null;
+$dbuser=getenv("APPNAME");
+$dbtype=getenv("DB_TYPE");
+$dbname=getenv("DB_NAME");
+$dbhost=getenv("DB_HOST");
+$dbport=getenv("DB_PORT");
+switch($dbtype) {
+case 'MYSQL'           :
+    $dbinfo=new devsql("mysql",$dbhost,$dbname,$dbuser);
+    break;
+  case 'POSTGRESQL'    :
+    $dbinfo=new devsql("pgsql",$dbhost,$dbname,$dbuser);
+    break;
+  default              :
+    unset($dbinfo);
+    $daterr="Connect Database, type <".$dbtype."> is unexpected!";
+    throw new ErrorException($daterr);
+    break;
+}
+rou_alert(0,"$OPEP connect to database");
+return $dbinfo;
+}
+
+//==============================================================
+//
+//     To close  database acces previously access
+//
+//==============================================================
+function sql_close(devsql $dbinfo) 
+
+{
+$OPEP="devsql.php, sql_close";
+
+rou_alert(0,"$OPEP JMPDBG disconnect database");
+$dbinfo->connection=NULL;
+}
+
+//==============================================================
+//
+//     To do a SQL query to database
+//
+//==============================================================
+function sql_query($conn,$reqst)
+
+{
+$dbtype=getenv("DB_TYPE");
+}
+
+//==============================================================
+//
+//      Function to return the number of record within a
+//      database table.
+//
+//==============================================================
+function sql_getnumrec($dbsql,$select,$table)
+
+{
+$OPEP="gessql.php:sql_getnumrec";
+
+$recnum=NULL;
+if ($dbsql!=NULL) {
+  $stmt=$dbsql->Select("Select count(*) from $table $select");
+  $recnum=$stmt->fetchColumn(); 
+  }
+return $recnum;
+}
+
+?>
diff --git a/www/directives.js b/www/directives.js
new file mode 100644 (file)
index 0000000..89d557f
--- /dev/null
@@ -0,0 +1,6 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+
+//==============================================================
+//all system functions
+//==============================================================
+
diff --git a/www/exetbl.php b/www/exetbl.php
new file mode 100644 (file)
index 0000000..83a7faa
--- /dev/null
@@ -0,0 +1,12 @@
+require_once 'TableFilter.php';
+$pdo = new PDO("pgsql:host=localhost;dbname=mailleur","mailleur","mailleurpass", [
+    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
+]);
+
+$filter = new TableFilter($pdo, "actions", ["remoteip","reverse","creation"]);
+
+// Exemple simple :
+$rows = $filter->fetchFiltered(["remoteip","reverse"], "localhost", 20);
+
+// Ensuite ton PHP construit son HTML/JS à partir de $rows
+
diff --git a/www/gesdis.php b/www/gesdis.php
new file mode 100644 (file)
index 0000000..b1b4575
--- /dev/null
@@ -0,0 +1,218 @@
+<?php
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+
+//==============================================================
+//                                                             
+//      Display management function
+//
+//==============================================================
+include_once "unienv.php";
+
+//==============================================================
+//
+//     To display header screeen part
+//
+//==============================================================
+function topper($isadmin,$logname,$pageref)
+
+{
+global $userlang;
+global  $dislang;
+
+$entete=getenv("APPNAME");
+$sellang = <<<EOT
+<select id="first-choice" onchange="updateTranslations()">
+<option selected="selected">$dislang</option>
+<option value="en">en</option>
+<option value="fr">fr</option>
+</select>
+EOT;
+$click1="";
+$click2="";
+$click3="";
+$admcolor="black";
+if ($isadmin==true) 
+  $admcolor="red";
+switch ($pageref) {
+  case "lvllog"        :
+    $sellang="";
+    break;
+  case "lvlmai"        :
+    $entete="<span class=\"translatable\" trkey=\"Email management\">Email management</span>";
+    $out="<span class=\"translatable\" trkey=\"Logout\">Logout</span>";
+    if ($isadmin==true) {
+      $remotes="<span class=\"translatable\" trkey=\"SMTP servers\">SMTP servers</span>";
+      $users="<span class=\"translatable\" trkey=\"Users management\">Users management</span>";
+      $click1="<a href='/lvlrmt.php'>$remotes</a>";
+      $click2="<a href='/lvlusr.php'>$users</a>";
+      }
+    $click3="<a href='/lvllog.php'>$out</a>";
+    break;
+  case "lvlusr"        :
+    $entete="<span class=\"translatable\" trkey=\"Users management\">Users management</span>";
+    //$entete=gettranslate($userlang,"User Management");
+    $main="<span class=\"translatable\" trkey=\"Email management\">Email management</span>";
+    $out="<span class=\"translatable\" trkey=\"Logout\">Logout</span>";
+    $click2="<a href='/lvlmai.php'>$main</a>";
+    $click3="<a href='/lvllog.php'>$out</a>";
+    break;
+  case "lvlrmt"        :
+    $entete="<span class=\"translatable\" trkey=\"SMTP servers\">SMTP servers</span>";
+    $main="<span class=\"translatable\" trkey=\"Email management\">Email management</span>";
+    $out="<span class=\"translatable\" trkey=\"Logout\">Logout</span>";
+    $click2="<a href='/lvlmai.php'>$main</a>";
+    $click3="<a href='/lvllog.php'>$out</a>";
+    break;
+  default      :
+    $color="red";
+    $data="???";
+    break;
+  }
+
+$STR = <<<EOT
+<CENTER><STRONG><FONT SIZE=+2>
+<Table WIDTH="100%">
+<TR>
+<TD align="left">
+<a href='/lvlmai.php'>
+<IMG src="reg-icons/Wwalczyszyn-Android-Style-Mail.96.png" ALT="mailbox">
+</a>
+</TD>
+<TD align="right">
+$entete
+</TD>
+<TR>
+</TABLE>
+</FONT></STRONG></CENTER>
+<table cellpadding="0" cellspacing="0" width="100%" border="0">
+<tr>
+  <td width="50%">
+  $sellang
+  <FONT COLOR=$admcolor>$logname</FONT>
+  </td>
+  <td width="50%">
+  <table cellpadding="0" cellspacing="0" width="100%" border="0">
+    <tr>
+    <td width="33%">
+    <p align="center"><strong>
+    $click1
+    </strong></p>
+    </td>
+    <td width="33%">
+    <p align="center"><strong>
+    $click2
+    </strong></p>
+    </td>
+    <td width="33%">
+    <p align="center"><strong>
+    $click3
+    </strong></p>
+    </td>
+    </tr>
+  </table>
+  </td>
+</tr>
+</table>
+<hr width="100%" size="8" color="black" noshade>
+EOT;
+
+return $STR;
+}
+
+//==============================================================
+//
+//     To display screen footer
+//
+//==============================================================
+function footer($pageref)
+
+{
+include        "release.php";
+
+$today=date("Y, D M d H:i:s T");
+
+$dbtype=getenv("DB_TYPE");
+$sql="unknown";
+switch($dbtype) {
+  case 'MYSQL'          :
+    $sql="mysql";
+    break;
+  case 'POSTGRESQL'     :
+    $sql="pgsql";
+    break;
+  default               :
+    $sql="undefined";
+    break;
+}
+
+$STR  = <<<EOT
+
+<hr width="100%" size="8" color="black" noshade>
+<TABLE WIDTH=100% BORDER=0 CELLPADDING=0 CELLSPACING=0>
+<TR>
+<TD width="30%" align="left"  valign="top" class="smalltxt10">
+Version: $m_release-$sql, ($pageref)
+</TD>
+<TD width="40%" align="center" valign="top" class="smalltxt10">
+$copyright
+</TD>
+<TD width="30%" align="right"  valign="top" class="smalltxt10">
+$today
+</TD>
+</TR>
+</TABLE>
+<hr width="100%" size="8" color="black" noshade>
+
+EOT;
+
+return $STR;
+}
+
+//==============================================================
+//
+//     To generate the HTML starter
+//
+//==============================================================
+function starthtml()
+
+{
+$theme="light";
+$curtime=time();        //Loading time stamp
+
+$STR  = <<<EOT
+<!DOCTYPE html>
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<TITLE>Mailleur WEB Interface</TITLE>
+<link rel="stylesheet" href="theme.css?v=$curtime">
+</HEAD>
+<BODY data-theme={$theme}>
+EOT;
+
+return $STR;
+}
+
+//==============================================================
+//
+//     To generate the HTML end 
+//
+//==============================================================
+function endhtml()
+
+{
+$curtime=time();        //Loading time stamp
+
+$STR  = <<<EOT
+<script defer src="translations.js?v={$curtime}"></script>
+<script defer src="directives.js?v={$curtime}"></script>
+<script defer src="coloration.js?v={$curtime}"></script>
+<script defer src="selectajax.js?v={$curtime}"></script>
+</BODY>
+</HTML>
+EOT;
+
+return $STR;
+}
+
+?>
diff --git a/www/gessql.php b/www/gessql.php
new file mode 100644 (file)
index 0000000..017cc0f
--- /dev/null
@@ -0,0 +1,337 @@
+<?php
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+//==============================================================
+//
+//     To access data within the database
+//
+//==============================================================
+include_once "subrou.php";
+include_once "devsql.php";
+
+class probe       {
+    protected $db;                      // devsql database instance
+    private string $table;              // table name
+
+    private ?string $where = null;      // WHERE conditions
+
+    private ?string $order = null;      // ORDER BY clause
+    private ?int $limit = null;         // LIMIT for pagination
+    private int $offset = 0;            // OFFSET for pagination, internal only
+    private ?string $columns = null;    // request specific colunn(s)
+    private string $OPEP="gessql.php:class_probe";
+
+    /**
+     * Constructor
+     **/
+    public function __construct(string $table,int $limit,int $offset) {
+      $this->db = sql_connect();
+      $this->table = $table;
+      $this->limit = $limit;
+      $this->offset = $offset;
+    }
+
+    // protecting data format
+    public function quote($value) {
+      return $this->db->quote($value);
+      }
+    /**
+     * Add the release function
+     **/
+    public function release(): void {
+      if ($this->db !== NULL) {
+        sql_close($this->db);
+        }
+      }
+
+    /**
+     * Add a WHERE condition
+     **/
+    public function where(string $datawhere): self {
+
+      $this->where = $datawhere;
+      return $this;
+     }
+
+    /**
+     * Set ORDER BY clause
+     **/
+    public function order(string $order): self {
+
+      $this->order = $order;
+      return $this;
+      }
+
+    /**
+     * Set LIMIT for pagination
+     **/
+    public function limit(int $n): self {
+
+      $this->limit = $n;
+      return $this;
+      }
+
+    /**
+     * Set Column selection
+     **/
+    public function columns(array $cols): self {
+      $this->columns = implode(', ', $cols);
+      return $this;
+      }
+
+    /**
+     * Execute the query and return results
+     */
+    public function get(): array {
+
+     $sql = "SELECT " . ($this->columns ?? "*") . " FROM {$this->table}";
+     if ($this->where) {
+       $sql .= " WHERE {$this->where}";
+       }
+
+     if ($this->order) {
+       $sql .= " ORDER BY {$this->order}";
+       }
+
+    if ($this->limit !== null) {
+      $sql .= " LIMIT {$this->limit}";
+      }
+
+    if ($this->offset > 0) {
+      $sql .= " OFFSET {$this->offset}";
+      }
+
+    rou_alert(0,"$this->OPEP, JMPDBG rqst sql=<$sql>");
+    $stmt = $this->db->Select($sql);
+    return $stmt->fetchAll();
+    }
+
+    /**
+     * Count total number of items matching the current WHERE conditions
+     */
+    public function getnumrec(): int {
+
+    $sql = "SELECT COUNT(*) AS cnt FROM {$this->table}";
+
+    if ($this->where) {
+      $sql .= " WHERE {$this->where}";
+      }
+
+    $stmt = $this->db->Select($sql);
+    $row = $stmt->fetch();
+    return (int) $row['cnt'];
+    }
+
+    /** ----------------------------- **/
+    /** Pagination / navigation      **/
+    /** ----------------------------- **/
+
+    /**
+     * Move offset to next page
+     * Does NOT fetch data
+     */
+    public function goNext(): int {
+
+    if ($this->limit !== null) {
+      $last=max($this->getnumrec(),$this->limit);
+      $this->offset = min($last-$this->limit,$this->offset+$this->limit);
+      }
+    return $this->offset;
+    }
+
+    /**
+     * Move offset to previous page
+     * Does NOT fetch data
+     */
+    public function goPrevious(): int {
+
+      if ($this->limit !== null) {
+        $this->offset = max(0, $this->offset - $this->limit);
+      }
+    return $this->offset;
+    }
+
+    /**
+     * Move offset to first page
+     * Does NOT fetch data
+     */
+    public function goFirst(): int {
+
+    $this->offset = 0;
+    return $this->offset;
+    }
+
+    /**
+     * Move offset to last page
+     * Does NOT fetch data
+     */
+    public function goLast(): int {
+
+    if ($this->limit !== null) {
+      $total = $this->getnumrec();
+      $this->offset = max(0, $total - $this->limit);
+      }
+    return $this->offset;
+    }
+
+    /**
+     * Get current offset value
+     */
+    public function getOffset(): int {
+
+    return $this->offset;
+    }
+}
+
+//==============================================================
+//
+//      Function to check if password is the right one
+//
+//==============================================================
+function sql_checkpassword($dbsql,$logname,$password)
+
+{
+$OPEP="gessql.php:sql_checkpassword";
+  
+$isgood=false;
+$phase=0;
+$proceed=true;
+while ($proceed==true) {
+  //rou_alert(0,"$OPEP, JMPDBG phase=$phase");
+  switch ($phase) {
+    case 0      :       //do we have both logname and password
+      if ($logname=="" || $password=="") {
+        rou_alert(0,"$OPEP, logname=<$logname> or password=<$password> missing!");
+        $phase=999;     //trouble trouble
+        }
+      break;
+    case 1      :       //extracting crypted password
+      $stmt=$dbsql->Select("Select * from emails where email='$logname'");
+      $dbpass=$stmt->fetch(PDO::FETCH_ASSOC)['password'];
+      if ($dbpass==NULL) { //is user known?
+        rou_alert(0,"$OPEP, logname=<$logname> missing from database");
+        $phase=999;     //user unknown, trouble trouble
+        }
+      break;
+    case 2      :       //'computing' crypted password
+      $idpass=$dbpass;
+      $ptr=strrchr($idpass,'$');
+      if ($ptr!=NULL) 
+        $idpass=substr($idpass,0,strlen($idpass)-strlen($ptr)+1);
+      $coded=crypt($password,$idpass);
+      if (strcmp($dbpass,$coded)!=0) {
+        rou_alert(0,"$OPEP, user=<$logname> wrong password=<$password>");
+        $phase=999;     //bad password 
+        }
+      break;
+    case 3      :       //everything fine
+      $random=(string)rand(0,9999999); 
+      $uniqid=uniqid("",true);
+      $delay=time()+(24*3600);
+      $limit=date("Y-m-d H:i:s",$delay);
+      setcookie(getenv("APPNAME"),"$random$uniqid",$delay,"/");
+      $fields="cookuuid,email,expire";
+      $values="'$random$uniqid','$logname','$limit'";
+      $dbsql->Insert("Insert into cookies ($fields) values ($values)");
+      $isgood=true;
+      break;
+    default     :       //SAFE Guard
+      $proceed=false;
+      break;
+    }
+  $phase++;
+  }
+return $isgood;
+}
+
+//==============================================================
+//
+//      Function to get a log name if a cookie is available
+//
+//==============================================================
+function sql_setlogname($dbsql)
+
+{
+global $isadmin;
+global $logname;
+global $userlang;
+global $dislang;
+
+$OPEP="gessql.php:sql_setlogname";
+
+$isadmin=0;
+$logname=NULL;
+$expire=NULL;
+$cookie=NULL;
+$phase=0;
+$proceed=true;
+while ($proceed==true) {
+  //rou_alert(0,"$OPEP, JMPDBG phase=$phase");
+  switch ($phase) {
+    case 0      :       //do we have a cookie
+      if (isset($_COOKIE[getenv("APPNAME")])==0) 
+        $phase=999;
+      break;
+    case 1      :       //is the cookie within database
+      $cookie=$_COOKIE[getenv("APPNAME")];
+      $cookie=htmlspecialchars($cookie);
+      $stmt=$dbsql->Select("Select * from cookies where cookuuid='$cookie'");
+      $expire=$stmt->fetch(PDO::FETCH_ASSOC)['expire'];
+      if ($expire==null) {
+        rou_alert(0,"$OPEP, cookie <$cookie> expire not found in DB!");
+        $phase=999;
+        }
+      break;
+    case 2      :       //checking if cookie expired
+      $texpire=strtotime($expire);
+      if (time()>$texpire) {
+        rou_alert(0,"$OPEP, cookie <$cookie> time limite");
+        $phase=999;
+        }
+      break;
+    case 3      :       //extracting logname
+      $stmt=$dbsql->Select("Select * from cookies where cookuuid='$cookie'");
+      $logname=$stmt->fetch(PDO::FETCH_ASSOC)['email'];
+      if ($logname==NULL) { //is user known?
+        rou_alert(0,"$OPEP, cookie <$cookie> not found in DB");
+        $phase=999;
+        }
+      break;
+    case 4      :       //getting user information
+      $stmt=$dbsql->Select("Select * from emails where email='$logname'");
+      if ($stmt==NULL) { //is user really known?
+        rou_alert(0,"$OPEP, cookie but user <$logname> missing from DB?");
+        $logname=NULL;
+        $phase=999;
+        }
+      break;
+    case 5      :       //getting user working language
+      $dislang="en";
+      $language=$stmt->fetch(PDO::FETCH_ASSOC)['lang'];
+      rou_alert(0,"$OPEP, JMPDBG langage=$language");
+      if ($language!=NULL) {
+        switch ($language) {
+          case "FRA"  :
+            $dislang="fr";
+            break;
+          case "ENG"  :
+          default     :       //NO BREAK
+            break;
+          }
+        }
+      break;
+    case 6      :       //checking if logname is an admin
+      $stmt=$dbsql->Select("Select * from admins where email='$logname'");
+      $id=$stmt->fetch(PDO::FETCH_ASSOC)['email'];
+      if ($id!=NULL)
+        $isadmin=true;
+      break;
+    default     :       //SAFE Guard
+      $proceed=false;
+      break;
+    }
+  $phase++;
+  }
+rou_alert(0,"$OPEP, now logname=<$logname>");
+}
+
+?>
diff --git a/www/index.php b/www/index.php
new file mode 120000 (symlink)
index 0000000..336b484
--- /dev/null
@@ -0,0 +1 @@
+lvllog.php
\ No newline at end of file
diff --git a/www/lvllog.php b/www/lvllog.php
new file mode 100644 (file)
index 0000000..b8317c3
--- /dev/null
@@ -0,0 +1,141 @@
+<?php
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+//==============================================================
+//
+//     login screen management
+//
+//==============================================================
+include_once "subrou.php";
+include_once "unienv.php";
+include_once "gesdis.php";
+include_once "gessql.php";
+
+//==============================================================
+//
+//     To display the login screen
+//
+//==============================================================
+function login()
+
+{
+$STR  = <<<EOT
+
+<table border="0" align="center" width="60%" cellpadding="0" cellspacing="0">
+    <tr>
+      <td align="center">
+      <FONT SIZE=+2><STRONG>
+      <span class="translatable" trkey="Authentication">Authentication</span>
+      </STRONG></FONT>
+      </td>
+    </tr>
+    <tr>
+      <td align="center" bgcolor="#000000">
+        <table width="100%" cellpadding="0" cellspacing="1" border="0">
+          <tr>
+            <td bgcolor="#FFFFFF" align="right">
+           <form method="post"
+                 action="lvllog.php"
+                 enctype="application/x-www-form-urlencoded"
+                 name="login">
+              <table align="center" border="0" cellspacing="5" cellpadding="0">
+                <tr>
+                  <td align="left" nowrap="nowrap"><span class="translatable" trkey="Email">Email</span><span>:</span></td>
+                  <td>
+                  <input  tabindex="1" type="text" name="email" size="40" onchange="" />
+                  <select id="first-choice" onchange="updateTranslations()">
+                    <option value="en">English</option>
+                    <option value="fr">Francais</option>
+                  </select>
+                  </td>
+                </tr>
+
+                <tr>
+                  <td align="left" nowrap="nowrap"><span class="translatable" trkey="Password">Password</span><span>:</span></td>
+                  <td>
+                 <input  tabindex="2" type="password" name="passwd" size="40" id="myInput" />
+                  <input type="checkbox" onclick="showpass()">
+                    <span class="translatable" trkey="Show" >Show</span>
+                 </td>
+                </tr>
+              </table>
+
+              <table width="100%" cellpadding="0" cellspacing="1" border="0">
+                <tr>
+                  <td align="center" colspan="2">
+                    <input  type="hidden" name="action" value="login" />
+                    <table>
+                      <tr>
+                        <td align="center">
+                        <input  tabindex="3" type="submit" name="loginbutton" value="Login" 
+                        class="translatable" trkey="Login" />
+                       </td>
+                      </tr>
+                    </table>
+                  </td>
+                </tr>
+              </table>
+           </form>
+          </tr>
+        </table>
+      </td>
+    </tr>
+  </table>
+<br>
+<br>
+
+EOT;
+
+return $STR;
+}
+
+//==============================================================
+//
+//      To display login screen body
+//
+//==============================================================
+function body()
+
+{
+global $isadmin;
+
+$start=starthtml();
+$stop=endhtml();
+$top=topper($isadmin,NULL,"lvllog");
+$footer=footer("lvllog");
+$login=login();
+
+$STR  = <<<EOT
+$start
+$top
+$login
+$footer
+$stop
+EOT;
+
+return $STR;
+}
+
+if ($_SERVER["REQUEST_METHOD"] == "POST") {
+  $pass=$_POST["passwd"];
+  $logname=$_POST["email"];
+  $dbsql=sql_connect();
+  $isok=sql_checkpassword($dbsql,$logname,$pass);
+  sql_close($dbsql);
+  switch ($isok) {
+    case false  :       //trouble report
+      echo "<script>";
+      echo "alert('wrong username or password. Please try again.');";
+      echo "</script>";
+      break;
+    default     :
+      header('Location: lvlmai.php');
+      break;
+    }
+  }
+
+//display main screen
+echo body();
+?>
+
+
+
diff --git a/www/lvlmai.php b/www/lvlmai.php
new file mode 100644 (file)
index 0000000..7b174e0
--- /dev/null
@@ -0,0 +1,344 @@
+<?php
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab :
+//==============================================================
+//
+//     Main screen management
+//
+//==============================================================
+
+/* ============================================================
+ * AJAX MODE (JSON only)
+ * ============================================================ */
+if (isset($_POST['columns'])) {
+
+    // (temporaire, pour test)
+    header('Content-Type: application/json; charset=utf-8');
+
+    echo json_encode([
+        'rows'   => [],   // on remplira plus tard
+        'search' => $_POST['search'] ?? ''
+    ]);
+
+    exit; // <<< CRITIQUE
+}
+
+include_once "subrou.php";
+include_once "unienv.php";
+include_once "gesdis.php";
+include_once "gessql.php";
+
+// Defining the application name
+global $myfilename;
+$myfilename="lvlmai";
+
+//==============================================================
+// Table header generation
+//==============================================================
+function tblheader(string $dsearch,int $numrec,int $offset,int $limit, string $selectedField)
+
+{
+global $userlang;
+global $myfilename;
+
+$filterOptions = [
+    'smtpfrom' => 'Originator',
+    'rcptto'   => 'Recipient',
+    'creation' => 'Date',
+    ];
+
+$optionsHtml = '';
+
+foreach ($filterOptions as $value => $trkey) {
+    $selected = ($selectedField === $value) ? ' selected' : '';
+
+    $optionsHtml .=
+        '<option value="' . htmlspecialchars($value, ENT_QUOTES) . '"'
+      . ' class="translatable"'
+      . ' trkey="' . htmlspecialchars($trkey, ENT_QUOTES) . '"'
+      . $selected
+      . '>'
+      . htmlspecialchars($trkey)
+      . '</option>';
+}
+error_log($optionsHtml);
+
+$dsearchHtml = htmlspecialchars($dsearch,ENT_QUOTES|ENT_SUBSTITUTE);
+
+$debut=$offset+1;
+$STR  = <<<EOT
+<TABLE WIDTH="100%" BORDER="0" CellSpacing="0">
+<TR>
+<TD align=left>
+<FORM action="{$myfilename}.php" method="post">
+<input type="hidden" name="dsearch" value="$dsearch"/>
+<input type="hidden" name="offset" value="$offset"/>
+<input type="hidden" name="limit" value="$limit"/>
+<button name="scanner" value="gofirst">
+<FONT SIZE=+3>
+&larrb;
+</FONT>
+</Button>
+<button name="scanner" value="goprevious">
+<FONT SIZE=+3>
+&larr;
+</FONT>
+</Button>
+<button name="scanner" value="gonext">
+<FONT SIZE=+3>
+&rarr;
+</FONT>
+</Button>
+<button name="scanner" value="golast">
+<FONT SIZE=+3>
+&rarrb;
+</FONT>
+</Button>
+<FONT SIZE=+2>$debut/$numrec</FONT>
+</FORM>
+</TD>
+<TD align=left>
+<FORM id="searchForm" action="{$myfilename}.php" method="post">
+<FONT SIZE=+1>
+    <span class="translatable" trkey="Search">Search</span>
+    <input type="text" id="searchInput" value="{$dsearchHtml}" name="dsearch" size=20 style="font-size: 100%;">
+    <input type="submit" style="display:none"/>
+</FONT>
+</FORM>
+</TD>
+<TD align=right>
+<FORM method="post">
+<input type="hidden" name="dsearch" value="$dsearch"/>
+<input type="hidden" name="offset" value="$offset"/>
+<input type="hidden" name="limit" value="$limit"/>
+<select id="limitSelect" name="limit" style="font-size: 130%;" onchange='if (this.value!=0) {this.form.submit();}'>
+<option selected="selected">$limit</option>
+<option value=20>20</option>
+<option value=40>40</option>
+<option value=80>80</option>
+<option value=100>100</option>
+</SELECT>
+</FORM>
+</TD>
+</TR>
+</TABLE>
+EOT;
+
+return $STR;
+}
+
+//==============================================================
+// Main screen body generation
+//==============================================================
+function body($logname)
+
+{
+global $userlang;
+global $isadmin;
+global $myfilename;
+
+$limit=20;
+$offset=0;
+$dsearch="";
+$selectedField = 'rcptto';
+
+
+if (isset($_POST['limit']))
+  $limit=intval($_POST['limit']);
+if (isset($_POST['offset']))
+  $offset=intval($_POST['offset']);
+
+$rqst = new probe("actions",$limit,$offset);
+
+if (isset($_POST['columns']) && is_array($_POST['columns'])) {
+    $cols = $_POST['columns']; // conserver tableau tel quel
+    $rqst->columns($cols);
+}
+
+if (isset($_POST['dsearch'])) {
+  $dsearch=trim($_POST['dsearch']);
+  if (strlen($dsearch)>0) {
+    // sécuriser le champ sélectionné
+    $allowedFields = ['smtpfrom','rcptto','creation'];
+  if (!in_array($selectedField,$allowedFields))
+    $selectedField = 'rcptto';
+
+  // filtrage selon le champ choisi
+  $safeSearch = $rqst->quote('%' . $dsearch . '%');
+  if ($selectedField === 'creation') {
+    // pour la date, on peut filtrer par LIKE
+    $rqst->where("$selectedField::text LIKE $safeSearch");
+    }
+  else {
+    $rqst->where("$selectedField LIKE $safeSearch");
+    }
+  }
+}
+
+$numrec=$rqst->getnumrec();
+
+if (isset($_POST['scanner'])) {
+  $scanner=$_POST['scanner'];
+  switch ($scanner) {
+    case "golast"       :
+      $offset=$rqst->goLast();
+      break;
+    case "gonext"       :
+      $offset=$rqst->goNext();
+      break;
+    case "goprevious"   :
+      $offset=$rqst->goPrevious();
+      break;
+    case "gofirst"       :
+      $offset=$rqst->goFirst();
+      break;
+    default             :
+      break;
+    }
+  }
+
+$actions=$rqst->order("creation desc")
+              ->limit($limit)
+              ->GET();
+
+if (!is_array($actions)) { 
+  $actions = [];
+  }
+
+if(isset($_POST['ajax']) && $_POST['ajax'] == 1) {
+  header('Content-Type: application/json');
+  echo json_encode([
+    'rows'   => $actions,
+    'search' => $dsearch
+    ]);
+  exit; // très important pour ne pas générer le reste de la page
+  }
+
+
+$line="";
+$count=$offset+1;
+foreach($actions as $action) {
+  $info=substr($action['info'],3,1);
+  $status=$action['status'];
+  $subject=htmlspecialchars($action['subject'],ENT_QUOTES);
+  if (($subject==NULL) || (strlen($subject)==0))
+    $subject=htmlspecialchars($action['info'],ENT_QUOTES);
+  switch ($status) {
+    case 250:
+      $bgcolor="#90EE90";
+      break;       // light green
+    case 460:
+      $bgcolor="#ff6666";
+      $subject=htmlspecialchars($action['info'],ENT_QUOTES);
+      break; // light red
+    case 551:
+      $bgcolor="#FF7F50";
+      break;       // coral
+    case 555:
+      $bgcolor="#D3D3D3";
+      break;       // light gray
+    default:
+      $bgcolor="yellow";
+      break;
+    }
+  $date=$action['creation'];
+  $reverse=$action['reverse'];
+  $remoteip=$action['remoteip'];
+  $smtpfrom=$action['smtpfrom'];
+  $recipient=$action['rcptto'];
+  $emailfrom=htmlspecialchars($action['emailfrom'],ENT_QUOTES);
+
+  // Generating the table contents
+  $line .= "<TR bgcolor=\"$bgcolor\">\r\n";
+  $line .= "<TD align=center>$count</TD>\r\n";
+  $line .= "<TD align=center>$status</TD>\r\n";
+  $line .= "<TD align=left>$date</TD>\r\n";
+  $line .= "<TD align=left>$remoteip</TD>\r\n";
+  $line .= "<TD align=left>$reverse</TD>\r\n";
+  $line .= "<TD align=left>$smtpfrom<BR><FONT SIZE=-1>$emailfrom</FONT></TD>\r\n";
+  $line .= "<TD align=left>$recipient</TD>\r\n";
+  $line .= "<TD align=left>$subject</TD>\r\n";
+  $line .= "</TR>\r\n";
+
+  $count++;
+  }
+
+//connection to database not needed anymore
+$rqst->release();
+unset($rqst);
+
+$start=starthtml();
+$stop=endhtml();
+$top=topper($isadmin,$logname,"$myfilename");
+$footer=footer("$myfilename");
+$tblheader=tblheader($dsearch,$numrec,$offset,$limit,$selectedField);
+$tblfooter="";
+if ($limit>30) 
+  $tblfooter=$tblheader;
+
+$STR  = <<<EOT
+
+$start
+$top
+<CENTER>
+$tblheader
+<table class="table-userlist" WIDTH="100%" BORDER="1" CellSpacing="0">
+<THEAD id="headerRow">
+<TR>
+<TH align=center>Num</TH>
+<TH align=center class="filter-col" trkey="Status">
+<span class="translatable">Status</span>
+</TH>
+<TH align=center class="filter-col" trkey="Date">
+<span class="translatable">Date</span>
+</TH>
+<TH align=center class="filter-col" trkey="IP">
+<span>IP</span>
+</TH>
+<TH align=center class="filter-col" trkey="Reverse IP">
+<span class="translatable">Reverse IP</span>
+</TH>
+<TH align=center class="filter-col" trkey="Originator">
+<span class="translatable">Originator</span>
+</TH>
+<TH align=center class="filter-col" trkey="Recipient">
+<span class="translatable">Recipient</span>
+</TH>
+<TH align=center class="filter-col" trkey="Subject">
+<span class="translatable">Subject</span>
+</TH>
+</TR>
+</THEAD>
+<TBODY id="bodyRows">
+$line
+</TBODY>
+</Table>
+$tblfooter
+</CENTER>
+<BR>
+$footer
+$stop
+EOT;
+
+return $STR;
+}
+
+//==============================================================
+// Script execution
+//==============================================================
+global $logname;
+
+rou_openlog();
+
+$dbsql=sql_connect();
+sql_setlogname($dbsql);
+sql_close($dbsql);
+
+if ($logname!=NULL) {
+  echo body($logname);
+  }
+else {
+  header('Location: lvllog.php');
+  }
+rou_closelog();
+?>
+
diff --git a/www/lvlrmt.php b/www/lvlrmt.php
new file mode 100644 (file)
index 0000000..478ce91
--- /dev/null
@@ -0,0 +1,220 @@
+<?php
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+//==============================================================
+//
+//     remote server management screen
+//
+//==============================================================
+include_once "subrou.php";
+include_once "unienv.php";
+include_once "gesdis.php";
+include_once "gessql.php";
+
+function tblheader(string $remoteip,int $numrec,int $offset,int $limit)
+
+{
+global $userlang;
+
+$debut=$offset+1;
+
+$STR  = <<<EOT
+<TABLE WIDTH="100%" BORDER="0" CellSpacing="0">
+<TR>
+<TD align=left>
+<FORM action="lvlrmt.php" method="post">
+<input type="hidden" name="remoteip" value="$remoteip"/>
+<input type="hidden" name="offset" value="$offset"/>
+<input type="hidden" name="limit" value="$limit"/>
+<button name="scanner" value="gofirst">
+<FONT SIZE=+3>
+&larrb;
+</FONT>
+</Button>
+<button name="scanner" value="goprevious">
+<FONT SIZE=+3>
+&larr;
+</FONT>
+</Button>
+<button name="scanner" value="gonext">
+<FONT SIZE=+3>
+&rarr;
+</FONT>
+</Button>
+<button name="scanner" value="golast">
+<FONT SIZE=+3>
+&rarrb;
+</FONT>
+</Button>
+<FONT SIZE=+2>$debut/$numrec</FONT>
+</FORM>
+</TD>
+<TD align=left>
+<FORM action="lvlrmt.php" method="post">
+<FONT SIZE=+1>
+<span class="translatable" trkey="Search">Search</span>
+<input type="text" value="$remoteip" name="remoteip" size=20 style="font-size: 100%;">
+<input type="submit" style="display:none"/>
+</FONT>
+</FORM>
+</TD>
+<TD align=right>
+<FORM method="post">
+<input type="hidden" name="remoteip" value="$remoteip"/>
+<input type="hidden" name="offset" value="$offset"/>
+<input type="hidden" name="limit" value="$limit"/>
+<select name="limit" style="font-size: 130%;" onchange='if (this.value!=0) {this.form.submit();}'>
+<option selected="selected">$limit</option>
+<option value=20>20</option>
+<option value=40>40</option>
+<option value=80>80</option>
+<option value=100>100</option>
+</SELECT>
+</FORM>
+</TD>
+</TR>
+</TABLE>
+EOT;
+
+return $STR;
+}
+
+//section to generate the main screen body
+function body($logname)
+
+{
+$OPEP="lvlrmt.php:body";
+
+global $isadmin;
+global $logname;
+
+$limit=20;
+$offset=0;
+$remoteip="";
+if (isset($_POST['limit']))
+  $limit=intval($_POST['limit']);
+if (isset($_POST['offset']))
+  $offset=intval($_POST['offset']);
+
+$rqst=NEW probe("remotes",$limit,$offset);
+
+if (isset($_POST['remoteip'])) {
+  $remoteip=trim(($_POST['remoteip']));
+  if (strlen($remoteip)>0)
+    $rqst->where("remoteip like '%$remoteip%'");
+  }
+$numrec=$rqst->getnumrec();
+
+if (isset($_POST['scanner'])) {
+  $scanner=$_POST['scanner'];
+  switch ($scanner) {
+     case "golast"       :
+      $offset=$rqst->goLast();
+      break;
+    case "gonext"       :
+      $offset=$rqst->goNext();
+      break;
+    case "goprevious"   :
+      $offset=$rqst->goPrevious();
+      break;
+    case "gofirst"       :
+      $offset=$rqst->goFirst();
+      break;
+    default             :
+      break;
+    }
+  }
+$remotes=$rqst->order("remoteip asc")
+              ->limit($limit)
+              ->get();
+$line="";
+$count=$offset;
+foreach($remotes as $remote) {
+  $count++;
+  $bgcolor="white";
+  if ($count & 1 ) {
+    $bgcolor="green";
+    }
+  $ip=$remote['remoteip'];
+  $reverse=$remote['reverse'];
+  $last=$remote['lastupdate'];
+  $credit=$remote['credit'];
+  $line=$line."<TR>\r\n";
+  $line=$line."<TD><FONT SIZE=-1>$count&nbsp</FONT>\r\n";
+  $line=$line."<INPUT TYPE=IMAGE SRC=\"/reg-icons/folder.gif\" alt=\"ouvrir le dossier\" title=\"ouvrir le dossier\">\r\n";
+  $line=$line."&nbsp;$ip\r\n";
+  $line=$line."</TD>\r\n";
+  $line=$line."<TD align=center>$reverse</TD>\r\n";
+  $line=$line."<TD align=center>$last</TD>\r\n";
+  $line=$line."<TD align=center>$credit</TD>\r\n";
+  $line=$line."</TR>";
+  }
+//connection to database not needed anymore
+$rqst->release();
+unset($rqst);
+
+rou_closelog();
+
+$start=starthtml();
+$stop=endhtml();
+$top=topper($isadmin,$logname,"lvlrmt");
+$footer=footer("lvlrmt");
+$tblheader=tblheader($remoteip,$numrec,$offset,$limit);
+$tblfooter="";
+if ($limit>30)
+  $tblfooter=$tblheader;
+
+$STR  = <<<EOT
+$start
+$top
+<style type="text/css">
+.table-userlist tr:nth-child(odd)
+  {
+  background-color: #66ff99;    /*light green   */
+  color: black ;
+  }
+
+.table-userlist tr:nth-child(even)
+  {
+  background-color: white;
+  color: black ;
+  }
+</style>
+
+<CENTER>
+$tblheader
+<table class="table-userlist" WIDTH="100%" BORDER="1" CellSpacing="0">
+<TR>
+<TH align=center>IP</TH>
+<TH align=center>Reverse</TH>
+<TH align=center>lastemail</TH>
+<TH align=center>credit</TH>
+</TR>
+$line
+</Table>
+$tblfooter
+</CENTER>
+<BR>
+<BR>
+$footer
+$stop
+EOT;
+
+return $STR;
+}
+
+rou_openlog();
+$dbsql=sql_connect();
+sql_setlogname($dbsql);
+sql_close($dbsql);
+
+if ($logname!=NULL) {
+  echo body($logname);
+  }
+else {
+  header('Location: lvllog.php');
+  }
+rou_closelog();
+?>
+
+
+
diff --git a/www/lvlusr.php b/www/lvlusr.php
new file mode 100644 (file)
index 0000000..616d09e
--- /dev/null
@@ -0,0 +1,217 @@
+<?php
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+//==============================================================
+//
+//     Main screen management
+//
+//==============================================================
+include_once "subrou.php";
+include_once "unienv.php";
+include_once "gesdis.php";
+include_once "gessql.php";
+
+function tblheader(string $username,int $numrec,int $offset,int $limit)
+
+{
+global $userlang;
+
+$debut=$offset+1;
+
+$STR  = <<<EOT
+<TABLE WIDTH="100%" BORDER="0" CellSpacing="0">
+<TR>
+<TD align=left>
+<FORM action="lvlusr.php" method="post">
+<input type="hidden" name="username" value="$username"/>
+<input type="hidden" name="offset" value="$offset"/>
+<input type="hidden" name="limit" value="$limit"/>
+<button name="scanner" value="gofirst">
+<FONT SIZE=+3>
+&larrb;
+</FONT>
+</Button>
+<button name="scanner" value="goprevious">
+<FONT SIZE=+3>
+&larr;
+</FONT>
+</Button>
+<button name="scanner" value="gonext">
+<FONT SIZE=+3>
+&rarr;
+</FONT>
+</Button>
+<button name="scanner" value="golast">
+<FONT SIZE=+3>
+&rarrb;
+</FONT>
+</Button>
+<FONT SIZE=+2>$debut/$numrec</FONT>
+</FORM>
+</TD>
+<TD align=left>
+<FORM action="lvlusr.php" method="post">
+<FONT SIZE=+1>
+<span class="translatable" trkey="Search">Search</span>
+<input type="text" value="$username" name="username" size=20 style="font-size: 100%;">
+<input type="submit" style="display:none"/>
+</FONT>
+</FORM>
+</TD>
+<TD align=right>
+<FORM method="post">
+<input type="hidden" name="username" value="$username"/>
+<input type="hidden" name="offset" value="$offset"/>
+<input type="hidden" name="limit" value="$limit"/>
+<select name="limit" style="font-size: 130%;" onchange='if (this.value!=0) {this.form.submit();}'>
+<option selected="selected">$limit</option>
+<option value=20>20</option>
+<option value=40>40</option>
+<option value=80>80</option>
+<option value=100>100</option>
+</SELECT>
+</FORM>
+</TD>
+</TR>
+</TABLE>
+EOT;
+
+return $STR;
+}
+
+//section to generate the main screen body
+function body($logname)
+
+{
+$OPEP="lvlusr.php:body";
+
+global $isadmin;
+global $logname;
+
+$limit=20;
+$offset=0;
+$username="";
+if (isset($_POST['limit']))
+  $limit=intval($_POST['limit']);
+if (isset($_POST['offset']))
+  $offset=intval($_POST['offset']);
+$rqst=NEW probe("emails",$limit,$offset);
+if (isset($_POST['username'])) {
+  $username=trim(($_POST['username']));
+  if (strlen($username)>0)
+    $rqst->where("email like '%$username%'");
+  }
+$numrec=$rqst->getnumrec();
+if (isset($_POST['scanner'])) {
+  $scanner=$_POST['scanner'];
+  switch ($scanner) {
+    case "golast"       :
+      $offset=$rqst->goLast();
+      break;
+    case "gonext"       :
+      $offset=$rqst->goNext();
+      break;
+    case "goprevious"   :
+      $offset=$rqst->goPrevious();
+      break;
+    case "gofirst"       :
+      $offset=$rqst->goFirst();
+      break;
+    default             :
+      break;
+    }
+}
+$emails=$rqst->order("email asc")
+             ->limit($limit)
+             ->get();
+$line="";
+$count=$offset;
+foreach($emails as $email) {
+  $count++;
+  $bgcolor="white";
+  if ($count & 1 ) {
+    $bgcolor="green";
+    }
+  $name=$email['email'];
+  $last=$email['lastemail'];
+  $max=$email['mxspace'];
+  $lock=$email['locked'];
+  $line=$line."<TR>\r\n";
+  $line=$line."<TD><FONT SIZE=-1>$count&nbsp</FONT>\r\n";
+  $line=$line."<INPUT TYPE=IMAGE SRC=\"/reg-icons/folder.gif\" alt=\"ouvrir le dossier\" title=\"ouvrir le dossier\">\r\n";
+  $line=$line."&nbsp;$name\r\n";
+  $line=$line."</TD>\r\n";
+  $line=$line."<TD align=center>$last</TD>\r\n";
+  $line=$line."<TD align=center>$max</TD>\r\n";
+  $line=$line."<TD align=center>$lock</TD>\r\n";
+  $line=$line."</TR>";
+  }
+//connection to database not needed anymore
+$rqst->release();
+unset($rqst);
+
+$start=starthtml();
+$stop=endhtml();
+$top=topper($isadmin,$logname,"lvlusr");
+$footer=footer("lvlusr");
+$tblheader=tblheader($username,$numrec,$offset,$limit);
+$tblfooter="";
+if ($limit>30)
+  $tblfooter=$tblheader;
+
+$STR  = <<<EOT
+$start
+$top
+<style type="text/css">
+.table-userlist tr:nth-child(odd)
+  {
+  background-color: #66ff99;    /*light green   */
+  color: black ;
+  }
+
+.table-userlist tr:nth-child(even)
+  {
+  background-color: white;
+  color: black ;
+  }
+</style>
+
+<CENTER>
+<table WIDTH="100%" BORDER="0" CellSpacing="0">
+$tblheader
+<table class="table-userlist" WIDTH="100%" BORDER="1" CellSpacing="0">
+<TR>
+<TH align=center>Email</TH>
+<TH align=center>lastemail</TH>
+<TH align=center>mxspace</TH>
+<TH align=center>locked</TH>
+</TR>
+$line
+</Table>
+$tblfooter
+</CENTER>
+<BR>
+<BR>
+$footer
+$stop
+EOT;
+
+return $STR;
+}
+
+
+rou_openlog();
+$dbsql=sql_connect();
+sql_setlogname($dbsql);
+sql_close($dbsql);
+
+if ($logname!=NULL) {
+  echo body($logname);
+  }
+else {
+  header('Location: lvllog.php');
+  }
+rou_closelog();
+?>
+
+
+
diff --git a/www/passval.html b/www/passval.html
new file mode 100644 (file)
index 0000000..3e5710b
--- /dev/null
@@ -0,0 +1,153 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1">
+<style>
+/* Style all input fields */
+input {
+  width: 100%;
+  padding: 12px;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  box-sizing: border-box;
+  margin-top: 6px;
+  margin-bottom: 16px;
+}
+
+/* Style the submit button */
+input[type=submit] {
+  background-color: #04AA6D;
+  color: white;
+}
+
+/* Style the container for inputs */
+.container {
+  background-color: #f1f1f1;
+  padding: 20px;
+}
+
+/* The message box is shown when the user clicks on the password field */
+#message {
+  display:none;
+  background: #f1f1f1;
+  color: #000;
+  position: relative;
+  padding: 20px;
+  margin-top: 10px;
+}
+
+#message p {
+  padding: 10px 35px;
+  font-size: 18px;
+}
+
+/* Add a green text color and a checkmark when the requirements are right */
+.valid {
+  color: green;
+}
+
+.valid:before {
+  position: relative;
+  left: -35px;
+  content: "✔";
+}
+
+/* Add a red text color and an "x" when the requirements are wrong */
+.invalid {
+  color: red;
+}
+
+.invalid:before {
+  position: relative;
+  left: -35px;
+  content: "✖";
+}
+</style>
+</head>
+<body>
+
+<h3>Password Validation</h3>
+<p>Try to submit the form.</p>
+
+<div class="container">
+  <form action="/action_page.php">
+    <label for="usrname">Username</label>
+    <input type="text" id="usrname" name="usrname" required>
+
+    <label for="psw">Password</label>
+    <input type="password" id="psw" name="psw" pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}" title="Must contain at least one number and one uppercase and lowercase letter, and at least 8 or more characters" required>
+    
+    <input type="submit" value="Submit">
+  </form>
+</div>
+
+<div id="message">
+  <h3>Password must contain the following:</h3>
+  <p id="letter" class="invalid">A <b>lowercase</b> letter</p>
+  <p id="capital" class="invalid">A <b>capital (uppercase)</b> letter</p>
+  <p id="number" class="invalid">A <b>number</b></p>
+  <p id="length" class="invalid">Minimum <b>8 characters</b></p>
+</div>
+                               
+<script>
+var myInput = document.getElementById("psw");
+var letter = document.getElementById("letter");
+var capital = document.getElementById("capital");
+var number = document.getElementById("number");
+var length = document.getElementById("length");
+
+// When the user clicks on the password field, show the message box
+myInput.onfocus = function() {
+  document.getElementById("message").style.display = "block";
+}
+
+// When the user clicks outside of the password field, hide the message box
+myInput.onblur = function() {
+  document.getElementById("message").style.display = "none";
+}
+
+// When the user starts to type something inside the password field
+myInput.onkeyup = function() {
+  // Validate lowercase letters
+  var lowerCaseLetters = /[a-z]/g;
+  if(myInput.value.match(lowerCaseLetters)) {  
+    letter.classList.remove("invalid");
+    letter.classList.add("valid");
+  } else {
+    letter.classList.remove("valid");
+    letter.classList.add("invalid");
+  }
+  
+  // Validate capital letters
+  var upperCaseLetters = /[A-Z]/g;
+  if(myInput.value.match(upperCaseLetters)) {  
+    capital.classList.remove("invalid");
+    capital.classList.add("valid");
+  } else {
+    capital.classList.remove("valid");
+    capital.classList.add("invalid");
+  }
+
+  // Validate numbers
+  var numbers = /[0-9]/g;
+  if(myInput.value.match(numbers)) {  
+    number.classList.remove("invalid");
+    number.classList.add("valid");
+  } else {
+    number.classList.remove("valid");
+    number.classList.add("invalid");
+  }
+  
+  // Validate length
+  if(myInput.value.length >= 8) {
+    length.classList.remove("invalid");
+    length.classList.add("valid");
+  } else {
+    length.classList.remove("valid");
+    length.classList.add("invalid");
+  }
+}
+</script>
+
+</body>
+</html>
diff --git a/www/reg-icons/Wwalczyszyn-Android-Style-Mail.96.png b/www/reg-icons/Wwalczyszyn-Android-Style-Mail.96.png
new file mode 100644 (file)
index 0000000..9bf62b5
Binary files /dev/null and b/www/reg-icons/Wwalczyszyn-Android-Style-Mail.96.png differ
diff --git a/www/reg-icons/folder.gif b/www/reg-icons/folder.gif
new file mode 100644 (file)
index 0000000..4826460
Binary files /dev/null and b/www/reg-icons/folder.gif differ
diff --git a/www/reg-icons/login.gif b/www/reg-icons/login.gif
new file mode 100644 (file)
index 0000000..e131950
Binary files /dev/null and b/www/reg-icons/login.gif differ
diff --git a/www/release.in b/www/release.in
new file mode 100644 (file)
index 0000000..ca4995e
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+//Information about application PHP data
+
+//release information
+$m_version=VERSION;
+$m_release=NEXTREL;
+$m_branch=BRANCH;
+$m_release=MAILREL;
+
+//constant
+$copyright="&copy; 2025 GPL_V3  (info@safe.ca)"
+
+?>
diff --git a/www/release.php b/www/release.php
new file mode 100644 (file)
index 0000000..488b832
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+//Information about application PHP data
+
+//release information
+$m_version="0.19";
+$m_release="11";
+$m_branch="dvl";
+$m_release="0.19-11-dvl";
+
+//constant
+$copyright="&copy; 2025 GPL_V3  (info@safe.ca)"
+
+?>
diff --git a/www/res/chat.php b/www/res/chat.php
new file mode 100644 (file)
index 0000000..fe1708f
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+$fields = ["Nom", "Email", "Âge"];
+$data = [
+    ["Alice", "alice@example.com", 25],
+    ["Bob", "bob@example.com", 30],
+    ["Charlie", "charlie@example.com", 22],
+];
+?>
+<!DOCTYPE html>
+<html lang="fr">
+<head>
+<meta charset="UTF-8">
+<title>Tableau filtrable "super débile écoeurant"</title>
+<style>
+table { border-collapse: collapse; width: 60%; margin-top: 10px; }
+th, td { border: 1px solid #000; padding: 6px 12px; text-align: left; cursor: pointer; transition: background-color 0.3s; }
+th.selected { background-color: #ffd700; color: #000; }
+td span.highlight { background-color: #ff4; font-weight: bold; }
+input { width: 60%; padding: 6px; font-size: 16px; }
+</style>
+</head>
+<body>
+
+<h2>Recherche filtrable “Super, débile, écoeurante” 😎</h2>
+
+<input type="text" id="searchInput" placeholder="Tapez pour filtrer...">
+
+<table id="myTable">
+    <thead>
+        <tr>
+            <?php foreach($fields as $field): ?>
+                <th><?= htmlspecialchars($field) ?></th>
+            <?php endforeach; ?>
+        </tr>
+    </thead>
+    <tbody>
+        <?php foreach($data as $row): ?>
+            <tr>
+                <?php foreach($row as $cell): ?>
+                    <td><?= htmlspecialchars($cell) ?></td>
+                <?php endforeach; ?>
+            </tr>
+        <?php endforeach; ?>
+    </tbody>
+</table>
+
+<script>
+// --- Setup ---
+const input = document.getElementById('searchInput');
+const table = document.getElementById('myTable');
+const rows = table.tBodies[0].rows;
+const headers = table.tHead.rows[0].cells;
+let activeCols = new Set([0]); // colonne(s) active(s) par défaut
+
+// --- Gestion du clic sur les en-têtes ---
+for (let i = 0; i < headers.length; i++) {
+    headers[i].addEventListener('click', () => {
+        if (activeCols.has(i)) {
+            activeCols.delete(i);
+            headers[i].classList.remove('selected');
+        } else {
+            activeCols.add(i);
+            headers[i].classList.add('selected');
+        }
+        filterTable();
+    });
+}
+
+// --- Fonction de filtrage et highlight ---
+input.addEventListener('input', filterTable);
+
+function filterTable() {
+    const query = input.value.toLowerCase();
+
+    for (let row of rows) {
+        let match = false;
+        for (let i = 0; i < row.cells.length; i++) {
+            const cell = row.cells[i];
+            const text = cell.textContent;
+            
+            if (activeCols.has(i) && text.toLowerCase().includes(query) && query !== "") {
+                // highlight
+                const regex = new RegExp(`(${escapeRegExp(query)})`, 'gi');
+                cell.innerHTML = text.replace(regex, '<span class="highlight">$1</span>');
+                match = true;
+            } else {
+                // reset
+                cell.textContent = text;
+            }
+        }
+        row.style.display = match || query === "" ? '' : 'none';
+    }
+}
+
+// --- Fonction helper pour regex ---
+function escapeRegExp(string) {
+    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
+// --- Initialisation visuelle ---
+for (let i of activeCols) headers[i].classList.add('selected');
+</script>
+
+</body>
+</html>
+
diff --git a/www/res/chat1.php b/www/res/chat1.php
new file mode 100644 (file)
index 0000000..1c88c09
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+$fields = ["Nom", "Email", "Âge"];
+$data = [
+    ["Alice", "alice@example.com", 25],
+    ["Bob", "bob@example.com", 30],
+    ["Charlie", "charlie@example.com", 22],
+];
+?>
+<!DOCTYPE html>
+<html lang="fr">
+<head>
+<meta charset="UTF-8">
+<title>Tableau Filtrable Ultime 😎</title>
+<style>
+body { font-family: sans-serif; }
+table { border-collapse: collapse; width: 60%; margin-top: 10px; }
+th, td { border: 1px solid #000; padding: 6px 12px; text-align: left; cursor: pointer; transition: background-color 0.3s, transform 0.2s; }
+th.selected { background-color: #ff9800; color: #fff; }
+td span.highlight { font-weight: bold; transition: background-color 0.3s; }
+input { width: 60%; padding: 6px; font-size: 16px; margin-bottom: 10px; }
+
+/* Confetti effect */
+.confetti { position: absolute; width: 6px; height: 6px; background-color: red; animation: confetti-fall 1s linear forwards; z-index: 1000; border-radius: 50%; }
+@keyframes confetti-fall { 0% { transform: translateY(0) rotate(0deg); opacity: 1; } 100% { transform: translateY(100px) rotate(360deg); opacity: 0; } }
+</style>
+</head>
+<body>
+
+<h2>Recherche “Ultime, Écoeurante, Joyeuse” 😎</h2>
+<input type="text" id="searchInput" placeholder="Tapez pour filtrer...">
+
+<table id="myTable">
+    <thead>
+        <tr>
+            <?php foreach($fields as $field): ?>
+                <th><?= htmlspecialchars($field) ?></th>
+            <?php endforeach; ?>
+        </tr>
+    </thead>
+    <tbody>
+        <?php foreach($data as $row): ?>
+            <tr>
+                <?php foreach($row as $cell): ?>
+                    <td><?= htmlspecialchars($cell) ?></td>
+                <?php endforeach; ?>
+            </tr>
+        <?php endforeach; ?>
+    </tbody>
+</table>
+
+<script>
+const input = document.getElementById('searchInput');
+const table = document.getElementById('myTable');
+const rows = table.tBodies[0].rows;
+const headers = table.tHead.rows[0].cells;
+
+// Colonne active par défaut
+let activeCols = new Set([0]);
+
+// Palette dynamique pour highlight par colonne
+const colors = ["#ffeb3b","#8bc34a","#03a9f4","#e91e63","#9c27b0"];
+
+// --- clic sur en-tête pour sélectionner/désélectionner colonnes ---
+for (let i = 0; i < headers.length; i++) {
+    headers[i].addEventListener('click', () => {
+        if (activeCols.has(i)) {
+            activeCols.delete(i);
+            headers[i].classList.remove('selected');
+        } else {
+            activeCols.add(i);
+            headers[i].classList.add('selected');
+        }
+        filterTable();
+    });
+}
+
+// --- filtrage et highlight ---
+input.addEventListener('input', filterTable);
+
+function filterTable() {
+    const query = input.value.toLowerCase();
+
+    for (let row of rows) {
+        let match = false;
+        for (let i = 0; i < row.cells.length; i++) {
+            const cell = row.cells[i];
+            const text = cell.textContent;
+
+            if (activeCols.has(i) && text.toLowerCase().includes(query) && query !== "") {
+                const regex = new RegExp(`(${escapeRegExp(query)})`, 'gi');
+                cell.innerHTML = text.replace(regex, `<span class="highlight" style="background-color:${colors[i % colors.length]}">$1</span>`);
+                match = true;
+            } else {
+                cell.textContent = text;
+            }
+        }
+        row.style.display = match || query === "" ? '' : 'none';
+        if (match && query !== "") {
+            triggerConfetti(row);
+            row.style.transform = "scale(1.02)";
+            setTimeout(()=>row.style.transform="scale(1)",200);
+        }
+    }
+}
+
+// --- helper regex ---
+function escapeRegExp(string) {
+    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
+// --- confetti ---
+function triggerConfetti(row) {
+    for (let i=0;i<5;i++){
+        const conf = document.createElement('div');
+        conf.className = 'confetti';
+        conf.style.left = (row.getBoundingClientRect().left + Math.random()*row.offsetWidth) + 'px';
+        conf.style.top = (row.getBoundingClientRect().top + window.scrollY) + 'px';
+        conf.style.backgroundColor = colors[Math.floor(Math.random()*colors.length)];
+        document.body.appendChild(conf);
+        setTimeout(()=>conf.remove(),1000);
+    }
+}
+
+// --- initialisation ---
+for (let i of activeCols) headers[i].classList.add('selected');
+</script>
+
+</body>
+</html>
+
diff --git a/www/res/chat2.php b/www/res/chat2.php
new file mode 100644 (file)
index 0000000..796ba8a
--- /dev/null
@@ -0,0 +1,141 @@
+<?php
+// ----------------------
+// Fichier PHP principal
+// ----------------------
+
+// Données du tableau
+$fields = ["Nom", "Email", "Âge"];
+$data = [
+    ["Alice", "alice@example.com", 25],
+    ["Bob", "bob@example.com", 30],
+    ["Charlie", "charlie@example.com", 22],
+];
+
+// Génération du HTML des en-têtes
+function generateTableHeader(array $fields): string {
+    $html = '';
+    foreach ($fields as $field) {
+        $html .= "<th>" . htmlspecialchars($field) . "</th>";
+    }
+    return $html;
+}
+
+// Génération du HTML du corps
+function generateTableBody(array $data): string {
+    $html = '';
+    foreach ($data as $row) {
+        $html .= "<tr>";
+        foreach ($row as $cell) {
+            $html .= "<td>" . htmlspecialchars($cell) . "</td>";
+        }
+        $html .= "</tr>";
+    }
+    return $html;
+}
+
+// Injection des contenus dans des variables
+$tableHeaderHTML = generateTableHeader($fields);
+$tableBodyHTML = generateTableBody($data);
+?>
+
+<!DOCTYPE html>
+<html lang="fr">
+<head>
+<meta charset="UTF-8">
+<title>Tableau filtrable Super</title>
+<style>
+table { border-collapse: collapse; width: 50%; margin-top: 10px; }
+th, td { border: 1px solid #000; padding: 4px 8px; text-align: left; cursor: pointer; }
+th.selected { background-color: #cce5ff; }
+td span.highlight { background-color: #ffeb3b; font-weight: bold; }
+input.filter-input { width: 50%; padding: 4px; margin-bottom: 8px; font-size: 14px; }
+</style>
+</head>
+<body>
+
+<h2>Tableau filtrable “Super”</h2>
+
+<input type="text" class="filter-input" placeholder="Tapez pour filtrer...">
+
+<table class="filter-table">
+    <thead>
+        <tr><?= $tableHeaderHTML ?></tr>
+    </thead>
+    <tbody>
+        <?= $tableBodyHTML ?>
+    </tbody>
+</table>
+
+<script>
+// Classe générique pour filtrer un tableau avec multi-colonnes
+class TableFilter {
+    constructor(tableElement, inputElement) {
+        this.table = tableElement;
+        this.input = inputElement;
+        this.rows = tableElement.tBodies[0].rows;
+        this.headers = tableElement.tHead.rows[0].cells;
+        this.activeCols = new Set([0]); // colonne(s) active(s) par défaut
+
+        this.init();
+    }
+
+    init() {
+        for (let i = 0; i < this.headers.length; i++) {
+            this.headers[i].addEventListener('click', () => {
+                // toggle colonne active
+                if (this.activeCols.has(i)) {
+                    this.activeCols.delete(i);
+                    this.headers[i].classList.remove('selected');
+                } else {
+                    this.activeCols.add(i);
+                    this.headers[i].classList.add('selected');
+                }
+                this.filter();
+            });
+        }
+
+        this.input.addEventListener('input', () => this.filter());
+
+        // Initialisation visuelle
+        for (let i of this.activeCols) this.headers[i].classList.add('selected');
+    }
+
+    filter() {
+        const query = this.input.value.toLowerCase();
+
+        for (let row of this.rows) {
+            let match = false;
+
+            for (let i = 0; i < row.cells.length; i++) {
+                const cell = row.cells[i];
+                const text = cell.textContent;
+
+                if (this.activeCols.has(i) && query && text.toLowerCase().includes(query)) {
+                    const regex = new RegExp(`(${this.escapeRegExp(query)})`, 'gi');
+                    cell.innerHTML = text.replace(regex, '<span class="highlight">$1</span>');
+                    match = true;
+                } else {
+                    cell.textContent = text; // reset highlight
+                }
+            }
+
+            // Affichage : au moins une colonne correspond
+            row.style.display = match || query === "" ? '' : 'none';
+        }
+    }
+
+    escapeRegExp(string) {
+        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+    }
+}
+
+// Instanciation générique pour tous les tableaux
+document.querySelectorAll('.filter-table').forEach(table => {
+    const input = document.querySelector('.filter-input');
+    new TableFilter(table, input);
+});
+</script>
+
+</body>
+</html>
+
diff --git a/www/res/chat3.php b/www/res/chat3.php
new file mode 100644 (file)
index 0000000..9debfeb
--- /dev/null
@@ -0,0 +1,169 @@
+<?php
+/* =======================================================
+   Super-DB One-File avec pagination SQL et multi-colonnes
+   Layout corrigé : [Lang] seule ligne, [Recherche][Pagination] sur ligne suivante
+   ======================================================= */
+class TableFilter
+{
+    private PDO $pdo;
+    private string $table;
+    private array $columns;
+
+    public function __construct(PDO $pdo, string $table, array $columns)
+    {
+        $this->pdo = $pdo;
+        $this->table = $table;
+        $this->columns = $columns;
+    }
+
+    public function fetchFiltered(array $selectedColumns, string $search, int $limit): array
+    {
+        if(empty($selectedColumns) || trim($search)===''){
+            $sql = "SELECT * FROM {$this->table} ORDER BY creation DESC LIMIT :limit";
+            $stmt = $this->pdo->prepare($sql);
+            $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchAll(PDO::FETCH_ASSOC);
+        }
+
+        $whereParts = [];
+        $params = [];
+        foreach($selectedColumns as $i => $col){
+            if(!in_array($col,$this->columns,true)) continue;
+            $whereParts[] = "$col ILIKE :s$i";
+            $params[":s$i"] = "%$search%";
+        }
+        $where = implode(" OR ", $whereParts);
+        $sql = "SELECT * FROM {$this->table} WHERE $where ORDER BY creation DESC LIMIT :limit";
+        $stmt = $this->pdo->prepare($sql);
+        foreach($params as $k=>$v) $stmt->bindValue($k,$v);
+        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
+        $stmt->execute();
+        return $stmt->fetchAll(PDO::FETCH_ASSOC);
+    }
+}
+
+/* =======================================================
+   Gestion AJAX
+   ======================================================= */
+if(php_sapi_name()!=='cli' && isset($_POST['search'])){
+    header("Content-Type: application/json");
+    $pdo = new PDO("pgsql:host=localhost;dbname=mailleur","mailleur","mailleurpass",
+        [PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
+    $filter = new TableFilter($pdo,"actions",["remoteip","reverse","creation"]);
+    $selected = $_POST["columns"]??[];
+    $search   = $_POST["search"]??"";
+    $limit    = (int)($_POST["limit"]??20);
+    $data = $filter->fetchFiltered($selected,$search,$limit);
+    echo json_encode($data);
+    exit;
+}
+
+/* =======================================================
+   Page HTML
+   ======================================================= */
+$lang = $_GET["lang"]??"en";
+$labels = ["en"=>["Remote IP","Reverse","Creation"],"fr"=>["IP distante","Reverse","Création"]];
+$colKeys = ["remoteip","reverse","creation"];
+$L = $labels[$lang];
+?>
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Bigre Super-DB Paginated JMP</title>
+<style>
+.table-filter-row { display:flex; gap:8px; align-items:center; margin-bottom:6px; flex-wrap: wrap; }
+#langSelect-row { display:block; margin-bottom:12px; } /* isole Lang sur sa propre ligne */
+table{border-collapse:collapse;width:100%;}
+th,td{padding:6px;border:1px solid #888;}
+.filter-col{cursor:pointer;color:#003;background:#f7f7ff;}
+.filter-col.active{background:#cce;}
+</style>
+</head>
+<body>
+
+<!-- Ligne 1 : Langue -->
+<div id="langSelect-row">
+    <select id="langSelect" onchange="window.location='?lang='+this.value;">
+        <option value="en" <?= $lang==="en"?"selected":"" ?>>English</option>
+        <option value="fr" <?= $lang==="fr"?"selected":"" ?>>Français</option>
+    </select>
+</div>
+
+<!-- Ligne 2 : Recherche + Pagination -->
+<div class="table-filter-row">
+    <input type="text" id="searchInput" placeholder="Type to filter…">
+    <select id="limitSelect">
+        <?php foreach([5,10,20,40,80] as $n): ?>
+            <option value="<?= $n ?>" <?= $n===20?"selected":"" ?>><?= $n ?></option>
+        <?php endforeach; ?>
+    </select>
+</div>
+
+<!-- Tableau -->
+<table id="dataTable">
+    <thead>
+        <tr>
+            <?php foreach($L as $i=>$label): ?>
+                <th class="filter-col" data-col="<?= $colKeys[$i] ?>"><?= htmlspecialchars($label) ?></th>
+            <?php endforeach; ?>
+        </tr>
+    </thead>
+    <tbody></tbody>
+</table>
+
+<script>
+let columns=[];
+const searchInput = document.getElementById("searchInput");
+const limitSelect = document.getElementById("limitSelect");
+
+// Header click pour multi-colonnes
+document.querySelectorAll(".filter-col").forEach(th=>{
+    th.addEventListener("click",()=>{
+        const col = th.dataset.col;
+        if(columns.includes(col)){
+            columns = columns.filter(c=>c!==col);
+            th.classList.remove("active");
+        } else {
+            columns.push(col);
+            th.classList.add("active");
+        }
+        refresh();
+    });
+});
+
+// Recherche et changement de limit
+searchInput.addEventListener("input",refresh);
+limitSelect.addEventListener("change",refresh);
+
+// AJAX vers le même fichier
+function refresh(){
+    const form = new FormData();
+    form.append("search", searchInput.value);
+    form.append("limit", limitSelect.value);
+    columns.forEach(c=>form.append("columns[]",c));
+
+    fetch("", {method:"POST", body:form})
+        .then(r=>r.json())
+        .then(json=>renderTable(json));
+}
+
+// Remplissage tableau
+function renderTable(rows){
+    const tbody=document.querySelector("#dataTable tbody");
+    tbody.innerHTML="";
+    for(const row of rows){
+        const tr=document.createElement("tr");
+        tr.innerHTML=`<td>${row.remoteip}</td><td>${row.reverse}</td><td>${row.creation}</td>`;
+        tbody.appendChild(tr);
+    }
+}
+
+// Load initial
+refresh();
+</script>
+
+</body>
+</html>
+
diff --git a/www/res/chat4.php b/www/res/chat4.php
new file mode 100644 (file)
index 0000000..91da797
--- /dev/null
@@ -0,0 +1,192 @@
+<?php
+/* =======================================================
+   Super-DB One-File corrigé : affichage NULL explicite
+   ======================================================= */
+class TableFilter
+{
+    private PDO $pdo;
+    private string $table;
+    private array $columns;
+
+    public function __construct(PDO $pdo, string $table, array $columns)
+    {
+        $this->pdo = $pdo;
+        $this->table = $table;
+        $this->columns = $columns;
+    }
+
+    public function fetchFiltered(array $selectedColumns, string $search, int $limit): array
+    {
+        if(empty($selectedColumns)) $selectedColumns = $this->columns;
+
+        if(trim($search)===''){
+            $sql = "SELECT * FROM {$this->table} ORDER BY creation DESC LIMIT :limit";
+            $stmt = $this->pdo->prepare($sql);
+            $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchAll(PDO::FETCH_ASSOC);
+        }
+
+        $whereParts = [];
+        $params = [];
+        foreach($selectedColumns as $i => $col){
+            if(!in_array($col,$this->columns,true)) continue;
+            $key = ":s$i";
+            $whereParts[] = "$col ILIKE $key";
+            $params[$key] = "%$search%";
+        }
+
+        $where = implode(" OR ", $whereParts);
+        $sql = "SELECT * FROM {$this->table} WHERE $where ORDER BY creation DESC LIMIT :limit";
+        $stmt = $this->pdo->prepare($sql);
+
+        foreach($params as $k=>$v){
+            $stmt->bindValue($k,$v,PDO::PARAM_STR);
+        }
+        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
+        $stmt->execute();
+        return $stmt->fetchAll(PDO::FETCH_ASSOC);
+    }
+}
+
+/* =======================================================
+   Gestion AJAX
+   ======================================================= */
+if(php_sapi_name()!=='cli' && isset($_POST['search'])){
+    header("Content-Type: application/json");
+    $pdo = new PDO("pgsql:host=localhost;dbname=mailleur","mailleur","mailleurpass",
+        [PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
+    $filter = new TableFilter($pdo,"actions",["remoteip","reverse","creation"]);
+
+    $selected = $_POST["columns"]??[];
+    if(empty($selected)) $selected = ["remoteip","reverse","creation"];
+
+    $search   = $_POST["search"]??"";
+    $limit    = (int)($_POST["limit"]??20);
+    $data = $filter->fetchFiltered($selected,$search,$limit);
+    echo json_encode(["rows"=>$data,"search"=>$search]);
+    exit;
+}
+
+/* =======================================================
+   Page HTML
+   ======================================================= */
+$lang = $_GET["lang"]??"en";
+$labels = ["en"=>["Remote IP","Reverse","Creation"],"fr"=>["IP distante","Reverse","Création"]];
+$colKeys = ["remoteip","reverse","creation"];
+$L = $labels[$lang];
+?>
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Super-DB Paginated Highlight</title>
+<style>
+.table-filter-row { display:flex; gap:8px; align-items:center; margin-bottom:6px; flex-wrap: wrap; }
+#langSelect-row { display:block; margin-bottom:12px; }
+table{border-collapse:collapse;width:100%;}
+th,td{padding:6px;border:1px solid #888;}
+.filter-col{cursor:pointer;color:#003;background:#f7f7ff;}
+.filter-col.active{background:#cce;}
+.highlight{background:#ff9;color:#000;}
+</style>
+</head>
+<body>
+
+<!-- Ligne 1 : Langue -->
+<div id="langSelect-row">
+    <select id="langSelect" onchange="window.location='?lang='+this.value;">
+        <option value="en" <?= $lang==="en"?"selected":"" ?>>English</option>
+        <option value="fr" <?= $lang==="fr"?"selected":"" ?>>Français</option>
+    </select>
+</div>
+
+<!-- Ligne 2 : Recherche + Pagination -->
+<div class="table-filter-row">
+    <input type="text" id="searchInput" placeholder="Type to filter…">
+    <select id="limitSelect">
+        <?php foreach([5,10,20,40,80] as $n): ?>
+            <option value="<?= $n ?>" <?= $n===20?"selected":"" ?>><?= $n ?></option>
+        <?php endforeach; ?>
+    </select>
+</div>
+
+<!-- Tableau -->
+<table id="dataTable">
+    <thead>
+        <tr>
+            <?php foreach($L as $i=>$label): ?>
+                <th class="filter-col" data-col="<?= $colKeys[$i] ?>"><?= htmlspecialchars($label) ?></th>
+            <?php endforeach; ?>
+        </tr>
+    </thead>
+    <tbody></tbody>
+</table>
+
+<script>
+let columns=[];
+let currentSearch="";
+
+const searchInput = document.getElementById("searchInput");
+const limitSelect = document.getElementById("limitSelect");
+
+// Header click pour multi-colonnes
+document.querySelectorAll(".filter-col").forEach(th=>{
+    th.addEventListener("click",()=>{
+        const col = th.dataset.col;
+        if(columns.includes(col)){
+            columns = columns.filter(c=>c!==col);
+            th.classList.remove("active");
+        } else {
+            columns.push(col);
+            th.classList.add("active");
+        }
+        refresh();
+    });
+});
+
+// Recherche et changement de limit
+searchInput.addEventListener("input",refresh);
+limitSelect.addEventListener("change",refresh);
+
+// AJAX vers le même fichier
+function refresh(){
+    currentSearch = searchInput.value.trim();
+    const form = new FormData();
+    form.append("search", currentSearch);
+    form.append("limit", limitSelect.value);
+
+    const activeColumns = columns.length ? columns : ["remoteip","reverse","creation"];
+    activeColumns.forEach(c=>form.append("columns[]",c));
+
+    fetch("", {method:"POST", body:form})
+        .then(r=>r.json())
+        .then(json=>renderTable(json.rows,json.search));
+}
+
+// Remplissage tableau avec surbrillance
+function renderTable(rows,search){
+    console.log("AJAX: nombre de lignes reçues =", rows.length);
+    const tbody=document.querySelector("#dataTable tbody");
+    tbody.innerHTML="";
+    const re = search ? new RegExp(search,"gi") : null;
+    for(const row of rows){
+        const tr=document.createElement("tr");
+        const cells = ["remoteip","reverse","creation"].map(c=>{
+            // NULL -> "NULL"
+            let val = row[c] === null ? "NULL" : row[c];
+            if(re) val = val.replace(re,m=>`<span class="highlight">${m}</span>`);
+            return `<td>${val}</td>`;
+        });
+        tr.innerHTML=cells.join("");
+        tbody.appendChild(tr);
+    }
+}
+
+// Load initial
+refresh();
+</script>
+
+</body>
+</html>
+
diff --git a/www/res/gestbl.php b/www/res/gestbl.php
new file mode 100644 (file)
index 0000000..d64cf41
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+/* =======================================================
+   TableFilter.php -->gestbl.php
+   Generic and autonomous class to search an filter table
+   contents.
+   ======================================================= */
+
+class TableFilter {
+
+private PDO $pdo;
+private string $table;
+private array $columns;
+private int $limit;
+
+/**
+ * Constructor
+ * @param PDO $pdo      PDO connection
+ * @param string $table Table name
+ * @param array $columns Columns available for filtering
+ **/
+public function __construct(PDO $pdo, string $table, array $columns) {
+  $this->pdo=$pdo;
+  $this->table=$table;
+  $this->columns=$columns;
+  $this->limit=20;
+  }
+
+/**
+ * Set LIMIT for extraction and filtering
+ **/
+
+/**
+ * Fetch filtered rows from the table
+ * 
+ * @param array $selectedColumns Columns to filter (subset of $this->columns)
+ * @param string $search Search text
+ * @param int $limit Number of rows to return
+ * @return array Associative array of rows
+ **/
+public function fetchFiltered(array $selectedColumns,string $search=''): array {
+  // If no columns selected, use all
+  if (empty($selectedColumns)) $selectedColumns = $this->columns;
+
+  // If search is empty, just SELECT with LIMIT
+  if (trim($search) === '') {
+    $sql = "SELECT * FROM {$this->table} ORDER BY 1 LIMIT :limit";
+    $stmt = $this->pdo->prepare($sql);
+    $stmt->bindValue(':limit', $this->limit, PDO::PARAM_INT);
+    $stmt->execute();
+    return $stmt->fetchAll(PDO::FETCH_ASSOC);
+    }
+
+  // Build WHERE clause for multiple columns
+  $whereParts = [];
+  $params = [];
+  foreach ($selectedColumns as $i => $col) {
+    if (!in_array($col, $this->columns, true)) continue;
+    $key = ":s$i";
+    $whereParts[] = "$col ILIKE $key";
+    $params[$key] = "%$search%";
+    }
+  $where = implode(" OR ", $whereParts);
+  // Prepare and execute statement
+  $sql = "SELECT * FROM {$this->table} WHERE $where ORDER BY 1 LIMIT :limit";
+  $stmt = $this->pdo->prepare($sql);
+  foreach ($params as $k => $v) {
+    $stmt->bindValue($k, $v, PDO::PARAM_STR);
+    }
+  $stmt->bindValue(':limit', $this->limit, PDO::PARAM_INT);
+  $stmt->execute();
+  // Return associative array
+  return $stmt->fetchAll(PDO::FETCH_ASSOC);
+  }
+}
+
diff --git a/www/res/unilng.php b/www/res/unilng.php
new file mode 100644 (file)
index 0000000..6029e83
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+//==============================================================
+//
+//     To manage language
+//
+//==============================================================
+include_once "subrou.php";
+
+
+//list of available word
+
+enum lng {
+  case en;     //English default language
+  case fr;     //French
+  case un;     //Unknown language (english)
+  }
+
+$userlang=lng::en;
+
+function gettranslate(lng $lang,$mot)
+
+{
+
+$mot=$mot."_X";
+return $mot;
+}
+?>
diff --git a/www/res/unimar.php b/www/res/unimar.php
new file mode 100644 (file)
index 0000000..a508534
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+//==============================================================
+//
+//     To manage all access to MariaDB database
+//
+//==============================================================
+
+//==============================================================
+//     To connect to a local/remote MYSQL database
+//==============================================================
+function mar_connect($dbhost,$dbusr,$dbname,$dbport)
+
+{
+$OPEP="unimar.php, mar_connect";
+
+$conn=mysqli_connect($dbhost,$dbusr,'',$dbname,$dbport);
+if (mysqli_connect_error()) {
+  }
+return $conn;
+}
+
+
+//==============================================================
+//     To disconnect from a local/remote MYSQL database
+//==============================================================
+function mar_close($conn)
+
+{
+$OPEP="unimar.php, mar_close";
+
+mysqli_close($conn);
+return 0;
+}
+?>
diff --git a/www/res/unipos.php b/www/res/unipos.php
new file mode 100644 (file)
index 0000000..f8c0856
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+//==============================================================
+//
+//     To manage all access to Postgresql database
+//
+//==============================================================
+
+class pgsql     {
+  private $connection = null;
+  // this function is called everytime this class is instantiated
+
+  public function __construct()        {
+    $this->connection = new PDO("mysql:host={$dbhost};dbname={$dbname};",$username);
+    $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+    $this->connection->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
+    }
+
+       // Insert a row/s in a Database Table
+  public function Insert()     {
+    }  
+
+  // Select a row/s in a Database Table
+  public function Select()      {
+    }
+
+  // Update a row/s in a Database Table
+  public function Update( )     {
+    }    
+
+  // Remove a row/s in a Database Table
+  public function Remove( )     {
+  }    
+
+  // execute statement
+  private function executeStatement( ){
+    }
+
+  }
+
+//==============================================================
+//     To connect to a local/remote POSTGRESQL database
+//==============================================================
+function pos_connect($dbhost,$dbusr,$dbname,$dbport)
+
+{
+$OPEP="unipop.php, pos_connect";
+
+$seq="host=$dbhost user=$dbusr dbname=$dbname port=$dbport";
+$conn=pg_connect($seq);
+return $conn;
+}
+
+
+//==============================================================
+//     To disconnect from a local/remote POSTGRESQL database
+//==============================================================
+function pos_close(PgSql\Connection $sqlptr)
+
+{
+$OPEP="unipos, pos_close";
+
+pg_close($sqlptr);
+unset($sqlptr);
+return 0;
+}
+?>
+
diff --git a/www/selectajax.js b/www/selectajax.js
new file mode 100644 (file)
index 0000000..62fbe86
--- /dev/null
@@ -0,0 +1,87 @@
+/*!
+ * selectajax.js
+ *
+ * Handles AJAX POST for column selection and search,
+ * updates the HTML table dynamically in lvlmai.php.
+ *
+ * Dependencies: none (vanilla JS)
+ *
+ * Main contributor: Chap, ID-20251216-001
+ */
+
+document.addEventListener('DOMContentLoaded', function() {
+
+    const form = document.getElementById('searchForm');
+    if (!form) return; // safety if form is missing
+
+    form.addEventListener('submit', function(e) {
+        e.preventDefault(); // prevent page reload
+
+        const formData = new FormData(form);
+        formData.set('ajax', '1');
+
+        // Safety retry mechanism to wait for DOM readiness
+        const maxRetries = 20;
+        let retryCount = 0;
+
+        function sendFetch() {
+            fetch('lvlmai.php', { method: 'POST', body: formData })
+                .then(resp => resp.json())
+                .then(data => {
+                    const headerRow = document.getElementById('headerRow');
+                    const bodyRows = document.getElementById('bodyRows');
+
+                    if (!headerRow || !bodyRows) {
+                        if (retryCount < maxRetries) {
+                            retryCount++;
+                            console.warn(`DOM not ready yet, retry ${retryCount}`);
+                            setTimeout(sendFetch, 100); // retry after 100ms
+                        } else {
+                            console.error("headerRow or bodyRows not found after max retries");
+                        }
+                        return;
+                    }
+
+                    // Clear previous table content
+                    //headerRow.innerHTML = '';
+                    bodyRows.innerHTML = '';
+
+                    if (!data || !data.rows || data.rows.length === 0) return;
+
+                    // Dynamic headers
+                    //Object.keys(data.rows[0]).forEach(col => {
+                     //   const th = document.createElement('th');
+                      //  th.textContent = col;
+                       // headerRow.appendChild(th);
+                    //});
+
+                    // Table rows
+                    data.rows.forEach(row => {
+                        const tr = document.createElement('tr');
+                        Object.values(row).forEach(val => {
+                            const td = document.createElement('td');
+                            td.textContent = val ?? ''; // handle nulls
+                            tr.appendChild(td);
+                        });
+                        bodyRows.appendChild(tr);
+                    });
+
+                    console.log("JMPDBG data:", data);
+
+                    // Dispatch event for other scripts (translations, color, sorting)
+                    document.dispatchEvent(new CustomEvent('tableDataUpdated', {
+                        detail: {
+                            rowsCount: data.rows.length,
+                            search: data.search ?? ''
+                        }
+                    }));
+
+                })
+                .catch(err => console.error('AJAX Error:', err));
+        }
+
+        sendFetch();
+    });
+
+});
+
diff --git a/www/selectajax.js.ang b/www/selectajax.js.ang
new file mode 100644 (file)
index 0000000..3791aed
--- /dev/null
@@ -0,0 +1,88 @@
+/**
+ * selectajax.js
+ *
+ * Handles AJAX POST for column selection and search input,
+ * then dynamically updates the HTML table in lvlmai.php.
+ *
+ * Dependencies: vanilla JS only
+ *
+ * Main contributor: Chap, iteration-ref: 20251216-001
+ *
+ * Usage: include this script at the end of the HTML body to ensure DOM readiness.
+ */
+
+function initSelectAjax(retryCount = 0) {
+    const maxRetries = 20;   // maximum retry attempts if DOM not ready
+
+    const form = document.getElementById('searchForm');
+    const headerRow = document.getElementById('headerRow');
+    const bodyRows = document.getElementById('bodyRows');
+
+    if (!form || !headerRow || !bodyRows) {
+        if (retryCount < maxRetries) {
+            console.log("initSelectAjax: DOM not ready, retry #" + (retryCount + 1));
+            setTimeout(() => initSelectAjax(retryCount + 1), 100); // wait 100ms
+        } else {
+            console.error("initSelectAjax: DOM elements not found after max retries");
+        }
+        return;
+    }
+
+    form.addEventListener('submit', function(e) {
+        e.preventDefault(); // prevent page reload
+
+        const formData = new FormData(form);
+        formData.set('ajax', '1');
+
+        fetch('lvlmai.php', {
+            method: 'POST',
+            body: formData
+        })
+        .then(resp => resp.text())
+        .then(text => {
+            console.log("Raw response:", text);
+            try {
+                return JSON.parse(text);
+            } catch(err) {
+                console.error("Failed to parse JSON:", err);
+                throw err;
+            }
+        })
+        .then(data => {
+            if (!data.rows) {
+                console.error("No 'rows' in response data");
+                return;
+            }
+
+            // Clear previous table content
+            headerRow.innerHTML = '';
+            bodyRows.innerHTML = '';
+
+            // Populate table header dynamically
+            const keys = Object.keys(data.rows[0]);
+            keys.forEach(col => {
+                const th = document.createElement('th');
+                th.textContent = col;
+                headerRow.appendChild(th);
+            });
+
+            // Populate table body
+            data.rows.forEach(row => {
+                const tr = document.createElement('tr');
+                keys.forEach(key => {
+                    const td = document.createElement('td');
+                    td.textContent = row[key];
+                    tr.appendChild(td);
+                });
+                bodyRows.appendChild(tr);
+            });
+
+            console.log("Table updated with", data.rows.length, "rows");
+        })
+        .catch(err => console.error("AJAX error:", err));
+    });
+}
+
+// Initialize after script load
+document.addEventListener('DOMContentLoaded', initSelectAjax);
+
diff --git a/www/selectajax.js.bad b/www/selectajax.js.bad
new file mode 100644 (file)
index 0000000..8920fd5
--- /dev/null
@@ -0,0 +1,67 @@
+document.addEventListener('DOMContentLoaded', function() {
+
+    const form = document.getElementById('searchForm');
+    if (!form) return;
+
+    form.addEventListener('submit', function(e) {
+        e.preventDefault();
+
+        const formData = new FormData(form);
+        formData.set('ajax', '1');
+
+        fetch('lvlmai.php', {
+            method: 'POST',
+            body: formData
+        })
+        .then(response => {
+            if (!response.ok) {
+                throw new Error("HTTP error " + response.status);
+            }
+            return response.json();   // 🔴 ÉTAPE MANQUANTE
+        })
+        .then(data => {
+
+            console.log("JMPDBG data:", data);
+
+            if (!data.rows) {
+                console.error("No data.rows in response");
+                return;
+            }
+
+            const headerRow = document.getElementById('headerRow');
+            const bodyRows  = document.getElementById('bodyRows');
+
+            if (!headerRow || !bodyRows) {
+                console.error("thead or tbody not found");
+                return;
+            }
+
+            headerRow.innerHTML = '';
+            bodyRows.innerHTML  = '';
+
+            console.log("JMPDBG rows:", data.rows.length);
+
+            if (data.rows.length === 0) return;
+
+            // Headers
+            Object.keys(data.rows[0]).forEach(col => {
+                const th = document.createElement('th');
+                th.textContent = col;
+                headerRow.appendChild(th);
+            });
+
+            // Rows
+            data.rows.forEach(row => {
+                const tr = document.createElement('tr');
+                Object.values(row).forEach(val => {
+                    const td = document.createElement('td');
+                    td.textContent = val ?? '';
+                    tr.appendChild(td);
+                });
+                bodyRows.appendChild(tr);
+            });
+        })
+        .catch(err => console.error("AJAX error:", err));
+    });
+});
+
diff --git a/www/selectajax.js.fra b/www/selectajax.js.fra
new file mode 100644 (file)
index 0000000..3489907
--- /dev/null
@@ -0,0 +1,64 @@
+function initSelectAjax(retryCount = 0) {
+    const maxRetries = 20;   // nombre maximum de tentatives
+    const form = document.getElementById('searchForm');
+    const headerRow = document.getElementById('headerRow');
+    const bodyRows = document.getElementById('bodyRows');
+
+    if (!form || !headerRow || !bodyRows) {
+        if (retryCount < maxRetries) {
+            console.warn(`DOM incomplet, réessai ${retryCount + 1}/${maxRetries} dans 100ms...`);
+            setTimeout(() => initSelectAjax(retryCount + 1), 100);
+        } else {
+            console.error("Échec d'initialisation AJAX : DOM incomplet après 20 tentatives");
+        }
+        return;
+    }
+
+    form.addEventListener('submit', function(e) {
+        e.preventDefault();
+
+        const formData = new FormData(form);
+        formData.set('ajax', '1');
+
+        fetch('lvlmai.php', { method: 'POST', body: formData })
+            .then(resp => resp.text())
+            .then(text => {
+                console.log("Réponse brute :", text);
+                try {
+                    return JSON.parse(text);
+                } catch(err) {
+                    console.error("Erreur parsing JSON :", err, text);
+                    throw err;
+                }
+            })
+            .then(data => {
+                headerRow.innerHTML = '';
+                bodyRows.innerHTML = '';
+
+                if (!data.rows || data.rows.length === 0) return;
+
+                // en-têtes
+                Object.keys(data.rows[0]).forEach(col => {
+                    const th = document.createElement('th');
+                    th.textContent = col;
+                    headerRow.appendChild(th);
+                });
+
+                // lignes
+                data.rows.forEach(row => {
+                    const tr = document.createElement('tr');
+                    Object.values(row).forEach(val => {
+                        const td = document.createElement('td');
+                        td.textContent = val ?? '';
+                        tr.appendChild(td);
+                    });
+                    bodyRows.appendChild(tr);
+                });
+            })
+            .catch(err => console.error('Erreur AJAX:', err));
+    });
+}
+
+// Appel sécurisé au chargement du DOM
+document.addEventListener('DOMContentLoaded', () => initSelectAjax());
+
diff --git a/www/subrou.php b/www/subrou.php
new file mode 100644 (file)
index 0000000..139d148
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+//==============================================================
+//
+//     To manage very low level function
+//
+//==============================================================
+//gobal variables.
+$debug=0;              //default debug level
+$logname=NULL;         //authenticated user name
+$isadmin=0;            //authenticated user with admin level
+
+//==============================================================
+//     Open a syslog channel
+//==============================================================
+function rou_openlog()
+
+{
+openlog("Mailleur (PHP)",LOG_PID,LOG_DAEMON);
+}
+
+//==============================================================
+//     close a syslog channel
+//==============================================================
+
+function rou_closelog()
+
+{
+closelog();
+}
+//==============================================================
+//     log data on syslog channel
+//==============================================================
+function rou_alert($dbglvl,$report)
+
+{
+global $debug;
+
+if ($debug>=$dbglvl)
+  syslog(LOG_INFO,"dbg=$debug, $report");
+}
+
+rou_openlog();
+?>
diff --git a/www/theme.css b/www/theme.css
new file mode 100644 (file)
index 0000000..9419ea5
--- /dev/null
@@ -0,0 +1,66 @@
+/* theme.css */
+
+/* =====================
+   Light Theme (default)
+   ===================== */
+:root {
+  --bg-color: white;
+  --text-color: black;
+
+  --highlight-bg: lightyellow;
+  --highlight-text: black;
+
+  --filter-col-bg: lavender;
+  --filter-col-hover-bg: lightsteelblue;
+  --filter-col-active-bg: cornflowerblue;
+  --filter-col-text: darkblue;
+}
+
+/* =====================
+   Dark Theme
+   ===================== */
+.dark {
+  --bg-color: black;
+  --text-color: gainsboro;
+
+  --highlight-bg: darkslategray;
+  --highlight-text: lightgoldenrodyellow;
+
+  --filter-col-bg: dimgray;
+  --filter-col-hover-bg: slategray;
+  --filter-col-active-bg: steelblue;
+  --filter-col-text: lightblue;
+}
+
+/* =====================
+   Generic elements
+   ===================== */
+body {
+  background-color: var(--bg-color);
+  color: var(--text-color);
+  font-family: sans-serif;
+}
+
+/* Column header behavior */
+.filter-col {
+  cursor: pointer;
+  color: var(--filter-col-text);
+  background-color: var(--filter-col-bg);
+}
+
+/* Mouse hover on column header */
+.filter-col:hover {
+  background-color: var(--filter-col-hover-bg);
+}
+
+/* Active (selected) column header */
+.filter-col.active {
+  background-color: var(--filter-col-active-bg);
+}
+
+/* Highlighted table cells or rows */
+.highlight {
+  background-color: var(--highlight-bg);
+  color: var(--highlight-text);
+}
+
diff --git a/www/theme2.css b/www/theme2.css
new file mode 100644 (file)
index 0000000..60b9b3f
--- /dev/null
@@ -0,0 +1,52 @@
+/* theme.css */
+
+/* =====================
+   Light Theme (default)
+   ===================== */
+:root {
+  --bg-color: #ffffff;          /* background color */
+  --text-color: #000000;        /* text color */
+  --highlight-bg: #ff9;         /* highlight background */
+  --highlight-text: #000;       /* highlight text color */
+  --filter-col-bg: #f7f7ff;     /* filter column background */
+  --filter-col-active-bg: #cce; /* active filter column background */
+  --filter-col-text: #003;      /* filter column text color */
+}
+
+/* =====================
+   Dark Theme
+   ===================== */
+.dark {
+  --bg-color: #1e1e1e;
+  --text-color: #e0e0e0;
+  --highlight-bg: #553333;
+  --highlight-text: #fff0b0;
+  --filter-col-bg: #333344;
+  --filter-col-active-bg: #5566aa;
+  --filter-col-text: #aaccff;
+}
+
+/* =====================
+   Generic elements using variables
+   ===================== */
+body {
+  background-color: var(--bg-color);
+  color: var(--text-color);
+  font-family: sans-serif;
+}
+
+.filter-col {
+  cursor: pointer;
+  color: var(--filter-col-text);
+  background: var(--filter-col-bg);
+}
+
+.filter-col.active {
+  background: var(--filter-col-active-bg);
+}
+
+.highlight {
+  background: var(--highlight-bg);
+  color: var(--highlight-text);
+}
+
diff --git a/www/translations.js b/www/translations.js
new file mode 100644 (file)
index 0000000..4ad97c7
--- /dev/null
@@ -0,0 +1,90 @@
+// vim: smarttab tabstop=8 shiftwidth=2 expandtab
+
+//language table
+const TR =
+  [
+    ["Authentication",  "Authentification"],
+    ["Date",            "Date"],
+    ["Email",           "Courriel"],
+    ["Email management","Gestion des courriers"],
+    ["Login",           "Connexion"],
+    ["Logout",          "Deconnexion"],
+    ["Main screen",     "Ecran Principal"],
+    ["Originator",      "Expediteur"],
+    ["Password",        "Mot de passe"],
+    ["Recipient",       "Destinataire"],
+    ["Reverse IP",      "IP inversée"],
+    ["SMTP servers",    "Serveurs SMTP"],
+    ["Search",          "Recherche"],
+    ["Subject",         "Sujet"],
+    ["Show",            "Afficher"],
+    ["Status",          "Statut"],
+    ["Users management","Gestion des usagers"],
+    [null,              null]
+  ];
+
+var translationMap = new Map();
+
+// Building EN/FR dictionnaries
+const translationEn = new Map();
+const translationFr = new Map();
+
+for (const [en, fr] of TR) {
+  if (en === null) break; //detecting end of table
+  translationEn.set(en, en);
+  translationFr.set(en, fr);
+  }
+
+translationMap.set("en", translationEn);
+translationMap.set("fr", translationFr);
+
+//==============================================================
+//function to translate login screen
+//==============================================================
+function updateTranslations() 
+
+{
+let translationLng = document.getElementById("first-choice").value;
+let translatables = document.getElementsByClassName("translatable");
+let map = translationMap.get(translationLng);
+
+for (let i of translatables) {
+  let key = i.getAttribute("trkey") || i.parentElement.getAttribute("trkey");
+
+// gestion si la clé n'existe pas
+  let value;
+  if (map.has(key)) {
+    value = map.get(key);
+    }
+  else {
+    console.warn(`Traduction manquante pour "${key}"`);
+    value = `<i>${key}</i>`; // valeur par défaut : le mot anglais
+    }
+
+  if (i.tagName === 'INPUT') {
+    i.value = key;
+    }
+  else {
+    i.innerHTML = value;
+    }
+  }
+}
+
+//==============================================================
+//function to show password contents
+//==============================================================
+function showpass()
+
+{
+let datain=document.getElementById("myInput");
+if (datain.type==="password") {
+  datain.type="text";
+  }
+else {
+  datain.type="password";
+  }
+}
+
+//execute function at first display
+updateTranslations();
+//==============================================================
diff --git a/www/unienv.php b/www/unienv.php
new file mode 100644 (file)
index 0000000..d2be85c
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+//==============================================================
+//
+//     To load environment variable
+//
+//==============================================================
+function loadenv()
+
+{
+$env_file_path="/etc/mailleur/mailleur.conf";
+$phase=0;
+$proceed=true;
+while ($proceed==true) {
+  rou_alert(2,"unienv.c:loadenv JMPDBG phase='$phase'");
+  switch ($phase) {
+    case 0     :               //checking if file exist
+      if(!is_file($env_file_path)){
+        throw new ErrorException("File <".$env_file_path."> is Missing!");
+       $phase=999;             //no need to go further
+        }
+      break;
+    case 1     :               //opening file 
+      $fopen=fopen($env_file_path,'r');
+      if (!$fopen) {
+       throw new ErrorException("Unable to read file <".($env_file_path).">!");
+       $phase=999;
+       }
+      break;
+    case 2     :               //scanning file
+      while (($line=fgets($fopen))!==false) {
+       if (substr(trim($line),0,1)=='#')
+         continue;
+       if (empty(trim($line)))
+         continue;
+       $line_no_comment=explode("#",$line,2)[0];
+       $env_ex=preg_split('/(\s?)\=(\s?)/',$line_no_comment);
+       $env_name=trim($env_ex[0]);
+       $env_value="";
+        if (isset($env_ex[1]))
+         $env_value=trim($env_ex[1]);
+       putenv("$env_name=$env_value");
+       }
+      break;
+    case 3     :               //closing file
+      fclose($fopen);
+      break;
+    default    :               //SAFE Guard
+      $proceed=false;
+      break;
+    }
+  $phase++;
+  }
+}
+
+loadenv();
+?>