From 4f7e2b2a4e65c3b64d0295cdeed18ce7c15ea800 Mon Sep 17 00:00:00 2001 From: gernot Date: Wed, 19 Feb 2003 08:19:51 +0000 Subject: [PATCH] Initial revision git-svn-id: https://svn.ibp.de/svn/capisuite/trunk/capisuite@3 4ebea2bb-67d4-0310-8558-a5799e421b66 --- .cvsignore | 11 + AUTHORS | 1 + ChangeLog | 0 Makefile.am | 25 + NEWS | 103 ++ README | 6 + TODO | 24 + acinclude.m4 | 98 ++ capisuite.cronin | 38 + capisuite.spec | 72 + configure.in | 26 + docs/.cvsignore | 9 + docs/Doxyfile.in | 969 +++++++++++++ docs/Makefile.am | 41 + docs/mainpage.doxy | 11 + docs/manual.README | 22 + docs/manual.docbook | 1371 +++++++++++++++++++ rc.capisuite.in | 180 +++ scripts/.cvsignore | 5 + scripts/Makefile.am | 37 + scripts/README | 2 + scripts/answering_machine.confin | 125 ++ scripts/capisuitefax | 168 +++ scripts/cs_helpers.pyin | 340 +++++ scripts/fax.confin | 142 ++ scripts/idle.py | 187 +++ scripts/incoming.py | 464 +++++++ scripts/remote-connect.py | 59 + scripts/waves/..la | Bin 0 -> 5199 bytes scripts/waves/.cvsignore | 2 + scripts/waves/0.la | Bin 0 -> 4800 bytes scripts/waves/1.la | Bin 0 -> 3599 bytes scripts/waves/10.la | Bin 0 -> 3599 bytes scripts/waves/11.la | Bin 0 -> 4000 bytes scripts/waves/12.la | Bin 0 -> 3599 bytes scripts/waves/13.la | Bin 0 -> 5599 bytes scripts/waves/14.la | Bin 0 -> 5599 bytes scripts/waves/15.la | Bin 0 -> 5199 bytes scripts/waves/16.la | Bin 0 -> 5199 bytes scripts/waves/17.la | Bin 0 -> 6400 bytes scripts/waves/18.la | Bin 0 -> 4800 bytes scripts/waves/19.la | Bin 0 -> 5599 bytes scripts/waves/2.la | Bin 0 -> 3599 bytes scripts/waves/20.la | Bin 0 -> 4800 bytes scripts/waves/3.la | Bin 0 -> 3599 bytes scripts/waves/30.la | Bin 0 -> 5199 bytes scripts/waves/4.la | Bin 0 -> 3599 bytes scripts/waves/40.la | Bin 0 -> 6400 bytes scripts/waves/5.la | Bin 0 -> 3200 bytes scripts/waves/50.la | Bin 0 -> 5599 bytes scripts/waves/6.la | Bin 0 -> 3200 bytes scripts/waves/60.la | Bin 0 -> 5599 bytes scripts/waves/7.la | 52 + scripts/waves/70.la | Bin 0 -> 5599 bytes scripts/waves/8.la | Bin 0 -> 3599 bytes scripts/waves/80.la | Bin 0 -> 5199 bytes scripts/waves/9.la | Bin 0 -> 3599 bytes scripts/waves/90.la | Bin 0 -> 6400 bytes scripts/waves/Makefile.am | 12 + scripts/waves/README | 2 + scripts/waves/am.la | Bin 0 -> 5599 bytes scripts/waves/anrufbeantworter-von.la | Bin 0 -> 18799 bytes scripts/waves/ansage-gespeichert.la | Bin 0 -> 12000 bytes scripts/waves/beep.la | 1 + scripts/waves/bitte-nachricht.la | Bin 0 -> 24000 bytes scripts/waves/bitte-neue-ansage-komplett.la | Bin 0 -> 84800 bytes scripts/waves/bitte-neue-ansage-kurz.la | Bin 0 -> 26400 bytes scripts/waves/ein.la | Bin 0 -> 2319 bytes scripts/waves/erklaerung.la | Bin 0 -> 75200 bytes scripts/waves/fernabfrage-aktiv.la | Bin 0 -> 40000 bytes scripts/waves/fuer-neue-ansage-9.la | Bin 0 -> 30400 bytes scripts/waves/fuer.la | Bin 0 -> 5599 bytes scripts/waves/keine-weiteren-nachrichten.la | Bin 0 -> 18400 bytes scripts/waves/nachricht-gelöscht.la | Bin 0 -> 10400 bytes scripts/waves/nachricht.la | Bin 0 -> 6800 bytes scripts/waves/nachrichten.la | Bin 0 -> 8000 bytes scripts/waves/neue-ansage-lautet.la | Bin 0 -> 16000 bytes scripts/waves/neue-nachricht.la | Bin 0 -> 12000 bytes scripts/waves/neue-nachrichten.la | Bin 0 -> 13599 bytes scripts/waves/uhr.la | Bin 0 -> 5199 bytes scripts/waves/um.la | Bin 0 -> 6400 bytes scripts/waves/und.la | 31 + scripts/waves/von.la | Bin 0 -> 5599 bytes scripts/waves/wenn-einverstanden-1.la | Bin 0 -> 31199 bytes scripts/waves/zum-abhoeren-1.la | Bin 0 -> 15199 bytes src/.cvsignore | 7 + src/Makefile.am | 20 + src/application/.cvsignore | 3 + src/application/Makefile.am | 5 + src/application/applicationexception.h | 121 ++ src/application/capisuite.cpp | 590 ++++++++ src/application/capisuite.h | 277 ++++ src/application/capisuitemodule.cpp | 1158 ++++++++++++++++ src/application/capisuitemodule.h | 124 ++ src/application/idlescript.cpp | 186 +++ src/application/idlescript.h | 160 +++ src/application/incomingscript.cpp | 318 +++++ src/application/incomingscript.h | 216 +++ src/application/pythonscript.cpp | 138 ++ src/application/pythonscript.h | 115 ++ src/backend/.cvsignore | 3 + src/backend/Makefile.am | 3 + src/backend/applicationinterface.h | 104 ++ src/backend/callinterface.h | 142 ++ src/backend/capi.cpp | 1031 ++++++++++++++ src/backend/capi.h | 500 +++++++ src/backend/capiexception.h | 209 +++ src/backend/connection.cpp | 1204 ++++++++++++++++ src/backend/connection.h | 774 +++++++++++ src/capisuite.conf.in | 70 + src/main.cpp | 126 ++ src/modules/.cvsignore | 3 + src/modules/Makefile.am | 7 + src/modules/audioreceive.cpp | 182 +++ src/modules/audioreceive.h | 158 +++ src/modules/audiosend.cpp | 136 ++ src/modules/audiosend.h | 164 +++ src/modules/callmodule.cpp | 160 +++ src/modules/callmodule.h | 180 +++ src/modules/calloutgoing.cpp | 86 ++ src/modules/calloutgoing.h | 112 ++ src/modules/connectmodule.cpp | 61 + src/modules/connectmodule.h | 94 ++ src/modules/disconnectmodule.cpp | 62 + src/modules/disconnectmodule.h | 86 ++ src/modules/faxreceive.cpp | 100 ++ src/modules/faxreceive.h | 121 ++ src/modules/faxsend.cpp | 56 + src/modules/faxsend.h | 78 ++ src/modules/readDTMF.cpp | 78 ++ src/modules/readDTMF.h | 83 ++ src/modules/switch2faxG3.cpp | 81 ++ src/modules/switch2faxG3.h | 86 ++ 133 files changed, 14414 insertions(+) create mode 100644 .cvsignore create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 TODO create mode 100644 acinclude.m4 create mode 100755 capisuite.cronin create mode 100644 capisuite.spec create mode 100644 configure.in create mode 100644 docs/.cvsignore create mode 100644 docs/Doxyfile.in create mode 100644 docs/Makefile.am create mode 100644 docs/mainpage.doxy create mode 100644 docs/manual.README create mode 100644 docs/manual.docbook create mode 100755 rc.capisuite.in create mode 100644 scripts/.cvsignore create mode 100644 scripts/Makefile.am create mode 100644 scripts/README create mode 100644 scripts/answering_machine.confin create mode 100755 scripts/capisuitefax create mode 100644 scripts/cs_helpers.pyin create mode 100644 scripts/fax.confin create mode 100644 scripts/idle.py create mode 100644 scripts/incoming.py create mode 100644 scripts/remote-connect.py create mode 100644 scripts/waves/..la create mode 100644 scripts/waves/.cvsignore create mode 100644 scripts/waves/0.la create mode 100644 scripts/waves/1.la create mode 100644 scripts/waves/10.la create mode 100644 scripts/waves/11.la create mode 100644 scripts/waves/12.la create mode 100644 scripts/waves/13.la create mode 100644 scripts/waves/14.la create mode 100644 scripts/waves/15.la create mode 100644 scripts/waves/16.la create mode 100644 scripts/waves/17.la create mode 100644 scripts/waves/18.la create mode 100644 scripts/waves/19.la create mode 100644 scripts/waves/2.la create mode 100644 scripts/waves/20.la create mode 100644 scripts/waves/3.la create mode 100644 scripts/waves/30.la create mode 100644 scripts/waves/4.la create mode 100644 scripts/waves/40.la create mode 100644 scripts/waves/5.la create mode 100644 scripts/waves/50.la create mode 100644 scripts/waves/6.la create mode 100644 scripts/waves/60.la create mode 100644 scripts/waves/7.la create mode 100644 scripts/waves/70.la create mode 100644 scripts/waves/8.la create mode 100644 scripts/waves/80.la create mode 100644 scripts/waves/9.la create mode 100644 scripts/waves/90.la create mode 100644 scripts/waves/Makefile.am create mode 100644 scripts/waves/README create mode 100644 scripts/waves/am.la create mode 100644 scripts/waves/anrufbeantworter-von.la create mode 100644 scripts/waves/ansage-gespeichert.la create mode 100644 scripts/waves/beep.la create mode 100644 scripts/waves/bitte-nachricht.la create mode 100644 scripts/waves/bitte-neue-ansage-komplett.la create mode 100644 scripts/waves/bitte-neue-ansage-kurz.la create mode 100644 scripts/waves/ein.la create mode 100644 scripts/waves/erklaerung.la create mode 100644 scripts/waves/fernabfrage-aktiv.la create mode 100644 scripts/waves/fuer-neue-ansage-9.la create mode 100644 scripts/waves/fuer.la create mode 100644 scripts/waves/keine-weiteren-nachrichten.la create mode 100644 scripts/waves/nachricht-gelöscht.la create mode 100644 scripts/waves/nachricht.la create mode 100644 scripts/waves/nachrichten.la create mode 100644 scripts/waves/neue-ansage-lautet.la create mode 100644 scripts/waves/neue-nachricht.la create mode 100644 scripts/waves/neue-nachrichten.la create mode 100644 scripts/waves/uhr.la create mode 100644 scripts/waves/um.la create mode 100644 scripts/waves/und.la create mode 100644 scripts/waves/von.la create mode 100644 scripts/waves/wenn-einverstanden-1.la create mode 100644 scripts/waves/zum-abhoeren-1.la create mode 100644 src/.cvsignore create mode 100644 src/Makefile.am create mode 100644 src/application/.cvsignore create mode 100644 src/application/Makefile.am create mode 100644 src/application/applicationexception.h create mode 100644 src/application/capisuite.cpp create mode 100644 src/application/capisuite.h create mode 100644 src/application/capisuitemodule.cpp create mode 100644 src/application/capisuitemodule.h create mode 100644 src/application/idlescript.cpp create mode 100644 src/application/idlescript.h create mode 100644 src/application/incomingscript.cpp create mode 100644 src/application/incomingscript.h create mode 100644 src/application/pythonscript.cpp create mode 100644 src/application/pythonscript.h create mode 100644 src/backend/.cvsignore create mode 100644 src/backend/Makefile.am create mode 100644 src/backend/applicationinterface.h create mode 100644 src/backend/callinterface.h create mode 100644 src/backend/capi.cpp create mode 100644 src/backend/capi.h create mode 100644 src/backend/capiexception.h create mode 100644 src/backend/connection.cpp create mode 100644 src/backend/connection.h create mode 100644 src/capisuite.conf.in create mode 100644 src/main.cpp create mode 100644 src/modules/.cvsignore create mode 100644 src/modules/Makefile.am create mode 100644 src/modules/audioreceive.cpp create mode 100644 src/modules/audioreceive.h create mode 100644 src/modules/audiosend.cpp create mode 100644 src/modules/audiosend.h create mode 100644 src/modules/callmodule.cpp create mode 100644 src/modules/callmodule.h create mode 100644 src/modules/calloutgoing.cpp create mode 100644 src/modules/calloutgoing.h create mode 100644 src/modules/connectmodule.cpp create mode 100644 src/modules/connectmodule.h create mode 100644 src/modules/disconnectmodule.cpp create mode 100644 src/modules/disconnectmodule.h create mode 100644 src/modules/faxreceive.cpp create mode 100644 src/modules/faxreceive.h create mode 100644 src/modules/faxsend.cpp create mode 100644 src/modules/faxsend.h create mode 100644 src/modules/readDTMF.cpp create mode 100644 src/modules/readDTMF.h create mode 100644 src/modules/switch2faxG3.cpp create mode 100644 src/modules/switch2faxG3.h diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..ab4d6ad --- /dev/null +++ b/.cvsignore @@ -0,0 +1,11 @@ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +stamp-h1 +config.* +configure +configure.scan +autoscan.log +capisuite.cron +rc.capisuite diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..223932c --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Gernot Hillier diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..fc217f2 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,25 @@ +spooldir = @localstatedir@/spool/capisuite +pkgsysconfdir = @sysconfdir@/capisuite +docdir = @docdir@ +doc_DATA = COPYING NEWS README +EXTRA_DIST = rc.capisuite.in capisuite.spec capisuite.cronin + +SUBDIRS = src scripts docs + +all: capisuite.cron rc.capisuite + +capisuite.cron: capisuite.cronin + rm -f $@ + sed -e 's,@pkgsysconfdir\@,$(pkgsysconfdir),g' \ + -e 's,@spooldir\@,$(spooldir),g' $< >$@ + chmod a+x $@ + +rc.capisuite: rc.capisuite.in + rm -f $@ + sed -e 's,@pkgsysconfdir\@,$(pkgsysconfdir),g' \ + -e 's,@bindir\@,$(bindir),g' $< >$@ + chmod a+x $@ + +clean-local: + rm -f rc.capisuite capisuite.cron + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..8ef2520 --- /dev/null +++ b/NEWS @@ -0,0 +1,103 @@ +0.01 (tag CAPISUITE_001): +========================= + + * changed name from CapiCom to CapiSuite (name conflict with MS crypto API) + * added doxygen-created documentation for classes and python exported functions + * get_DTMF() was renamed to read_DTMF() and can wait for DTMF now + * connect_telephony() renamed to connect_voice() + +0.02 (tag CAPISUITE_002): +========================= + + * many bug fixes as usual (SEGV, ...) + * service constants SERVICE_VOICE, SERVICE_FAXG3 and SERVICE_OTHER + available in python now, no need to use CIP values any more + * audio_send and audio_receive return length in seconds now + * added support for idle script which can initiate outgoing calls + +0.03 (tag CAPISUITE_003): +========================= + + * improvement in idle script handling, own class for it (IdleScript) + * new classes for Python script handling (PythonScript) and derived classes + (IncomingScript & IdleScript) + * new python functions call_voice and call_faxG3 to initiate outgoing calls + * changed python exception handling to allow multiple calls in one script to be + handled correctly + * python functions disconnect() and reject() wait for complete disconnection and + return the disconnect cause now + * assure nice disconnection in any error case (hopefully) + * when error occured in script, physical connection is finished immediately leading + to an error visible at the sending side (e.g. when using the fax protocol) + * cleaned up python reference counting and threads, no known memory leaks any more + * many changes to support outgoing calls (new module, many small changes) + * Connection objects will be destroyed by application level now so dangling pointers + are avoided + * exception handling generally improved + +0.1 (tag CAPISUITE_01): +======================= + * "make install" and "make dist" work now, use config.h + * added main docu page for doxygen + * added capisuitefax-script (command line tool for sending faxes) + * added support for sending faxes in idle.py + * added support for "capisuite.conf" (global configuration file) + * capisuite can write its output to logfiles now + * faxsend module added, new python function fax_send() + * idle script will be disabled after 10 subsequent errors + * B3 disconnect cause now returned by disconnect() python function + +0.2 (tag CAPISUITE_02): +======================= + * log improvements: log-level configurable (see capisuite.conf), appending log-file instead of re-creating + * configure allows to set docdir with --with-docdir + * CapiSuite can be finished using Ctrl-C and SIGTERM nicely + * very limited support for reload (kill -1) - only re-activates de-actived idle script yet, + no reload of configuration + * all configuration for the scripts put in own config file + * support for various new configuration options, multi-user-ready scripts (different user dirs in spool_dir/users) + * audio_receive does truncate recorded silence away + * remote inquiry supports recording of own announcement + * commandline option "-d" runs CapiSuite as daemon + * new python commands: capisuite.log and capisuite.error let scripts write messages to the CapiSuite log + and error log + +0.2.1 (tag CAPISUITE_021): +========================== + * many document improvements (new DocBook manual) + +0.3 (tag CAPISUITE_03): +======================= + * split up script configuration in two files (anwering machine, fax), + some new features configurable now (e.g. actions) + +0.3.1 (tag CAPISUITE_031): +========================== + * dist: included spec and init file in CVS and dist + * scripts: use different sendqueues for each user + * core: fixed some bugs: + - capisuite.error() didn't work, + - logging in outgoing connections didn't work + - callingParty wasn't set correctly + * scripts: answering machine switches to fax when incoming service indicator says fax + * scripts: sayNumber can now handle all number from 0 to 99, so all dates and times are + now said nearly correctly for the remote inquiry + * scripts: fixed a typo in incoming.py + * docs: added ISDN/CAPI error codes to manual + +0.3.2 (tag CAPISUITE_032): +========================== + * core: finally got rid of the CommonC++ library: + - threading implemented using native pthread_* calls + - rewritten CapiSuite::parseConfigFile() to use STL string routines + - changed Connection class to use pthread_mutex_* + * scripts: fixed bug which lead to hanging processes of externally started + progs like sendmail + * scripts: minor fixes + +0.4 (tag CAPISUITE_04): +======================= + * added cron script for cleaning up spool dirs + * fixed bug in rc.capisuite (was also started when not configured) + * scripts: remote inquiry supports new and old messages now + * scripts: capisuitefax can show sendqueue and delete jobs now diff --git a/README b/README new file mode 100644 index 0000000..a4ad7a0 --- /dev/null +++ b/README @@ -0,0 +1,6 @@ +CapiSuite +========= + +For the documentation see the created HTML documents +situated in docs/manual/index.html or in the installed version see +PREFIX/share/doc/capisuite/manual/index.html diff --git a/TODO b/TODO new file mode 100644 index 0000000..8c1a14d --- /dev/null +++ b/TODO @@ -0,0 +1,24 @@ +CRITICAL: +- finish manual.docbook, provide simple examples +- somewhere there's a bad thing (tm) which holds the python global lock + despite it mustn't (try incoming + outgoing call at the same time, + idlescript will stop working sometime...) + +IMPORTANT: +- fax headline + +NICE: +- ?valgrind-clean the used libs and Python? +- support more than one send_controller +- don't use 34xx codes, define constants instead and print meaningful messages +- include email account check in idly.py +- add docbook -> html to Makefile.am +- any solution for sending fax when having no fax config? + +FUTURE PLANS: +- setuid away from root (problem: chown of recorded file to user) +- log syntax errors in scripts, too + - PyRun_SimpleFile must be replaced by PyRun_File for this IMHO +- test-implement the whole application part in Python +- rewrite capisuitefax and idle.py to use named socket communication + diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..576cffc --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,98 @@ +# +# Autoconf macros for configuring the build of Python extension modules +# +# $Header: /root/cvs2svn/capisuite/capisuite/acinclude.m4,v 1.1 2003/02/19 08:19:52 gernot Exp $ +# +# taken out of Postgres CVS by Gernot Hillier +# + +# CS_SET_DOCDIR +# ------------- +# Set the name of the docdir to the given value. This is not nice, but I +# found no other name to do it than with AC_ARG_WITH. Please tell me if +# you have better ideas... +AC_DEFUN(CS_SET_DOCDIR, +[AC_ARG_WITH(docdir, + AC_HELP_STRING([--with-docdir=DOCDIR], + [use DOCDIR to install documentation to (default is PREFIX/share/doc/capisuite)]), + docdir=$withval, docdir=$datadir/doc/capisuite) +AC_SUBST(docdir) +]) + +# PGAC_CHECK_PYTHON_DIRS +# ----------------------- +# Determine the name of various directory of a given Python installation. +AC_DEFUN([PGAC_CHECK_PYTHON_DIRS], +[AC_REQUIRE([AM_PATH_PYTHON]) +AC_MSG_CHECKING([Python installation directories]) +python_version=`${PYTHON} -c "import sys; print sys.version[[:3]]"` +python_prefix=`${PYTHON} -c "import sys; print sys.prefix"` +python_execprefix=`${PYTHON} -c "import sys; print sys.exec_prefix"` +python_libdir=`${PYTHON} -c "from distutils import sysconfig; print sysconfig.get_python_lib(1,1)"` +python_configdir="${python_libdir}/config" +python_moduledir="${python_libdir}/site-packages" +python_moduleexecdir="${python_libdir}/site-packages" +python_includespec="-I${python_prefix}/include/python${python_version}" +python_linkforshared=`${PYTHON} -c "import distutils.sysconfig; print distutils.sysconfig.get_config_var('LINKFORSHARED')"` +if test "$python_prefix" != "$python_execprefix"; then + python_includespec="-I${python_execprefix}/include/python${python_version} $python_includespec" +fi + +AC_SUBST(python_version)[]dnl +AC_SUBST(python_prefix)[]dnl +AC_SUBST(python_execprefix)[]dnl +AC_SUBST(python_configdir)[]dnl +AC_SUBST(python_moduledir)[]dnl +AC_SUBST(python_moduleexecdir)[]dnl +AC_SUBST(python_includespec)[]dnl +AC_SUBST(python_linkforshared)[]dnl +# This should be enough of a message. +if test "$python_prefix" != "$python_execprefix"; then + AC_MSG_RESULT([$python_libdir and $python_execprefix]) +else + AC_MSG_RESULT([$python_libdir]) +fi +])# _PGAC_CHECK_PYTHON_DIRS + + +# PGAC_CHECK_PYTHON_MODULE_SETUP +# ------------------------------ +# Finds things required to build a Python extension module. +# This used to do more, that's why it's separate. +# +# It would be nice if we could check whether the current setup allows +# the build of the shared module. Future project. +AC_DEFUN([PGAC_CHECK_PYTHON_MODULE_SETUP], +[ + AC_REQUIRE([PGAC_CHECK_PYTHON_DIRS]) +])# PGAC_CHECK_PYTHON_MODULE_SETUP + + +# PGAC_CHECK_PYTHON_EMBED_SETUP +# ----------------------------- +# Courtesy of the INN 2.3.1 package... +AC_DEFUN([PGAC_CHECK_PYTHON_EMBED_SETUP], +[AC_REQUIRE([PGAC_CHECK_PYTHON_DIRS]) +AC_MSG_CHECKING([how to link an embedded Python application]) + +if test ! -f "$python_configdir/Makefile"; then + AC_MSG_RESULT(no) + AC_MSG_ERROR([Python Makefile not found]) +fi + +_python_libs=`grep '^LIBS=' $python_configdir/Makefile | sed 's/^.*=//'` +_python_libc=`grep '^LIBC=' $python_configdir/Makefile | sed 's/^.*=//'` +_python_libm=`grep '^LIBM=' $python_configdir/Makefile | sed 's/^.*=//'` +_python_liblocalmod=`grep '^LOCALMODLIBS=' $python_configdir/Makefile | sed 's/^.*=//'` +_python_libbasemod=`grep '^BASEMODLIBS=' $python_configdir/Makefile | sed 's/^.*=//'` + +pgac_tab=" " # tab character +python_libspec=`echo X"$_python_libs $_python_libc $_python_libm -lpython$python_version $_python_liblocalmod $_python_libbasemod" | sed -e 's/^X//' -e "s/[[ $pgac_tab]][[ $pgac_tab]]*/ /g"` +LIBS="$LIBS $python_libspec" +LDFLAGS="$LDFLAGS -L$python_configdir $python_linkforshared" +AC_MSG_RESULT([${python_libspec}]) + +AC_SUBST(LIBS)[]dnl +AC_SUBST(LDFLAGS) +])# PGAC_CHECK_PYTHON_EMBED_SETUP + diff --git a/capisuite.cronin b/capisuite.cronin new file mode 100755 index 0000000..e9f3013 --- /dev/null +++ b/capisuite.cronin @@ -0,0 +1,38 @@ +#!/bin/sh +# +# capisuite. CapiSuite cleanup script, should be run regularly +# by cron. It's only useful for the default scripts provided +# with CapiSuite. +# +# It will read a central configuration file placed in +# /etc/capisuite/cronjob.conf where you must define a variable +# called MAX_DAYS which defines how many days a received or sent +# file may stay in the spool dirs. +# +# Author: Gernot Hillier +# + +# +# paranoia settings +# +umask 022 + +PATH=/sbin:/bin:/usr/sbin:/usr/bin +export PATH + +# do nothing if there is no global config +test -r @pkgsysconfdir@/cronjob.conf || exit + +for i in @spooldir@/users/*/received @spooldir@/done @spooldir@/failed; do + # reset defaults + test -r @pkgsysconfdir@/cronjob.conf && . @pkgsysconfdir@/cronjob.conf + # user can overwrite default values + test -r $i/cronjob.conf && . $i/cronjob.conf + + test "$MAX_DAYS" -gt 0 2> /dev/null || continue + find $i/. -name "*fax-[0-9]*.*" ! -type d ! -type s -atime +$MAX_DAYS -exec rm {} \; + find $i/. -name "*voice-[0-9]*.*" ! -type d ! -type s -atime +$MAX_DAYS -exec rm {} \; +done; + +exit 0 + diff --git a/capisuite.spec b/capisuite.spec new file mode 100644 index 0000000..870eef3 --- /dev/null +++ b/capisuite.spec @@ -0,0 +1,72 @@ +# +# spec file for package capisuite +# +# Author: Gernot Hillier +# +# This spec file was developed for the use with SuSE Linux. But it +# should also work for any other distribution with slight changes. +# If you created your own RPM, please tell me and I'll happily include +# the spec or a link to your RPM on the homepage. + +# neededforbuild capi4linux gcc-c++ libstdc++-devel libxml2-devel python python-devel + +Name: capisuite +License: GPL +Group: Applications/Communications +Autoreqprov: on +Version: 0.3 +Release: 0 +Requires: sfftobmp sox +Summary: capisuite +Source0: capisuite-%{version}.tar.gz +Source1: rc.capisuite +Url: http://www.capisuite.de +BuildRoot: %{_tmppath}/%{name}-%{version}-build +PreReq: %insserv_prereq + +%description +CapiSuite is a ISDN telecommunication suite providing easy to use +telecommunication functions which can be controlled from Python scripts. + +It uses a CAPI-compatible driver for accessing the ISDN-hardware, so you'll +need a Eicon or AVM card with the according driver. + +CapiSuite is distributed with two example scripts for call incoming handling +and fax sending. See /usr/share/capisuite/scripts and +/usr/share/doc/packages/capisuite for further information. + +Authors: +-------- + Gernot Hillier + +%prep +%setup +./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --with-docdir=/usr/share/doc/packages/capisuite + +%build +make + +%install +make DESTDIR=$RPM_BUILD_ROOT install +mkdir -p $RPM_BUILD_ROOT/etc/init.d +mkdir -p $RPM_BUILD_ROOT/usr/sbin +install -g root -m 755 -o root %{SOURCE1} $RPM_BUILD_ROOT/etc/init.d/capisuite +ln -sf ../../etc/init.d/capisuite $RPM_BUILD_ROOT/usr/sbin/rccapisuite + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%config /etc/capisuite/capisuite.conf +%config /etc/capisuite/fax.conf +%config /etc/capisuite/answering_machine.conf +/usr/bin/capisuite +/usr/bin/capisuitefax +%doc /usr/share/doc/packages/capisuite +/usr/share/capisuite +/usr/lib/capisuite +/var/spool/capisuite +/usr/%{_lib}/python2.2/site-packages/cs_helpers.py +/etc/init.d/capisuite +/usr/sbin/rccapisuite + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..cc674d6 --- /dev/null +++ b/configure.in @@ -0,0 +1,26 @@ +AC_INIT(src/main.cpp) +AM_INIT_AUTOMAKE(capisuite,0.4) +AM_CONFIG_HEADER(config.h) + +AC_LANG_CPLUSPLUS +AC_PROG_CC +AC_PROG_CXX +AC_PROG_INSTALL +AC_PROG_RANLIB +AC_PROG_MAKE_SET +AC_PATH_PROG(doxygen,doxygen) +dnl suggested by autoscan: + +AC_CHECK_FUNCS([gettimeofday]) +AC_CHECK_HEADERS([sys/time.h]) +AC_HEADER_TIME + +CS_SET_DOCDIR + +AC_CHECK_LIB(capi20,capi20_register,,AC_MSG_ERROR(libcapi20 not found)) +AC_CHECK_LIB(pthread,pthread_create,,AC_MSG_ERROR(libpthread not found)) +AM_PATH_PYTHON(2.2) +PGAC_CHECK_PYTHON_EMBED_SETUP +CPPFLAGS='-DLOCALSTATEDIR=\"$(localstatedir)\" -DPKGDATADIR=\"$(pkgdatadir)\" -DPKGSYSCONFDIR=\"$(sysconfdir)/capisuite\" -DPKGLIBDIR=\"$(pkglibdir)\" $(python_includespec)' + +AC_OUTPUT(Makefile src/Makefile src/backend/Makefile src/modules/Makefile src/application/Makefile scripts/Makefile scripts/waves/Makefile docs/Makefile) diff --git a/docs/.cvsignore b/docs/.cvsignore new file mode 100644 index 0000000..9e3ec88 --- /dev/null +++ b/docs/.cvsignore @@ -0,0 +1,9 @@ +reference +reference/* +manual +manual/* +susebuch +susebuch/* +Doxyfile +Makefile +Makefile.in diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in new file mode 100644 index 0000000..c64b157 --- /dev/null +++ b/docs/Doxyfile.in @@ -0,0 +1,969 @@ +# Doxyfile 1.2.17 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = CapiSuite + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @version@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en +# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, +# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = YES + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consist of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @capisuite_sources@ @srcdir@ + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl + +FILE_PATTERNS = *.cpp *.h *.doxy + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse. + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = reference + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output dir. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non empty doxygen will try to run +# the html help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the Html help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, +# or Internet explorer 4.0+). Note that for large projects the tree generation +# can take a very long time. In such cases it is better to disable this feature. +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse the +# parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yield more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermedate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/docs/Makefile.am b/docs/Makefile.am new file mode 100644 index 0000000..822f7fd --- /dev/null +++ b/docs/Makefile.am @@ -0,0 +1,41 @@ +docdir = @docdir@ +EXTRA_DIST = Doxyfile.in mainpage.doxy manual.docbook manual.README + +all: docs + +docs: Doxyfile + if test "x$(doxygen)" != "x"; then \ + $(doxygen) Doxyfile ;\ + fi + +dist-hook: Doxyfile + $(doxygen) Doxyfile + mkdir $(distdir)/reference + cp $(srcdir)/reference/* $(distdir)/reference/ + mkdir $(distdir)/manual + cp $(srcdir)/manual/* $(distdir)/manual/ + +Doxyfile: Doxyfile.in + sed -e 's,@version\@,$(VERSION),g' \ + -e 's,@capisuite_sources\@,$(top_srcdir)/src,g' \ + -e 's,@srcdir\@,$(srcdir),g' $< >$@ + +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(docdir)/reference ; \ + (cd reference; for i in *; do \ + $(INSTALL_DATA) $$i $(DESTDIR)$(docdir)/reference/$$i ;\ + done;) + $(mkinstalldirs) $(DESTDIR)$(docdir)/manual ; \ + (cd manual; for i in *; do \ + $(INSTALL_DATA) $$i $(DESTDIR)$(docdir)/manual/$$i ;\ + done;) + +uninstall-local: + rm -rf $(DESTDIR)$(docdir) + +clean-local: + rm -f Doxyfile + +maintainer-clean-local: + rm -rf reference manual; + diff --git a/docs/mainpage.doxy b/docs/mainpage.doxy new file mode 100644 index 0000000..7aa47c1 --- /dev/null +++ b/docs/mainpage.doxy @@ -0,0 +1,11 @@ +/** @mainpage %CapiSuite Documentation + +This is the reference manual describing the internal structures of %CapiSuite. If you're interested in developing, +you're right here. +@htmlonly +If you "just" want to use it, please refer to ../manual/index.html. +@endhtmlonly +Thx! + +@author Gernot Hillier +*/ diff --git a/docs/manual.README b/docs/manual.README new file mode 100644 index 0000000..4ad16a8 --- /dev/null +++ b/docs/manual.README @@ -0,0 +1,22 @@ +The CapiSuite manual is written in the DocBook format. Only +read on if you want to change it. You'll need some knowledge +of DocBook and the appropriate tools for that. + +Otherwise just use the prepared HTML documentation which +will be installed in your doc_dir when you do "make install". +Have a look into /usr/local/share/doc/capisuite/manual/ +after the installation and please READ it. :-) + +------ Only experts read on here, please ------ + +You can create HTML pages by using tools like xsltproc and +the DocBook stylesheets of Norman Walsh. + +An example of how to call xsltproc: + +xsltproc -o manual/ /usr/share/sgml/docbook/docbook-xsl-stylesheets/xhtml/chunk.xsl manual.docbook + +To validate the document, use e.g. xmllint: + +xmllint --noout --valid manual.docbook + diff --git a/docs/manual.docbook b/docs/manual.docbook new file mode 100644 index 0000000..d8a7044 --- /dev/null +++ b/docs/manual.docbook @@ -0,0 +1,1371 @@ + +CapiSuite"> +]> + +CapiSuite 0.4 + + + + + + GernotHillier +
gernot@hillier.de
+
+
+ + +Introduction + + Welcome to &cs; + Welcome to &cs;, a python-scriptable ISDN telecommunication suite. It uses the new CAPI interface for accessing + your ISDN-hardware - so you'll need a card for which a CAPI compatible + driver is available. Currently these are all cards manufactured by AVM + and some Eicon cards. + + + + This manual should help you to be able to use &cs; as quick as possible. + As I hate reading long documentation just as much as you do, let's jump + right in. + + + What the heck is "&cs;"?! + &cs; tries to give the user the ability to code his own ISDN applications + without having to fiddle around with all the dirty programming details like callback + functions, data buffers, protocol settings and so on. + + I took a scripting language which is (in my opinion) very easy to understand, + to use and to learn - especially for beginners: Python. I extended it with some + functions providing the basic ISDN "building blocks" for the users application. + Behind these functions the heart of &cs; implements all the dirty details + a user isn't interested in. My goal was to make script-coding as + simple as possible but to also give you the flexibility to realize what you + want. + + To give you an impression, coding a simple answering machine is as easy as: + + + def callIncoming (call, service, call_from, call_to): # defines a function executed at incoming calls + connect_voice (call, 10) # answer the call after 10 seconds + audio_send (call, "my-announcement.la") # wave file containing the announcement + audio_send (call, "beep.la") # wave file containing BEEP + audio_receive (call, "call.la", 10) # record max. 10 seconds into call.la + + + + Of course some details are missing like creating a unique filename or storing + the additional information (called and calling party numbers, time, ...) - but I assume + you got my idea. + + And - don't be afraid - if you just want to have a normal answering machine or send and + receive some fax documents, you can use the default scripts distributed with &cs;. + They give you already some nice features - e.g. the answering machine is multi-user + ready, supports automatic fax detection and remote inquiry functions. You'll only + need to tell &cs; some details like your own number, record an own announcement + and that's it. + + So &cs; is already equipped for your daily telecommunication needs - but if you don't + like to do the things the way I do - just change it or completely do it on your + own (and if you write nice scripts or have changes to my default scripts, I would + love to get and perhaps make them available for all users if you don't mind). + + + Structure of the manual + This manual is split into three big parts. + + The first part () explains how you install &cs;, what + you can do with the default scripts you have after installing it and how + to configure them. No line of code will be presented here. If you just want + to use the default scripts that should be all the reading you need. + + The second part () will tell you how to write your own scripts. It will + give you a very, very small introduction into Python and a complete reference + of the commands &cs; adds to it. Last, an overview over the default + scripts is given which will tell you how they work so you can easily take them + as starting points and/or examples for your own application. + + The last part () is intended for programmers who want to + help in developing the &cs; core. It provides a rough overview of the internal + structure of the program. All the rest is documented in doxygen pages, which give you + a thorough description for each single class, method and attribute... + + There are also some additional parts containing "what I also wanted to mention": + + Look at @ref knownbugs to find a list of known problems and what you should do + if you think you found a bug. + + As &cs; started as a diploma thesis, I want to thank all who helped me so far in this section + @ref blubber. + + When you want to code your own scripts or want to help in developing the &cs; core, + you'll soon stumble upon some special ISDN and CAPI error codes, which are explained in + . + + + Hope I managed to whet your appetite - so let's now really start over to get you ready to + use it. + + + +Getting Started + Requirements and installation of &cs; + + Requirements + Hardware and drivers + As &cs; uses the CAPI (Common ISDN Application Programming Interface) + for accessing your ISDN-hardware, you'll need a card for which a CAPI compatible + driver is available. + + Currently these are all cards manufactured by AVM and some Eicon cards. + If you have one of the passive cards of AVM, you'll + have to download and install their CAPI drivers - you can't use the + drivers of the ISDN4Linux project included in the default Linux kernel yet. + There are also some distributions (e.g. current versions of SuSE) which + include the Capi4Linux drivers from AVM already - you'll only have to + activate them (use YaST2 in SuSE Linux). If you own an active card of AVM + (e.g. the B1, C2 or C4), then you'll have everything you need already installed. + + No, there's currently no way to get it working with the old ISDN4Linux interface. + Perhaps there never will be one as the ISDN4Linux project will provide a CAPI + compatible interface some near day in the future - so all supported ISDN cards + will work with &cs; then. If you nevertheless want to write a backend for + ISDN4Linux, just contact me - I'll be more than happy to help you with that. + + &cs; has mainly been tested on AVM ISDN cards, esp. the Fritz!PCI, the Fritz!USB and + the B1 on the i386 platform but there should be no problem with other + CAPI-compatible drivers for other cards or on other platforms. Nevertheless, + some features aren't mandatory for all CAPI-compatible cards, so perhaps + you may not be able to fax or to switch from voice to fax mode with all + cards. + + Software + &cs; depends on some packages which must be installed before &cs; can be used. + + I will list them here with a short information why this packages are needed and where to + find further information on how to install them. It may be always a good idea to check the + installation tool of your favourite distribution first and see if they're included with it before + trying to download and install them from the net. Don't be afraid, because there are so many - + most of them will be included in nearly every distribution and perhaps are already installed on your system. + + + + Python + &cs; uses an embedded Python interpreter to interpret the given scripts - + so you'll need an installed and working version of Python. This should be included + in mostly every up-to-date Linux distribution. For further infos on Python, a nice + tutorial and much more, please go to + + + + sox + This is the swiss-knife for converting audio formats. It's not required + by the &cs; core, but will be very helpful if you want to hear or record the + voice files used for calls on your machine. It's also required if you want to + use the default scripts of &cs;. I'll bet this is included in your distribution + and most likely already installed on your system. Just try to start sox + to get sure. You'll find more details on + + + + sfftobmp + &cs; will save fax files in the CAPI specific format Structured Fax File (SFF). + sfftobmp is a small but useful converter to convert this files to more + common formats like JPEG, TIFF or BMP. Get it on . + It's again not needed by the &cs; core, but for the default scripts. + + + + sffview + This tool is a simple but useful SFF viewer. It's not needed by any + &cs; component, but very useful if you just want to see a fax file without + the need to convert it first. You can get it from . + + + + tiff2ps + A small utility to convert TIFF files to the Postscript format. It's needed by + the default script to convert faxes to PDF files (SFF->TIFF->PS->PDF :-} ). + Surely already on your system. Details on + + + + ps2pdf + Again a small utility for the SFF->PDF chain - this time for the + conversion of Adobe PostScript to Adobe PDF. It's part of Ghostscript, so + you have it definitely already. () + + + + current Ghostscript with cfax patch + Current Ghostscript versions will include a device to create the above mentioned + SFF files. If you have an older version, you'll need the patch from + . To see if your GhostScript + version already has this patch, please call gs --help and see if you can + find the device cfax in the long list of supported devices. + + + + + + Installation + First of all, I would suggest to check if your CAPI-driver is setup correctly. + To do this, simply run capiinfo on a root shell. + + If you get many lines of output, your CAPI driver works. If you just get + an error message, you'll have to install CAPI-compatible drivers. Refer + to the documentation of your ISDN card vendor, your Linux distribution + and/or some ISDN mailing lists for this, please. If you really can't find + anyone to support you in doing this, you may ask on the &cs; mailing + lists for support as last resort. + + The rest of the installation depends on wether you use binary or source + packages for installing &cs;. If you don't want to change the + &cs; sources, I would recommend you to use the binary packages + when available for your distribution and platform. + + You can download both binary packages and sources from the download section on + . If you built up your own RPM packages + for other distributions, please send me them and I'll copy them there... + + Installation from binary packages + + If you can get binary packages for your distribution and platform, + I would advise to use them. Currently, there are only RPM packages + for SuSE Linux available as this is the distribution I use (and BTW + the company which paid and supported me to write &cs; as diploma + thesis ;-) ). + + To install the &cs; RPM packages you can either use your favorite setup tool - + either given by your distributor or the community - or you can do manually + (as root): + + rpm -Uvh capisuite-version.rpm + + To install binary packages not in the RPM format, please refer to the + documentation of you package manager. + + If you managed to install &cs; on a system not mentioned above, + please tell me and I'll include the instructions here certainly. + + Now everything should be setup ready to run. So please read on in + . + + + Installation from the source packages + If there are no binary packages you can use or if you like to do + everything on your own, you can get the sources from the download section. + + Download the newest source tarball (capisuite-X.Y.tar.gz) from the + &cs; homepage and copy it to some location. Go there and issue the following commands: + + ./configure +make +su # get root now +make install + + This will install &cs; completely in the /usr/local-tree. If you + want it to stay in other directories, please see the commandline-help + printed by + + ./configure --help + + for options to customize the installation directories. + + Installation from CVS + If you want to live on the bleeding edge and always test the newest features, + you may also checkout the current sources of &cs; from the CVS + repository. + + This is not recommended unless you want to test the newest features or + want to help in developing &cs;! The sources in CVS may do anything, + may not work or not even compile. Do this on your own risk! + + You'll need installed and working versions of the usual development tools like + autoconf, autoheader, automake, GNU make, gcc/g++ and also the components described + above (esp. development packages of Python). + + If you want to build the documentation out of the sources, you'll also need + Doxygen. + + For instructions on where to find the CVS repository and how to checkout + the sources, please refer to the download section on the &cs; homepage + on . + + After you checked out the sources to some directory, please do + + automake -a +autoreconf + + + After that you can continue with the normal installation process as described in + . + + + + + + How &cs; works, how it is configured and started + First, let's start with a short introduction what &cs; actually + is and how it works. After that, the configuration and startup of &cs; + will be explained in short. + + How does &cs; work? + &cs; is a daemon (program which runs in the background) whos + main task is to sit around and wait until a call is incoming. + If this happens it will start a special Python script - the + incoming script - and do what this script tells it, for example + record a voice call to implement an answering machine. + + To also be able to issue outgoing calls, another script is called + at regular intervals - the idle script. It can check any resource + to get instructions for placing a call - one can for example imagine + to check a special mail account or watch a special directory where + tasks are placed by the user. + + So all user-visible actions and the behaviour of &cs; are defined + in these two scripts. + + You'll need to do two things now: + + + provide scripts by either + + using and configuring the default scripts distributed with &cs; or + writing your own scripts (perhaps by using the default ones as templates) + + + configure &cs; itself and tell it where to find the two scripts + + + This page concentrates on the general configuration of &cs; - that consists mainly of + options telling it which scripts to use and where and how to log its activities. After + that, some details about starting &cs; are described. + + The next pages will then introduce the standard scripts you already installed along with + &cs; and tell you how to use the answering maching and fax functions provided + by them. + + The details on how to write your own scripts are covered in another part of the + documentation (). + + Configuration of &cs; + &cs; uses a general configuration file for the core functions. This file should + be located in /etc/capisuite/capisuite.conf or /usr/local/etc/capisuite/capisuite.conf + depending on how you installed &cs;. + + Most options are set to reasonable defaults already for using the standard scripts + - so if you want you can also skip this section and read on in + + The options will be presented in brief here - for further details please + refer to the comments in the configuration file itself. + + Options available in capisuite.conf + + + This option tells &cs; which script should be executed at incoming + calls. Only change this if you want to use your own script. + + + + This option reflects the path and name of the idle script. + This script is called in regular intervalls to check if any outgoing + call should be done. As above, the default should be ok if you don't + use your own script. + + + + Here you can define how often the idle script should be executed. The + number given is the interval between subsequent invocations in seconds. + Lesser numbers give you quicker response to queued jobs but also a higher + system load. The default should be ok in most cases. + + + + This file will be used for all "normal" messages printed by + &cs; telling you what it does. Error messages are written to a + special log (see below). + + + + You can define how detailled the log output of &cs; will be. + The default will give you some informational messages for each + incoming and outgoing call and should be enough for normal use. I would + recommend to only increase it if you encounter some problems. + Logs of higher level are mainly intended for developers, so just use + them if you want to report a problem or have some know-how of the CAPI + interface and the internals of &cs;. + + + + All errors which &cs; detects internally and in your scripts + will end up here. These were put to an extra file so that they don't + get lost in the normal log. Please check this log regularly for any + messages - especially when you encounter problems. Please report all + messages you don't understand and which aren't caused by your + own script-modifications to the &cs; team. + + + + Startup of CapiSuite + As &cs; is a daemon, it is normally activated during the system + startup process. Just add a call to + + /path/to/capisuite -d + + in your startup scripts. In LSB conforming Linux distributions, you'll + find the startup scripts in /etc/init.d. For detailled documentation + how to add a service there please refer to the documentation of your + distribution. There's an example startup script written for SuSE Linux included + in the source distribution (see rc.capisuite) which should (hopefully) + work with other LSB compliant distributions, too. If you need to modify it, I'll + welcome your feedback and happily add instructions for other distributions here. + + If you use the right RPM packages of &cs;, the necessary scripts + should already be included. For activating them, please use your + distributors config tool. If you use the RPM distributed with SuSE Linux + and want to stay with the default scripts, everything should work "out of the box". + As soon as you have configured the default scripts, simply run + rccapisuite restart. + + For debug puposes, you can also start capisuite manually at any time + by just calling + + /path/to/capisuite + + There are also some other commandline options available: + + + commandline options of &cs; + + + show a short summary of commandline options + + + + use a custom configuration file instead + of /etc/capisuite/capisuite.conf or + /usr/local/etc/capisuite/capisuite.conf. + + + + run as daemon (used in your startup script, see above) + + + + + Features and configuration of the default scripts + As already written above, &cs; comes with default scripts + giving you the most used communication functions of an answering machine + and a fax device. + + This section should help you to use them for your daily needs. + + Script features + The scripts distributed with &cs; give you the following main + functions: + + + + multi-user answering machine + + different users using different numbers and different announcements are supported + incoming calls are saved and sent to the user by email + the delay until a call is accepted and the maximum record length are freely adjustable + silence is detected and the call terminated after an adjustable silence time + incoming fax calls are automatically detected by the CNG/CED signals and received + comfortable, menu-controlled remote inquiry functions are supported telling you + the date/time when the call was received and the called and calling numbers (currently only german). + record your own announcement via the remote inquiry menu + nearly each setting is configurable globally but can be overwritten for each user also + + + + fax machine + + different users using different numbers are supported for incoming faxes + incoming faxes are stored and sent to the user by email + command line tool for sending PostScript documents as fax included + number of tries and delays for sending faxes freely configurable + currently supports only one ISDN controller for outgoing faxes + + + + + + How the scripts work + Here follows a rough overview of how the scripts work generally. I will only explain + the behaviour which is important for the user here. If you want to understand the internals, + please refer to @ref userguidescriptchapter + + When an incoming call is received, several lists for the different users are + searched for the called number. The different users can define their own numbers in + the configuration (see below). So the scripts decide by looking on the called number + to which user the call destinates. If they find the number in the voice- or fax-number + list of any user, they'll answer the call with this service and give the caller the possibility to + leave his message or send his fax. + + The received document is then saved to a local directory in some native format + and also converted to a well-known format and mailed to the user along with some + details of the call. Voice calls are sent as a WAV attachment, while fax calls + are sent as PDF documents attached to the mail. + + So you'll normally get your incoming calls as a mail to a specified address - + but they're also saved in the local filesystem to be on the safe side. + It's your task to delete old files you don't need any more - perhaps via + some script called by a cronjob. + + There's also the possibility to do a remote inquiry on the answering machine + via a normal phone. The caller is presented a menu where he can choose to record + his announcement or to hear the saved voice calls. He will be told how many calls + are available, from whom and when they were received and so on. He'll also be + able to delete recorded calls he doesn't need any more. + + Another script will check a special queue directory for fax send jobs + regularly. To put jobs in this directory, a special commandline tool is also + provided. See for further details on this. + + + Script configuration + There are some important options which the scripts need to know before you can use them - + things like numbers and some details of how to handle the calls. + + These options are read from two configuration files. Each configuration file is divided into + one or more sections. A section begins with the section name in square brackets like [section] + while the options are key="value" lines. + + Each file must have a special section called [GLOBAL] and one section + for each user called [<username>] (with <username> being a + valid system user). + + The [GLOBAL]-section defines some global options like + pathnames and default settings for options users can change on their own. The user-sections + hold all the options which belong to the particular user. + + All options for the two files are described in short below. For all details, please see the comments + in the sample configuration files installed with &cs;. + + Configuration for fax service + This file holds all available config options for the fax services (fax receive and send). + + It's read from /etc/capisuite/fax.conf or + /usr/local/etc/capisuite/fax.conf (depending on the installation). + + available options for [GLOBAL] section in fax config + + + This directory is used to archive sent (or failed) jobs. It must exist and + the user &cs; runs as must have write permission to its subdirectories. + Two subdirectories are used: + + + spooldir/done/ + Successfully finished jobs are moved to this directory. + + + spooldir/failed/ + A job which has failed finally ends up here. + + + + + + + This directory is used to save faxes to. It must exist and + the user &cs; runs as must have write permission to it. It will contain + one subdirectory for each configured user (named like his userid). The + following subdirectories are used below the user-specific dir: + + + user_dir/username/received/ + Received faxes are saved here. + + + user_dir/username/sendq/ + Fax files to be sent are queued here by capisuitefax. + + + + + + + When a fax can't be sent to the destination for any reason, it's tried for several times. + This setting limits the number of tries. If all tries failed, the job will be considered + failed, moved to the failed dir (see ) and the user will get a mail. + + + + + When a fax can't be sent to the destination for any reason, it's tried again. + This setting specifies the delays in seconds between subsequent tries. The different values are + separated with commas and no blanks. The list should have send_tries-1 + (see ) values - if not, surplus entries are ignored and missing + entries are filled up with the last value. The default should just be ok giving you increasing + delays for up to 10 tries. + + + + + If you have more than one ISDN controller installed (some active cards for more than + one basic rate interface like the AVM C2 or C4 are also represented as multiple controllers for + CAPI applications like &cs;), you can decide which controller (and therefore which basic rate + interface) should be used for sending your faxes. All controllers are numbered starting with 1. + If you're not sure which controller has which number, increase the log level to at least 2 + in &cs; (see ), restart it and have a look in the log file where all + controllers will be listed then. Unfortunately, &cs; isn't able to use more than one controller + for sending faxes at the moment, so no list is allowed here. If you have only one controller, + just leave it at 1 + + + + + This number is used as our own number for outgoing calls. If it's not given, + the first number of fax_numbers is used (see ). Please + replace with one valid MSN of your ISDN interface or leave empty. This value can be + overwritten in the user sections individually. + + + + + Default setting which defines how many seconds we will wait for a successful connection after + dialing the number. This value can be overwritten in the user sections individually. + + + + + Default fax station ID to use when sending a fax document. The station ID is + usually the number of your fax station in international format, so an example would be + "+49 89 123456" for a number in Munich, Germany. Station IDs may only consist of the "+"-sign, + spaces and the digits 0-9. The maximal length is 20. This value can be overwritten in the user + sections individually. + + + + + Default fax headline to use when sending a fax document. Where and if this + headline will be presented depends on the implementation of your CAPI driver. The headline + should have a reasonable length to fit on the top of a page, but there's no definite limit + given. + + + + available options for user sections in fax config + + + User specific value for the global option above + + + + User specific value for the global option above + + + + User specific value for the global option above + + + + User specific value for the global option above + + + + A list containing the numbers on which this user wants to receive incoming fax calls. + These numbers are used to differ between users - so the same number must not appear in more + than one user section! The numbers are separated with commas and no blanks + are allowed. The first number of the list also serves as our own number when + sending a fax if outgoing_MSN is not set (see ) + If you want to use the same number for receiving fax and voice calls, please + do not enter it here. Use the voice_numbers option instead + (see ) - the answering machine has a built in fax detection + and can also receive faxes. + When this list is set to *, + all incoming calls will be accepted for this user (use with care!). + This is only useful for a setup with only one user which wants to receive any call as fax. + + + + + + If given, this string indicates an email-address where the received faxes and + voice calls will be sent to. If it is empty, they will be sent to the user account on the + system &cs; is running on. The address is also used to send status reports + for sent fax jobs to. If you don't want emails to be sent at all, use the + action option (see ). + + + + + Here you can define what action will be taken when a call is received. + Currently, three possible actions are supported: + + + + the received call will be mailed to the given address (see + above) and saved to the user_dir (see ) + + + + + + the call will be only saved to the user_dir (see ) + + + + + + + + + Configuration for the answering machine + This file holds all available config options for the answering machine. + + It's read from /etc/capisuite/answering_machine.conf or + /usr/local/etc/capisuite/answering_machine.conf (depending on the installation). + + available options for [GLOBAL] section in answering machine config + + + The answering machine script uses several wave files, for example + a global announcement if the user hasn't set his own and some spoken word fragments + for the remote inquiry and the menu presented there. These audio files are searched + in this directory. If user_audio_files is enabled (see ), each user can also + provide his own audio snippets in his user_dir (see ). + + + + This directory is used to save user specific data to. It must exist and + the user &cs; runs as must have write permission to it. It will contain + one subdirectory for each configured user (named like his userid). The + following subdirectories are used below the user-specific dir: + + + user_dir/username/ + Here the user may provide his own audio_files + (see also option above). + The user defined announcement is also saved here. + + + user_dir/username/received/ + Received voice calls are saved here. + + + + + + + If set to 1, each user may provide his own audio files + in his user directory (see ). If set to 0, + only the audio_dir (see ) will be searched. + + + + + Sets the default value for the delay for accepting an incoming + call in (in seconds). A value of 10 means that the answering + machine accepts incoming calls 10 seconds after the incoming connection request. + This value can be overwritten in the user sections individually. + + + + + Sets the default name for the announcement files for the users. + The announcement is searched in user_dir/username/announcement then. If not found, + a global announcement containing the called MSN will be played. This value can + be overwritten in the user sections individually. + + + + + Default setting for the maximum record length in seconds. This value can + be overwritten in the user sections individually. + + + + + Default setting for the record silence timeout in seconds. When set to a value + greater than 0, the recording will be aborted if silence is detected for the given + amount of seconds. Set this to 0 to disable it. This value can + be overwritten in the user sections individually. + + + + available options for user sections in answering machine config + + + User specific value for the global option above + + + + User specific value for the global option above + + + + User specific value for the global option above + + + + User specific value for the global option above + + + + A list containing the numbers on which this user wants to receive incoming voice calls. + These numbers are used to differ between users - so the same number must not appear in more + than one user section! The numbers are separated with commas and no blanks + are allowed. The answering machine script does also automatic fax detection, so a fax can + also be sent to this number. When this list is set to *, + all incoming calls will be accepted for this user (use with care!). + This is only useful for a setup with only one user which wants to receive any call. + + + + + If given, this string indicates an email-address where the received faxes and + voice calls will be sent to. If it is empty, they will be sent to the user account on the + system &cs; is running on. The address is also used to send status reports + for sent fax jobs to. If you don't want emails to be sent at all, use the + action option (see ). + + + + + The answering machine also supports a remote inquiry function. + This function is used by entering a PIN (Personal Identification Number) + while the announcement is played. This PIN can be setup here. + If you don't want to use the remote inquiry function, just use an empty + PIN setting. The PIN doesn't have a maximal length - but perhaps you should + not use 200 digits or you perhaps won't be able to remember them (I won't at least). ;-) + + + + + Here you can define what action will be taken when a call is received. + Currently, three possible actions are supported: + + + + the received call will be mailed to the given address (see + above) and saved to the user_dir (see ) + + + + + + the call will be only saved to the user_dir (see ) + + + + + only the announcement will be played - no voice file will + be recorded + + + + + + + + + + Deleting old files + + As written above, all incoming and outgoing calls will be saved on + the local file system to assure nothing gets lost. There's no cleaning + up done by &cs;, so these files will stay forever on your system + if you don't clean them up from time to time. + + As it's not very convenient to do this manually, I would advise to + automate this process. cron is predestinated for + such a task. On most modern GNU/Linux distributions, you can simply place + scripts in /etc/cron.daily and they will be called + automatically once a day. + + An example for a bash script you can use is also in the &cs; distribution. + Just copy capisuite.cron to /etc/cron.daily/capisuite + and assure it has correct permissions (owner root, executable bit set). + + Now create a file cronjob.conf holding a line like + + MAX_DAYS=30 + + in your &cs; configuration directory (usually /etc/capisuite + or /usr/local/etc/capisuite). This tells the cron job how + long the files should be stored. Each file which wasn't accessed in the + last MAX_DAYS days will be deleted when + /etc/cron.daily/capisuite is started. If + MAX_DAYS is set to 0, no cleaning up is done. You can + also place a cronjob.conf file with an own value for + MAX_DAYS in each directory which is cleaned up. + + + Using &cs; together with the default scripts + Receiving calls + Now this is a nice, short section. Once you have configured + &cs;, the scripts and started &cs; successfully, there's nothing + more you have to do. You'll get your mails as described in + and that's it. You only have + to setup your mail program to receive local mails. Enjoy! :-) + + Doing a remote inquiry + To do a remote inquiry, please enter your PIN (see ) + while the announcement of the answering machine is played. After some seconds + you will get a "voice menu" telling you how to record your own announcement + for your answering machine or how to playback the received calls. + + Sending fax jobs + The default scripts for &cs; also include a commandline + tool for sending faxes called capisuitefax. + + capisuitefax will be called with some parameters + telling it which file to send (it currently only supports PostScript files) + and to which number. It will then enqueue the job converted to the + right format into the send queue + from which it's collected by another &cs; script and sent to the + destination. If the sending was completed successfully or failed finally + after trying for some times, the according user will get an email + telling him what has happened. + + The following options are recognized by capisuitefax: + + capisuitefax -d dialstring [-h] [-q] file1 [file2...] + + + + the number which should be called (destination of the fax) + + + + show a short commandline help + + + + be quiet, don't output informational messages + + + + one or more PostScript files to send to this destination (more than + one PostScript file will produce several separate fax jobs) + + + + + + + +Users Guide + + In the last chapter you've seen how to use the default scripts distributed with &cs;. + But the main goal in developing &cs; was not to provide a perfect ready-to-use + application. I intended to develop a tool where you can write your own + applications very easy. I'll show you how to do this in the next sections. + + + Introduction to Python + + As I tought about the scripting language I wanted to integrate into &cs;, + my first idea was to develop an own, simple one. But as more as I looked into it, as + more I found that a general purpose language having common features already will be much + more helpful than re-inventing every wheel that I would need. So I looked for some + easy to integrate (and to learn) language. The one I liked most was Python - and it + also had a nice documentation about embedding, so I chose it and I'm still happy about + that decision. :-) + + So the first thing you'll have to do is to learn a little bit of Python. Don't be afraid - + it was developed as a beginners language and Guido (Guido van Rossum, the inventor of Python) + has done very well in my opinion. + + In the next few sections, I'll give you a short introduction to the features of Python + you most probably will need for &cs;. As this shouldn't be a manual about Python or a tutorial + in computer programming, I assume you're already familiar with the basic concepts of todays + wide-spread procedural and object-oriented languages. + + If not, I would advise you to get and read a book for learning Python - there are many + available in different languages - or to have a look on the Python home page on + where nice and comprehensive manuals and tutorials are + available for free. + + Python Basics + + Python supports most features you know from other common languages. So here's the + syntax of the basic operations shown in a Python session. A python session is another + fine feature of its interpreter: just start it by typing python + in a shell and you'll get the prompt of Python: + + + + + + Used file formats + &cs; always reads and saves files in the native format as they will be expected and given + by the CAPI ISDN drivers. This preserves it from having to convert everything from and to other + formats thus reducing unnecessary overhead. + + As these formats aren't that well-known and you will need special tools to convert or + view/play them, I'll give you a short overview of how you can do this. + + All tools which I refer to here are described in . See + there for informations how to get them. + + Format for voice files (inversed A-Law, 8kHz, mono) + ISDN transmits voice data as waves with a sample-rate of 8kHz in mono. To + save bandwith, a compression called A-Law is used (at least in Europe, other countries + like the USA use u-Law which is quite similar to A-Law). For any reason + beyond my understanding, they use a bit-reversed form of A-Law called + inversed A-Law. + + Creating A-Law files + + There are two possible ways to create A-Law files. + + The first one is to call your computer with your phone (either use the default answering + machine script and configure it as described in + or write a simple script yourself), record whatever you want and take the created + output file (when you use the default scripts please take the file from the + user_dir, not the attachment of the mail as this is already converted) and use it. + + You eventually want to trim the recorded file and remove unwanted + noise and silence at the beginning and the end. This can easily be done + by sox and play (which comes together + with sox). + + sox is used to convert a file while play + is used to just play it. Both support the same effects including the trim option. + Both also detect what type of file you are using by looking at the suffix of + your file name. So all your inversed A-Law files should be named something.la + (.la is the inversed form of .al which stands for A-Law). + + So let's first try to find the optimal values for the trim effect by calling + play: + + play myfile.la trim <start-offset> <duration> + + Now play around with start-offset and duration (both given in seconds) until + you find the right values. If you found them, you can use sox + to actually produce the needed file: + + sox myfile.la outfile.la trim <start-offset> <duration> + + You'll now get a file named outfile.la which should + contain what you want. + + The second way to create an inversed A-Law file is to record a normal WAV-file + with your favourite sound-tools and convert it to the destination format using + sox. You'll get the best results when your WAV file already + is in 8kHz, mono, 8 bit format. sox is able to convert + other waves if necessary but this usually will result in bad quality. + + Now you can convert the WAV to inversed A-Law by calling: + + sox myfile.wav -r 8000 -c 1 -b outfile.la + + + Playing A-Law files + Again, there are two possibilities. The play command + of sox is able to just play the inversed A-Law format without + any conversion. Just call play with the filename as parameter: + + play myfile.la + + But you can also use sox to convert the A-Law files to the more common + WAV format by just invoking: + + sox myfile.la outfile.wav + + The created outfile.wav can be played + by nearly any audio player without problems. + + + Format of fax files (Structured Fax Files) + CAPI-compliant drivers will expect and provide fax files in a so called + Structured Fax File (SFF). As this seems to be a CAPI-specific format, there + are not much tools out there for GNU/Linux which are capable of handling it. + Finally I found some small tools written by Peter Schäfer, so + we can use them thankfully :-). + + Creating a SFF + In current Ghostscript releases, a patch from Peter has been + included to produce SF files. To see if your Ghostscript already + supports it, enter gs --help and look for + the so-called cfax-device in the long device list + presented to you. If it's not listed, you have to take a newer Ghostscript + or recompile it, sorry. I don't know any other way to produce SFF currently. + + You need a PostScript file (as produced by nearly every Linux program + when you choose "print to file") first. Now you can call GhostScript to + convert it to a SFF: + + gs -dNOPAUSE -dQUIET -dBATCH -sDEVICE=cfax -sOutputFile=outfile.sff myfile.ps + + If you're not sure if it worked you can use sffview. + + Viewing / converting from SFF + To simply view a received SFF, you can use the sffview + program. It's a simple but useful tool for viewing SF files without the need + to convert them. Just start it and you will get a GUI where you can open the + desired file. + + If you want to convert a fax file to a more common format, I recommend + using sfftobmp. It supports quite some common output formats + like JPEG, TIFF, PBM or BMP. I prefer multipage TIFF files as this is the only + format being able to contain several pages in one file. To convert + SFF to multipage TIFF, call: + + sfftobmp -tif myfile.sff outfile.tiff + + This will give you a TIFF file which you can convert now to nearly + any other useful format with the TIFF tools, for example tiff2ps + + + + + + +Developers Guide + + +CAPI 2.0 Error Codes + The CAPI interface used here has its own coding of standard ISDN + error codes. Most of the errors described in + are only important for developers of the &cs; core. As user, you only need + to know the codes shown in + as they'll be used in the &cs; Python functions like disconnect (@ref). + + In the next two sections, you'll find a list of all codes and a short description. + A detailled description of the CAPI codes can be found in the CAPI specification available at + . + + All numbers are given hexadecimal! + +
CAPI errors describing connection problems + + All errors described here indicate some problem with the connection. + These errors are also important for script writers as they're returned by + some CapiSuite Python functions like capisuite.disconnect() (@ref). + +
Protocol errors + Protocol errors indicate some problem during data transfer. Only messages for + voice (transparent) and fax are shown here as these are the only protocols spoken + by &cs;. + + + 3301 - Protocol error layer 1 (broken line or B-channel removed by signalling protocol) + 3302 - Protocol error layer 2 + 3303 - Protocol error layer 3 + 3304 - Another application got that call + 3311 - T.30 (fax) error: Connection not successful (remote station is not a G3 fax device) + 3312 - T.30 (fax) error: Connection not successful (training error) + 3313 - T.30 (fax) error: Disconnect before transfer (remote station doesn' support transfer mode, e.g. wrong resolution) + 3314 - T.30 (fax) error: Disconnect during transfer (remote abort) + 3315 - T.30 (fax) error: Disconnect during transfer (remote procedure error) + 3316 - T.30 (fax) error: Disconnect during transfer (local transmit data underflow) + 3317 - T.30 (fax) error: Disconnect during transfer (local receive data overflow) + 3318 - T.30 (fax) error: Disconnect during transfer (local abort) + 3319 - T.30 (fax) error: Illegal parameter coding (e.g. defective SFF file) + +
+
ISDN error codes + + The codes shown here are ISDN error codes which are described by the + ETS 300 102-01 standard in more detail. It's currently available for private use at + without fee. For details how the ISDN codes + are mapped to the CAPI numbers see the CAPI specification, parameter "Info". + + + 3480 - Normal termination + 3481 - Unallocated (unassigned) number + 3482 - No route to specified transit network + 3483 - No route to destination + 3486 - Channel unacceptable + 3487 - Call awarded and being delivered in an established channel + 3490 - Normal call clearing + 3491 - User busy + 3492 - No user responding + 3493 - No answer from user (user alerted) + 3495 - Call rejected + 3496 - Number changed + 349A - Non-selected user clearing + 349B - Destination out of order + 349C - Invalid number format + 349D - Facility rejected + 349E - Response to STATUS ENQUIRY + 349F - Normal, unspecified + 34A2 - No circuit / channel available + 34A6 - Network out of order + 34A9 - Temporary failure + 34AA - Switching equipment congestion + 34AB - Access information discarded + 34AC - Requested circuit / channel not available + 34AF - Resources unavailable, unspecified + 34B1 - Quality of service unavailable + 34B2 - Requested facility not subscribed + 34B9 - Bearer capability not authorized + 34BA - Bearer capability not presently available + 34BF - Service or option not available, unspecified + 34C1 - Bearer capability not implemented + 34C2 - Channel type not implemented + 34C5 - Requested facility not implemented + 34C6 - Only restricted digital information bearer capability is available + 34CF - Service or option not implemented, unspecified + 34D1 - Invalid call reference value + 34D2 - Identified channel does not exist + 34D3 - A suspended call exists, but this call identity does not + 34D4 - Call identity in use + 34D5 - No call suspended + 34D6 - Call having the requested call identity has been cleared + 34D8 - Incompatible destination + 34DB - Invalid transit network selection + 34DF - Invalid message, unspecified + 34E0 - Mandatory information element is missing + 34E1 - Message type non-existent or not implemented + 34E2 - Message not compatible with call state or message type non-existent or not implemented + 34E3 - Information element non-existent or not implemented + 34E4 - Invalid information element contents + 34E5 - Message not compatible with call state + 34E6 - Recovery on timer expiry + 34EF - Protocol error, unspecified + 34FF - Interworking, unspecified + +
+
+ +
Internal CAPI errors + These errors are mainly of interest for developers of the CapiSuite core. If you're just a user, you + won't need most of them. + +
Informative values (no error) + These values are only warnings and may appear in the extensive CapiSuite log only in CAPI messages. + + 0000 - No error, request accepted + 0001 - NCPI not supported by current protocol, NCPI ignored + 0002 - Flags not supported by current protocol, flags ignored + 0003 - Alert already sent by another application + +
+
Errors concerning CAPI_REGISTER + These errors may appear when the application starts and mostly indicate problems with your driver installation. + + + 1001 - Too many applications. + 1002 - Logical Block size too small; must be at least 128 bytes. + 1003 - Buffer exceeds 64 kbytes. + 1004 - Message buffer size too small, must be at least 1024 bytes. + 1005 - Max. number of logical connections not supported. + 1006 - reserved (unknown error). + 1007 - The message could not be accepted because of an internal busy condition. + 1008 - OS Resource error (out of memory?). + 1009 - CAPI not installed. + 100A - Controller does not support external equipment. + 100B - Controller does only support external equipment. + +
+
Message exchange errors + These errors are really internal: they're raised if the application calls + CAPI in a wrong way. If they occur, it's usually a bug which you should tell + the &cs; team. + + + 1101 - Illegal application number. + 1102 - Illegal command or subcommand, or message length less than 12 octets. + 1103 - The message could not be accepted because of a queue full condition. + 1104 - Queue is empty. + 1105 - Queue overflow: a message was lost!! + 1106 - Unknown notification parameter. + 1107 - The message could not be accepted because on an internal busy condition. + 1108 - OS resource error (out of memory?). + 1109 - CAPI not installed. + 110A - Controller does not support external equipment. + 110B - Controller does only support external equipment. + +
+
Resource/Coding Errors + The errors described here are issued when the application tries to use a ressource which isn't available. + These are mostly also bugs in the application. Please tell us. + + + 2001 - Message not supported in current state + 2002 - Illegal Controller / PLCI / NCCI + 2003 - Out of PLCI + 2004 - Out of NCCI + 2005 - Out of LISTEN + 2007 - llegal message parameter coding + +
+
Errors concerning requested services + The errors described here are issued when the application tries to request a service wrong. + Again these are mostly bugs you should tell us. + + + 3001 - B1 protocol not supported + 3002 - B2 protocol not supported + 3003 - B3 protocol not supported + 3004 - B1 protocol parameter not supported + 3005 - B2 protocol parameter not supported + 3006 - B3 protocol parameter not supported + 3007 - B protocol combination not supported + 3008 - NCPI not supported + 3009 - CIP Value unknown + 300A - Flags not supported (reserved bits) + 300B - Facility not supported + 300C - Data length not supported by current protocol + 300D - Reset procedure not supported by current protocol + +
+
+
+ + + + +
diff --git a/rc.capisuite.in b/rc.capisuite.in new file mode 100755 index 0000000..4a258b2 --- /dev/null +++ b/rc.capisuite.in @@ -0,0 +1,180 @@ +#! /bin/bash +# Copyright (c) 1995-2002 SuSE Linux AG, Nuernberg, Germany. +# All rights reserved. +# +# Author: Kurt Garloff , Gernot Hillier +# +# This file was written for the use with SuSE Linux, but it should +# (hopefully) work for any other LSB compliant distribution. If you need to +# modify it, I'll welcome your feedback. TIA! +# +# /etc/init.d/capisuite +# and its symbolic link +# /usr/sbin/rccapisuite +# +# system startup script for the CapiSuite daemon +# +# LSB compatible service control script; see http://www.linuxbase.org/spec/ +# +### BEGIN INIT INFO +# Provides: capisuite +# Required-Start: $syslog $remote_fs isdn +# X-UnitedLinux-Should-Start: $time ypbind sendmail +# Required-Stop: $syslog $remote_fs isdn +# X-UnitedLinux-Should-Stop: $time ypbind sendmail +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Short-Description: CapiSuite daemon providing ISDN fax and voice services +# Description: Start CapiSuite to use the default scripts included +# for the ISDN fax and answering machine. It tests for configured +# fax and answering machine users so please modify if you want +# to use your own scripts! +### END INIT INFO +# + +# Check for missing binaries (stale symlinks should not happen) +CAPISUITE_BIN=@bindir@/capisuite +test -x $CAPISUITE_BIN || exit 5 + +# Check for existence of needed config file and read it +CAPISUITE_CONFIG=@pkgsysconfdir@/capisuite.conf +test -r $CAPISUITE_CONFIG || exit 6 + +# Shell functions sourced from /etc/rc.status: +# rc_check check and set local and overall rc status +# rc_status check and set local and overall rc status +# rc_status -v ditto but be verbose in local rc status +# rc_status -v -r ditto and clear the local rc status +# rc_status -s display "skipped" and exit with status 3 +# rc_status -u display "unused" and exit with status 3 +# rc_failed set local and overall rc status to failed +# rc_failed set local and overall rc status to +# rc_reset clear local rc status (overall remains) +# rc_exit exit appropriate to overall rc status +# rc_active checks whether a service is activated by symlinks +# rc_splash arg sets the boot splash screen to arg (if active) +. /etc/rc.status + +# Reset status of this service +rc_reset + +# Return values acc. to LSB for all commands but status: +# 0 - success +# 1 - generic or unspecified error +# 2 - invalid or excess argument(s) +# 3 - unimplemented feature (e.g. "reload") +# 4 - user had insufficient privileges +# 5 - program is not installed +# 6 - program is not configured +# 7 - program is not running +# 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) +# +# Note that starting an already running service, stopping +# or restarting a not-running service as well as the restart +# with force-reload (in case signaling is not supported) are +# considered a success. + +case "$1" in + start) + echo -n "Starting CapiSuite " + configured=yes + # Check if there are configured users for fax or + # answering machine. Otherwise exit. + # IMPORTANT: Change this or comment it out if you want to use + # your own CapiSuite scripts. + while read -r sec rest ; do + if [ "${sec:0:1}" = "[" -a "$sec" != "[GLOBAL]" ]; then + configured_fax=yes + break + fi + done < <(cat @pkgsysconfdir@/fax.conf) + while read -r sec rest ; do + if [ "${sec:0:1}" = "[" -a "$sec" != "[GLOBAL]" ]; then + configured_voice=yes + break + fi + done < <(cat @pkgsysconfdir@/answering_machine.conf) + test "$configured_fax" -o "$configured_voice" || configured=no + # end check for configured users + + ## Start daemon with startproc(8). If this fails + ## the return value is set appropriately by startproc. + if [ $configured = "yes" ]; then + startproc $CAPISUITE_BIN -d + else + rc_failed 6 + fi + + # Remember status and be verbose + rc_status -v + ;; + stop) + echo -n "Shutting down CapiSuite " + ## Stop daemon with killproc(8) and if this fails + ## killproc sets the return value according to LSB. + + killproc -TERM $CAPISUITE_BIN + + # Remember status and be verbose + rc_status -v + ;; + try-restart) + ## Do a restart only if the service was active before. + ## Note: try-restart is not (yet) part of LSB (as of 1.2) + $0 status >/dev/null && $0 restart + + # Remember status and be quiet + rc_status + ;; + restart) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + + # Remember status and be quiet + rc_status + ;; + force-reload) + ## Signal the daemon to reload its config. Most daemons + ## do this on signal 1 (SIGHUP). + ## If it does not support it, restart. + + echo -n "Reload service CapiSuite " + killproc -HUP $CAPISUITE_BIN + rc_status -v + ;; + reload) + ## Like force-reload, but if daemon does not support + ## signaling, do nothing (!) + + echo -n "Reload service CapiSuite " + killproc -HUP $CAPISUITE_BIN + rc_status -v + + ;; + status) + echo -n "Checking for service CapiSuite " + ## Check status with checkproc(8), if process is running + ## checkproc will return with exit status 0. + + # Return value is slightly different for the status command: + # 0 - service up and running + # 1 - service dead, but /var/run/ pid file exists + # 2 - service dead, but /var/lock/ lock file exists + # 3 - service not running (unused) + # 4 - service status unknown :-( + # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) + + # NOTE: checkproc returns LSB compliant status values. + checkproc $CAPISUITE_BIN + # NOTE: rc_status knows that we called this init script with + # "status" option and adapts its messages accordingly. + rc_status -v + ;; + *) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" + exit 1 + ;; +esac +rc_exit diff --git a/scripts/.cvsignore b/scripts/.cvsignore new file mode 100644 index 0000000..6866f16 --- /dev/null +++ b/scripts/.cvsignore @@ -0,0 +1,5 @@ +answering_machine.conf +fax.conf +cs_helpers.py +Makefile +Makefile.in diff --git a/scripts/Makefile.am b/scripts/Makefile.am new file mode 100644 index 0000000..f1ed3fa --- /dev/null +++ b/scripts/Makefile.am @@ -0,0 +1,37 @@ +spooldir = @localstatedir@/spool/capisuite +pkgsysconfdir = @sysconfdir@/capisuite + +dist_pkglib_DATA = idle.py incoming.py README +python_module_DATA = cs_helpers.py +EXTRA_DIST = cs_helpers.pyin fax.confin answering_machine.confin + +pkgsysconf_DATA = fax.conf answering_machine.conf + +dist_bin_SCRIPTS = capisuitefax + +SUBDIRS = waves + +.pyin.py: + rm -f $@ + sed -e 's,@\pkgsysconfdir@,$(pkgsysconfdir),g' $< >$@ + +.confin.conf: + rm -f $@ + sed -e 's,@pkgdatadir\@,$(pkgdatadir),g' \ + -e 's,@spooldir\@,$(spooldir),g' $< >$@ + +uninstall-hook: + -rmdir $(DESTDIR)$(pkglibdir) + -rmdir $(DESTDIR)$(spooldir)/sendq $(DESTDIR)$(spooldir)/done \ + $(DESTDIR)$(spooldir)/failed $(DESTDIR)$(spooldir)/users $(DESTDIR)$(spooldir) + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(spooldir)/sendq + $(mkinstalldirs) $(DESTDIR)$(spooldir)/done + $(mkinstalldirs) $(DESTDIR)$(spooldir)/failed + $(mkinstalldirs) $(DESTDIR)$(spooldir)/users + +clean-local: + rm -f cs_helpers.py + rm -f fax.conf answering_machine.conf + diff --git a/scripts/README b/scripts/README new file mode 100644 index 0000000..ada1dc9 --- /dev/null +++ b/scripts/README @@ -0,0 +1,2 @@ +This directory holds the global python scripts for CapiSuite. Please +see the CapiSuite documentation for further details. diff --git a/scripts/answering_machine.confin b/scripts/answering_machine.confin new file mode 100644 index 0000000..ab431bb --- /dev/null +++ b/scripts/answering_machine.confin @@ -0,0 +1,125 @@ +# $Id: answering_machine.confin,v 1.1 2003/02/19 08:19:54 gernot Exp $ +# +# This is the configuration file for the answering machine scripts distributed +# with CapiSuite +# +# It is read by the incoming.py script which is distributed with CapiSuite. +# If you don't want to use it but develop your completely own application, +# you won't need it! CapiSuite itself (the daemon) doesn't read it. +# +# For a further description, please see the CapiSuite documentation - +# there's a part describing the scripts. +# +# As usual, lines starting with # or empty lines will be ignored +# +# The rest must be key value pairs written as key=value or section names. +# +# Additional whitespaces and quotation marks (") surrounding +# the values will be ignored. +# +# The file is split in sections starting with "[sectionname]". The section +# [GLOBAL] contains all options common for all users. For each different user, +# an own section is used which must at least contain "voice_numbers". +# +# Nearly all global options can be overwritten in the [user]-sections + +############################################################################### +############################ global settings ################################## +############################################################################### + +[GLOBAL] + +# Directory where audio snippets used in the answering machine script are +# located. If user_audio_files is enabled (see below), each user can also +# provide his own audio snippets in his user_dir (see below). +audio_dir="@pkgdatadir@/" + +# Directory for all user-specific data. Contains one subdirectory +# for each user (named like his userid). The following directory tree is used: +# +# user_dir/username/ - here the user may provide his own audio_files +# (see user_audio_files option). The user defined announcement +# is also saved here. +# user_dir/username/received - all received calls (voice and fax) will be saved here +voice_user_dir="@spooldir@/users/" + +# Controls wether the user_dir (see below) will also be searched for audio +# files. If set to "1", the script will look in the user_dir and then in +# audio_dir for a needed audio file. If "0", only audio_dir is used. +# This doesn't affect the announcement, which can and should be different +# for each user in any case. +user_audio_files="1" + +# Global setting for the time in seconds before incoming voice calls are +# accepted. +voice_delay="15" + +# This value gives the default name of the announcement file which is searched +# in the user_dir. Each user should provide one in his/her own dir. +announcement="announcement.la" + +# record_length +# +# Global setting for the maximal record length of the answering machine +# in seconds +record_length="60" + +# record_silence_timeout +# +# Global setting for the length of silence after which recording is +# finished by the answering machine. +record_silence_timeout="5" + +############################################################################### +############################# user settings ################################### +############################################################################### + +# The following sections start with the name of the users which want to use +# CapiSuite. The names must be exactly equal to system users. +# +# Each user section can override the following default options given above: +# +# voice_delay, announcement, record_length, record_silence_timeout +# +# Additionally, the following options are possible: +# +# voice_numbers="13,14" +# This list contains the numbers on which this user wants to receive incoming +# voice calls. The values are separated by commas. You can also use the special +# entry "*" which stands for accepting ALL incoming calls (use with care!). +# +# voice_email="name@domain.de" +# If given, this string indicates an email-address where the received faxes +# and voice calls will be sent to. If it is empty, the recorded calls and +# faxes will be sent to the user on the current system. It's also used to +# send status reports for sent fax jobs to. If you don't want to get emails, +# see the "action" option below +# +# pin="" +# pin for activating the remote inquiry. Start typing when the announcement +# is played. If you don't want remote inquiry function for your answering +# machine for security or other reasons, just set this to an empty string. +# You can use as many digits as you want. The script will wait 2 seconds after +# each typed digit for the next one. +# +# voice_action="" +# Here you can define what action will be taken when a call is received. +# Currently, three possible actions are supported: +# +# MailAndSave - the received call will be mailed to the given address (see +# "email" above) and saved to the user_dir. +# SaveOnly - the call will be only saved to the user_dir +# None - only the announcement will be played - no voice file will +# be recorded +# +# Here's an example of a valid user configuration for "gernot" - just remove +# the leading #-signs and edit it: +# +# [gernot] +# voice_numbers="13,14" +# voice_action="MailAndSave" +# voice_delay="10" +# record_length="60" +# voice_email="" # sent to gernot@localhost +# pin="99*45" + diff --git a/scripts/capisuitefax b/scripts/capisuitefax new file mode 100755 index 0000000..70d6a54 --- /dev/null +++ b/scripts/capisuitefax @@ -0,0 +1,168 @@ +#!/usr/bin/python +# +# capisuitefax - capisuite tool for enqueuing faxes +# --------------------------------------------------- +# copyright : (C) 2002 by Gernot Hillier +# email : gernot@hillier.de +# version : $Revision: 1.1 $ +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +import getopt,os,sys,re,time,pwd,errno,fcntl +# capisuite stuff +import cs_helpers + +dialstring="" +abort="" +quiet=0 +listqueue=0 + +def usage(error=""): + print "capisuitefax - capisuite tool for enqueueing faxes" + print + print "usage:" + print "capisuitefax [-q] -d file1 [file2...] or" + print "capisuitefax [-q] -a or" + print "capisuitefax [-h] [-l]" + print + print "possible options are:" + print + print "-d , --dialstring= send fax to this number (required)" + print "-a , --abort= abort fax job with id (id is a number)" + print "-l, --list print the jobs in the send queue" + print "-h, --help print this usage information" + print "-q, --quiet be quiet, don't output informational messages" + print + print "The given files must be in Adobe PostScript format" + if (error!=""): + print + print "ERROR:",error + sys.exit(1) + +def showlist(config,user): + sendq=config.get("GLOBAL","fax_user_dir")+user+"/sendq/" + + print "ID Number Tries Next try" + + files=os.listdir(sendq) + files=filter (lambda s: re.match("fax-.*\.txt",s),files) + if (not len(files)): + print "--- queue empty ---" + + for job in files: + control=cs_helpers.readConfig(sendq+job) + sys.stdout.write(re.match("fax-([0-9]+)\.txt",job).group(1)) + sys.stdout.write("\t") + sys.stdout.write(control.get("GLOBAL","dialstring")) + if (len(control.get("GLOBAL","dialstring"))<8): + sys.stdout.write("\t") + sys.stdout.write("\t") + sys.stdout.write(control.get("GLOBAL","tries")) + sys.stdout.write("\t\t") + sys.stdout.write(control.get("GLOBAL","starttime")+"\n") + + sys.exit(0) + +def abortjob(config,user,job): + sendq=config.get("GLOBAL","fax_user_dir")+user+"/sendq/" + job="fax-"+job+".txt" + + if (not os.access(sendq+job,os.W_OK)): + print "job to abort not valid" + sys.exit(1) + + try: + lockfile=open(sendq+job[:-3]+"lock","w") + fcntl.lockf(lockfile,fcntl.LOCK_EX | fcntl.LOCK_NB) # lock so that it isn't deleted while sending + os.unlink(sendq+job) + os.unlink(sendq+job[:-3]+"sff") + fcntl.lockf(lockfile,fcntl.LOCK_UN) + os.unlink(sendq+job[:-3]+"lock") + except IOError,err: + if (err.errno in (errno.EACCES,errno.EAGAIN)): + print "Sorry, this job is currently in transmission. Can't abort." + + +try: + optlist,args = getopt.getopt(sys.argv[1:], "d:a:lhq", ['dialstring=','help',"abort=","list","quiet"]) + +except getopt.GetoptError, e: + usage(e.msg) + +# read options +for option,param in optlist: + if option in ('-d','--dialstring'): dialstring=param + if option in ('-h','--help'): usage() + if option in ('-l','--list'): listqueue=1 + if option in ('-a','--abort'): abort=param + if option in ('-q','--quiet'): quiet=1 +if (not abort and not listqueue and not dialstring): + usage("No usable command given.") +for i in dialstring: + if ((i>'9' or i<'0') and i not in ('+')): + usage("Invalid dialstring given.") + +if (dialstring and len(args)==0): + usage("No fax files given") + +# test if this user is allowed to send faxes +config=cs_helpers.readConfig() +user=pwd.getpwuid(os.getuid())[0] +if (not config.has_section(user)): + print "Sorry, you're no valid user for CapiSuite" + sys.exit(1) +if (not config.has_option(user,"fax_numbers")): + print "Sorry, you're not allowed to use fax services" + sys.exit(1) + +# test environment +sendq=config.get("GLOBAL","fax_user_dir")+user+"/sendq/" +if (not os.access(sendq,os.W_OK)): + print "can't write to queue dir" + sys.exit(1) + +if (listqueue): + showlist(config,user) + +if (abort): + abortjob(config,user,abort) + +# convert and enqueue files +for i in args: + if (not os.access(i,os.R_OK)): + sys.stderr.write("can't open "+i+'\n') + continue + t=os.popen("file -b -i "+i+" 2>/dev/null") + filetype=t.read() + if (t.close()): + usage("can't execute \"file\"") + if (not re.search("application/postscript",filetype)): + sys.stderr.write(i+" is not a PostScript file\n") + continue + + newname=cs_helpers.uniqueName(sendq,"fax","sff") + + ret=(os.system("gs -dNOPAUSE -dQUIET -dBATCH -sDEVICE=cfax -sOutputFile="+newname+" "+i))>>8 + if (ret): + sys.stderr.write("error during SFF-conversion at file "+i+'. Ghostscript not installed?\n') + sys.exit() + + cs_helpers.writeDescription(newname,"dialstring=\""+dialstring+"\"\n" + +"starttime=\""+time.ctime()+"\"\ntries=\"0\"\n" + +"user=\""+user+"\"\n") + print i,"successful enqueued as",newname,"for",dialstring + + + + + + + + + + + + diff --git a/scripts/cs_helpers.pyin b/scripts/cs_helpers.pyin new file mode 100644 index 0000000..fc8dc6f --- /dev/null +++ b/scripts/cs_helpers.pyin @@ -0,0 +1,340 @@ +# cs_helpers.py - some helper functions for CapiSuite scripts +# ----------------------------------------------------------- +# copyright : (C) 2002 by Gernot Hillier +# email : gernot@hillier.de +# version : $Revision: 1.1 $ +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# the name of the config file read by the scripts; see there for options and +# descriptions +configfile_fax="@pkgsysconfdir@/fax.conf" +configfile_voice="@pkgsysconfdir@/answering_machine.conf" + +# @brief read configuration file and return a ConfigParser object +# +# The configfile is read from the path given above and the surrounding +# quotation marks from the values are removed +# +# @return the constructed config file object +def readConfig(file=""): + import ConfigParser + config=ConfigParser.ConfigParser() + if (file==""): + config.readfp(open(configfile_fax)) + config.readfp(open(configfile_voice)) + else: + config.readfp(open(file)) + for s in config.sections(): + for o in config.options(s): + value=config.get(s,o) + if (len(value)>1 and value[0]=='"'): + config.set(s,o,value[1:-1]) + if (not config.has_section('GLOBAL')): + raise IOError("invalid configuration file - section GLOBAL is missing") + return config + +# @brief get an option from the user or global section +# +# The option is searched in the users section and if not found +# in the global section. +# +# @param config the ConfigParser object containing the values +# @param user the name of the user section to use +# @param option the name of the option to search for +# +# @return the value for this option or None if it's not found +def getOption(config,user,option): + if config.has_option(user,option): + return config.get(user,option) + elif config.has_option('GLOBAL',option): + return config.get('GLOBAL',option) + else: + return None + +# @brief Search for an audio file first in user_dir, than in audio_dir +# +# @param config the ConfigParser object containing the configuration +# @param user the name of the user +# @param filename the filename of the wave file +# +# @return the found file with full path +def getAudio(config,user,filename): + import os + systemdir=config.get('GLOBAL','audio_dir') + userdir=config.get('GLOBAL','voice_user_dir')+user+"/" + if (config.getint('GLOBAL','user_audio_files') and os.access(userdir+filename,os.R_OK)): + return userdir+filename + else: + return systemdir+filename + +# @brief thread-safe creation of a unique filename in a directory +# +# This function reads the nextnumber from then "nextnr"-file in the given +# directory and updates it. It holds the next free file number. +# +# If nextnr doesn't exist, it's created. +# +# The filenames created will have the format +# +# basename-number.suffix +# +# @param directory name of the directory to work in +# @param basename the basename of the filename +# @param suffix the suffix of the filename (without ".") +# +# @return new file name +def uniqueName(directory,basename,suffix): + import fcntl,os,re + # acquire lock + lockfile=open(directory+"cs_lock","w") + fcntl.lockf(lockfile,fcntl.LOCK_EX) + + try: + countfile=open(directory+basename+"-nextnr","r") + nextnr=int(countfile.readline()) + countfile.close() + except IOError: + # search for next free sequence number + files=os.listdir(directory) + files=filter (lambda s: re.match(re.escape(basename)+"-.*\."+re.escape(suffix),s),files) + if (len(files)): + files=map(lambda s: int(s[len(basename)+1:-len(suffix)-1]),files) + nextnr=max(files)+1 # take nr of last file and increase it by one + else: + nextnr=0 + files.sort() + + newname=directory+basename+"-"+str(nextnr)+"."+suffix + + countfile=open(directory+basename+"-nextnr","w") + countfile.write(str(nextnr+1)+'\n') + countfile.close() + + # unlock + fcntl.lockf(lockfile,fcntl.LOCK_UN) + lockfile.close() + os.unlink(directory+"cs_lock") + return newname + +# @brief send email with text and attachment of type sff or la converted to pdf/wav +# +# This function creates a multipart MIME-message containing a text/plain +# part with a string and one attachment of type application/pdf or audio/wav. +# +# The given attachment is automatically converted from Structured Fax File +# (.sff) or inversed A-Law (.la) to the well known PDF or WAV format. +# +# @param mail_from the From: address for the mail +# @param mail_to the To: address for the mail +# @param mail_subject the subject of the mail +# @param mail_type containing either "sff" or "la" +# @param text a string containing the text of the first part of the mail +# @param attachment name of the file to send as attachment +def sendMIMEMail(mail_from,mail_to,mail_subject,mail_type,text,attachment): + import email.MIMEBase,email.MIMEText,email.MIMEAudio,email.Encoders,os,sys,popen2,capisuite + msg = email.MIMEBase.MIMEBase("multipart","mixed") + msg['Subject']=mail_subject + msg['From']=mail_from + msg['To']=mail_to + + msg.preamble = 'This is a Multipart-MIME-message. Please use a capable mailer.\n' + msg.epilogue = '' # To guarantee the message ends with a newline + + basename=attachment[:attachment.rindex('.')+1] + try: + if (mail_type=="sff"): + # sff -> tif + ret=os.spawnlp(os.P_WAIT,"sfftobmp","sfftobmp","-tif",attachment,basename+"tif") + if (ret or not os.access(basename+"tif",os.F_OK)): + raise "conv-error","Can't convert sff to tif. sfftobmp not installed?" + # tif -> ps -> pdf + tiff2ps=popen2.Popen3("tiff2ps -a "+basename+"tif") + if (tiff2ps.poll()!=-1): + raise "conv-error","Error while calling tiff2ps. Not installed?" + tiff2ps.tochild.close() # we don't need the input pipe + ps2pdf=popen2.Popen3("ps2pdf - -") + if (ps2pdf.poll()!=-1): + raise "conv-error","Error while calling ps2pdf. Not installed?\n" + ps2pdf.tochild.write(tiff2ps.fromchild.read()) + tiff2ps.fromchild.close() + ret=tiff2ps.wait() + if (ret!=0): + raise "conv-error","Error "+str(ret)+" occured during tiff2ps" + os.unlink(basename+"tif") + ps2pdf.tochild.close() # send EOF, so that it starts to convert + # create attachment with pdf stream + filepart = email.MIMEBase.MIMEBase("application","pdf") + filepart.add_payload(ps2pdf.fromchild.read()) + ps2pdf.fromchild.close() + ret=ps2pdf.wait() + if (ret!=0): + raise "conv-error","Error "+str(ret)+" occured during ps2pdf" + email.Encoders.encode_base64(filepart) + elif (mail_type=="la"): + # la -> wav + # don't use stdout as sox needs a file to be able to seek in it otherwise the header will be incomplete + ret = os.spawnlp(os.P_WAIT,"sox","sox",attachment,basename+"wav") + if (ret or not os.access(basename+"wav",os.R_OK)): + raise "conv-error","Error while calling sox. Not installed?" + filepart = email.MIMEAudio.MIMEAudio(open(basename+"wav").read(),"x-wav") + os.unlink(basename+"wav") + textpart = email.MIMEText.MIMEText(text) + msg.attach(textpart) + msg.attach(filepart) + except "conv-error",errormessage: + text+="\n\nERROR occured while converting file: "+errormessage+"\nPlease talk to your friendly administrator.\n" + textpart = email.MIMEText.MIMEText(text) + msg.attach(textpart) + + sendmail = popen2.Popen3("sendmail -f "+mail_from+" "+mail_to) + if (sendmail.poll()!=-1): + capisuite.error("Error while calling sendmail. Not installed?\n") + return + sendmail.tochild.write(msg.as_string()) + sendmail.tochild.close() + sendmail.fromchild.close() + ret=sendmail.wait() + if (ret!=0): + capisuite.error("Error while calling sendmail, return code="+str(ret)) + else: + capisuite.log("sendmail finished successful",3) + +# @brief send a simple text email +# +# This function creates a simple mail +# +# @param mail_from the From: address for the mail +# @param mail_to the To: address for the mail +# @param mail_subject the subject of the mail +# @param text a string containing the text of the first part of the mail +def sendSimpleMail(mail_from,mail_to,mail_subject,text): + import email.Encoders, email.MIMEText, popen2, sys,capisuite + # Create a text/plain message, using Quoted-Printable encoding for non-ASCII + # characters. + msg = email.MIMEText.MIMEText(text, _encoder=email.Encoders.encode_quopri) + + msg['Subject'] = mail_subject + msg['From'] = mail_from + msg['To'] = mail_to + + sendmail = popen2.Popen3("sendmail -f "+mail_from+" "+mail_to) + if (sendmail.poll()!=-1): + capisuite.error("Error while calling sendmail. Not installed?\n") + return + sendmail.tochild.write(msg.as_string()) + sendmail.tochild.close() + sendmail.fromchild.close() + ret=sendmail.wait() + if (ret!=0): + capisuite.error("Error while calling sendmail, return code="+str(ret)) + else: + capisuite.log("sendmail finished successful",3) + + +# @brief write description file for received fax or voice +# +# This function writes an INI-style description file for the given data file +# which can later on be read by a ConfigParser instance. The data file name +# is used, the extension stripped and replaced by .txt +# +# @param filename the data filename (with extension!) +# @param content the content as string +def writeDescription(filename,content): + descr=open(filename[:filename.rindex('.')+1]+"txt","w") + descr.write("# Description file for "+filename+"\n") + descr.write("# This if for internal use of CapiSuite.\n") + descr.write("# Only change if you know what you do!!\n") + descr.write("[GLOBAL]\n") + descr.write("filename=\""+filename+"\"\n") + descr.write(content) + descr.close() + +# @brief say a german number +# +# All numbers from 0 to 99 are said correctly, while all larger ones are +# split into numbers and only the numbers are said one after another +# +# @param call reference to the call +# @param number the number to say +# @param curr_user the current user named +# @param config the ConfigParser instance holding the configuration info +def sayNumber(call,number,curr_user,config): + import capisuite + if (len(number)==2 and number[0]!="0"): + if (number[0]=="1"): + if (number[1]=="0"): + capisuite.audio_send(call,getAudio(config,curr_user,"10.la"),1) + elif (number[1]=="1"): + capisuite.audio_send(call,getAudio(config,curr_user,"11.la"),1) + elif (number[1]=="2"): + capisuite.audio_send(call,getAudio(config,curr_user,"12.la"),1) + elif (number[1]=="3"): + capisuite.audio_send(call,getAudio(config,curr_user,"13.la"),1) + elif (number[1]=="4"): + capisuite.audio_send(call,getAudio(config,curr_user,"14.la"),1) + elif (number[1]=="5"): + capisuite.audio_send(call,getAudio(config,curr_user,"15.la"),1) + elif (number[1]=="6"): + capisuite.audio_send(call,getAudio(config,curr_user,"16.la"),1) + elif (number[1]=="7"): + capisuite.audio_send(call,getAudio(config,curr_user,"17.la"),1) + elif (number[1]=="8"): + capisuite.audio_send(call,getAudio(config,curr_user,"18.la"),1) + elif (number[1]=="9"): + capisuite.audio_send(call,getAudio(config,curr_user,"19.la"),1) + else: + if (number[1]=="0"): + capisuite.audio_send(call,getAudio(config,curr_user,number+".la"),1) + elif (number[1]=="1"): + capisuite.audio_send(call,getAudio(config,curr_user,"ein.la"),1) + capisuite.audio_send(call,getAudio(config,curr_user,"und.la"),1) + capisuite.audio_send(call,getAudio(config,curr_user,number[0]+"0.la"),1) + else: + capisuite.audio_send(call,getAudio(config,curr_user,number[1]+".la"),1) + capisuite.audio_send(call,getAudio(config,curr_user,"und.la"),1) + capisuite.audio_send(call,getAudio(config,curr_user,number[0]+"0.la"),1) + else: + for i in number: + capisuite.audio_send(call,getAudio(config,curr_user,i+".la"),1) + +# $Log: cs_helpers.pyin,v $ +# Revision 1.1 2003/02/19 08:19:54 gernot +# Initial revision +# +# Revision 1.8 2003/02/10 14:03:34 ghillie +# - cosmetical fixes in sendMIMEMail +# - added wait() calls to popen objects, otherwise processes will hang +# after CapiSuite has run them (i.e. sendmail stays as Zombie) +# +# Revision 1.7 2003/02/03 14:47:49 ghillie +# - sayNumber now works correctly for all numbers between 0 and 99 +# (in german). Added the necessary voice files and improved "1"-"9" +# +# Revision 1.6 2003/01/27 21:55:10 ghillie +# - getOption returns now None if option isn't found at all (no exception) +# - removed capisuite.log from uniqueName() (not possible in capisuitefax!) +# - added some missing "import capisuite" statements +# +# Revision 1.5 2003/01/27 19:24:29 ghillie +# - updated to use new configuration files for fax & answering machine +# +# Revision 1.4 2003/01/19 12:02:40 ghillie +# - use capisuite log functions instead of stdout/stderr +# +# Revision 1.3 2003/01/17 15:08:17 ghillie +# - typos as usual... +# - added sendSimpleMail for normal text messages +# +# Revision 1.2 2003/01/15 15:52:49 ghillie +# - readConfig now takes filename as parameter +# - uniqueName: countfile now has basename as prefix, fixed small bug +# in countfile creation +# - sendMail: added .la->.wav convertion, error messages now included +# in messages to user +# - writeDescription: [data] renamed to [global] +# - sayNumber: small fixes +# diff --git a/scripts/fax.confin b/scripts/fax.confin new file mode 100644 index 0000000..0570690 --- /dev/null +++ b/scripts/fax.confin @@ -0,0 +1,142 @@ +# $Id: fax.confin,v 1.1 2003/02/19 08:19:54 gernot Exp $ +# +# This is the fax configuration file for the scripts distributed with CapiSuite +# +# It is read by the scripts which are distributed with CapiSuite (incoming.py, +# idle.py and capisuitefax). If you don't want to use these scripts and develop +# your completely own application, you won't need it! CapiSuite itself (the +# daemon) doesn't read it. +# +# For a further description, please see the CapiSuite documentation - there's a +# part describing the scripts and this config file. +# +# As usual, lines starting with # or empty lines will be ignored +# +# The rest must be key value pairs written as key=value or section names. +# +# Additional whitespaces and quotation marks (") surrounding +# the values will be ignored. +# +# The file is split in sections starting with "[sectionname]". The section +# [GLOBAL] contains all options common for all users. For each different user, +# an own section is used which must at least contain "fax_numbers" +# +# Nearly all options are available in the [GLOBAL] section and +# the user sections. The defaults from the global section can be overwritten in +# [user]-sections. + +############################################################################### +############################ global settings ################################## +############################################################################### + +[GLOBAL] + +# Directory where idle.py will save its data. There must exist two +# subdirectories: +# +# spool_dir/done - a successful delivered job is moved here +# spool_dir/failed - jobs which have finally failed live here +spool_dir="@spooldir@/" + +# Directory for all user-specific data. Contains one subdirectory +# for each user (named like his userid). The following directory tree is used below: +# +# user_dir/username/received - all received calls (voice and fax) will be saved here +# user_dir/username/sendq - the files to send will be queued here +fax_user_dir="@spooldir@/users/" + +# send_tries +# +# Number of tries for sending a fax document. After completing the +# given number of tries, the document will considered as failed. +send_tries="10" + +# send_delays +# +# Delays in seconds between the send_tries. The different values are separated +# by commas. The first value gives the delay between the first and the second +# try and so on. The list should have send_tries-1 values. If some values are +# missing, the last value will be used for all subsequent tries. Superfluous +# values will be ignored. +send_delays="60,60,60,300,300,3600,3600,18000,36000" + +# send_controller +# +# This value defines which one of the installed controllers will be used for +# sending faxes. All controllers are numbered beginning with "1". If you +# have only one controller installed, leave this value alone. Unfortunately, +# there's only one send_controller supported currently. +send_controller="1" + +# outgoing_MSN +# +# The MSN (number) to use for outgoing calls. You can also leave this empty. +# Then default MSN of your ISDN interface will be used automatically. Will +# be overwritten by the first entry of fax_numbers if set. +outgoing_MSN="" + +# outgoing_timeout +# +# This value decides how long to wait for a successful connection if the other +# party doesn't answer the call at once. +outgoing_timeout="60" + +# fax_stationID +# +# This is the default for the fax station ID (fax number send to the other +# party). It must only contain the following characters ,'+','0'..'9'. +# The maximal length is 20 chars. +fax_stationID="+49 000 0000" + +# fax_headline +# +# This is the default for the fax headline. There's no definitive length +# constraint given by the CAPI specification, so it may be dependent on +# the driver you use. Just use a reasonable short string. +fax_headline="Fax sent by CapiSuite (http://www.capisuite.de)" + +############################################################################### +############################# user settings ################################### +############################################################################### + +# The following sections start with the name of the users which want to use +# CapiSuite. The names must be exactly equal to system users. +# +# Each user section can override the following default options given above: +# +# outgoing_MSN, outgoing_timeout, fax_stationID, fax_headline +# +# Additionally, the following options are possible: +# +# fax_numbers=",, ..." +# A list containing the numbers on which this user wants to receive incoming +# fax calls. The values are separated by commas. The first number is also +# used as our own number for outgoing calls. This overrides outgoing_MSN. +# You can also use the special entry "*" which stands for accepting ALL +# incoming calls as fax (use with care!) +# +# fax_email="name@domain.de" +# If given, this string indicates an email-address where the received faxes +# and voice calls will be sent to. If it is empty, the recorded calls and +# faxes will be sent to the user on the current system. It's also used to +# send status reports for sent fax jobs to. If you don't want to get emails, +# see the "action" option below +# +# fax_action="" +# Here you can define what action will be taken when a fax is received. +# Currently, three possible actions are supported: +# +# MailAndSave - the received call will be mailed to the given address (see +# "email" above) and saved to the user_dir. +# SaveOnly - the fax will be only saved to the user_dir +# +# Here's an example of a valid user configuration for "gernot" - just remove +# the leading #-signs and edit it: +# +# [gernot] +# fax_numbers="11,12" +# fax_stationID="+49 89 123456" +# fax_headline="Gernot Hillier - sent by CapiSuite" +# fax_email="" # sent to gernot@localhost +# fax_action="MailAndSave" + diff --git a/scripts/idle.py b/scripts/idle.py new file mode 100644 index 0000000..ee748d0 --- /dev/null +++ b/scripts/idle.py @@ -0,0 +1,187 @@ +# idle.py - default script for capisuite +# --------------------------------------------- +# copyright : (C) 2002 by Gernot Hillier +# email : gernot@hillier.de +# version : $Revision: 1.1 $ +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# + +import os,re,time,pwd,fcntl +# capisuite stuff +import capisuite,cs_helpers + +def idle(capi): + config=cs_helpers.readConfig() + done=config.get('GLOBAL','spool_dir')+"done/" + failed=config.get('GLOBAL','spool_dir')+"failed/" + if (not os.access(done,os.W_OK) or not os.access(failed,os.W_OK)): + raise "Can't read/write to the necessary spool dirs" + + userlist=config.sections() + userlist.remove('GLOBAL') + + for user in userlist: # search in all user-specified sendq's + userdata=pwd.getpwnam(user) + if (not config.has_option(user,"fax_numbers")): + continue + + udir=config.get("GLOBAL","fax_user_dir")+user+"/" + sendq=udir+"sendq/" + if (not os.access(udir,os.F_OK)): + os.mkdir(udir) + os.chown(udir,userdata[2],userdata[3]) + if (not os.access(sendq,os.F_OK)): + os.mkdir(sendq) + os.chown(sendq,userdata[2],userdata[3]) + + files=os.listdir(sendq) + files=filter (lambda s: re.match("fax-.*\.txt",s),files) + + for job in files: + job_fax=job[:-3]+"sff" + real_user_c=os.stat(sendq+job).st_uid + real_user_j=os.stat(sendq+job_fax).st_uid + if (real_user_j!=pwd.getpwnam(user)[2] or real_user_c!=pwd.getpwnam(user)[2]): + capisuite.error("job "+sendq+job_fax+" seems to be manipulated (wrong uid)! Ignoring...") + fcntl.lockf(lockfile,fcntl.LOCK_UN) + lockfile.close() + os.unlink(sendq+job[:-3]+"lock") + continue + + lockfile=open(sendq+job[:-3]+"lock","w") + # read directory contents + fcntl.lockf(lockfile,fcntl.LOCK_EX) # lock so that it isn't deleted while sending + + if (not os.access(sendq+job,os.W_OK)): # perhaps it was cancelled? + fcntl.lockf(lockfile,fcntl.LOCK_UN) + lockfile.close() + os.unlink(sendq+job[:-3]+"lock") + continue + + control=cs_helpers.readConfig(sendq+job) + starttime=time.mktime(time.strptime(control.get("GLOBAL","starttime"))) + if (starttime>time.time()): + fcntl.lockf(lockfile,fcntl.LOCK_UN) + lockfile.close() + os.unlink(sendq+job[:-3]+"lock") + continue + + tries=control.getint("GLOBAL","tries") + dialstring=control.get("GLOBAL","dialstring") + mailaddress=cs_helpers.getOption(config,user,"fax_email") + if (mailaddress=="" or mailaddress==None): + mailaddress=user + + capisuite.log("job "+job_fax+" from "+user+" to "+dialstring+" initiated",1) + result,resultB3 = sendfax(capi,sendq+job_fax,dialstring,user,config) + + capisuite.log("job "+job_fax+": result was %x,%x" % (result,resultB3),1) + + if (result in (0,0x3400,0x3480,0x3490) and resultB3==0): + movejob(job_fax,sendq,done,user) + capisuite.log("job "+job_fax+": finished successfully",1) + cs_helpers.sendSimpleMail(user,mailaddress,"Fax to "+dialstring+" sent successfully.", + "Your fax job to "+dialstring+" was sent successfully.\n\n" + +"Filename: "+job_fax+"\nNeeded tries: "+str(tries) + +("\nLast result: 0x%x/0x%x" % (result,resultB3)) + +"\n\nIt was moved to "+done+user+"-"+job_fax) + else: + max_tries=config.getint('GLOBAL','send_tries') + delays=config.get('GLOBAL','send_delays').split(",") + delays=map(int,delays) + if (tries=max_tries): + movejob(job_fax,sendq,failed,user) + capisuite.log("job "+job_fax+": failed finally",1) + cs_helpers.sendSimpleMail(user,mailaddress,"Fax to "+dialstring+" FAILED.", + "I'm sorry, but your fax job to "+dialstring+" failed finally.\n\n" + +"Filename: "+job_fax+"\nTries: "+str(tries) + +"\nLast result: 0x%x/0x%x" % (result,resultB3) + +"\n\nIt was moved to "+failed+user+"-"+job_fax) + + fcntl.lockf(lockfile,fcntl.LOCK_UN) + lockfile.close() + os.unlink(sendq+job[:-3]+"lock") + +def sendfax(capi,job,dialstring,user,config): + try: + outgoing_nr=cs_helpers.getOption(config,user,'outgoing_MSN') + if (outgoing_nr==""): + outgoing_nr=(config.get(user,'fax_numbers').split(','))[0] + (call,result)=capisuite.call_faxG3(capi,int(config.get('GLOBAL','send_controller')), + outgoing_nr, dialstring, int(cs_helpers.getOption(config,user,'outgoing_timeout')), + cs_helpers.getOption(config,user,'fax_stationID'), cs_helpers.getOption(config,user,'fax_headline')) + if (result!=0): + return(result,0) + capisuite.fax_send(call,job) + return(capisuite.disconnect(call)) + except capisuite.CallGoneError: + return(capisuite.disconnect(call)) + +def movejob(job,olddir,newdir,user): + os.rename(olddir+job,newdir+user+"-"+job) + os.rename(olddir+job[:-3]+"txt",newdir+user+"-"+job[:-3]+"txt") + +# +# History: +# +# $Log: idle.py,v $ +# Revision 1.1 2003/02/19 08:19:54 gernot +# Initial revision +# +# Revision 1.12 2003/02/18 09:54:22 ghillie +# - added missing lockfile deletions, corrected locking protocol +# -> fixes Bugzilla 23731 +# +# Revision 1.11 2003/02/17 16:48:43 ghillie +# - do locking, so that jobs can be deleted +# +# Revision 1.10 2003/02/10 14:50:52 ghillie +# - revert logic of outgoing_MSN: it's overriding the first number of +# fax_numbers now +# +# Revision 1.9 2003/02/05 15:59:11 ghillie +# - search for *.txt instead of *.sff so no *.sff which is currently created +# by capisuitefax will be found! +# +# Revision 1.8 2003/01/31 11:22:00 ghillie +# - use different sendq's for each user (in his user_dir). +# - use prefix user- for names in done and failed +# +# Revision 1.7 2003/01/27 21:56:46 ghillie +# - mailaddress may be not set, that's the same as "" +# - use first entry of fax_numbers as outgoing MSN if it exists +# +# Revision 1.6 2003/01/27 19:24:29 ghillie +# - updated to use new configuration files for fax & answering machine +# +# Revision 1.5 2003/01/19 12:03:27 ghillie +# - use capisuite log functions instead of stdout/stderr +# +# Revision 1.4 2003/01/17 15:09:26 ghillie +# - updated to use new configuration file capisuite-script.conf +# +# Revision 1.3 2003/01/13 16:12:00 ghillie +# - renamed from idle.pyin to idle.py as all previously processed variables +# stay in the config file and cs_helpers.pyin now +# +# Revision 1.2 2002/12/16 13:07:22 ghillie +# - finished queue processing +# +# Revision 1.1 2002/12/14 13:53:19 ghillie +# - idle.py and incoming.py are now auto-created from *.pyin +# + diff --git a/scripts/incoming.py b/scripts/incoming.py new file mode 100644 index 0000000..9b87dd9 --- /dev/null +++ b/scripts/incoming.py @@ -0,0 +1,464 @@ +# incoming.py - standard incoming script for capisuite +# ---------------------------------------------------- +# copyright : (C) 2002 by Gernot Hillier +# email : gernot@hillier.de +# version : $Revision: 1.1 $ +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# + +# general imports +import time,os,re,string,pwd +# CapiSuite imports +import capisuite,cs_helpers + +# @brief main function called by CapiSuite when an incoming call is received +# +# It will decide if this call should be accepted, with which service and for +# which user. The real call handling is done in faxIncoming and voiceIncoming. +# +# @param call reference to the call. Needed by all capisuite functions +# @param service one of SERVICE_FAXG3, SERVICE_VOICE, SERVICE_OTHER +# @param call_from string containing the number of the calling party +# @param call_to string containing the number of the called party +def callIncoming(call,service,call_from,call_to): + # read config file and search for call_to in the user sections + try: + config=cs_helpers.readConfig() + userlist=config.sections() + userlist.remove('GLOBAL') + curr_user="" + + for u in userlist: + if config.has_option(u,'voice_numbers'): + numbers=config.get(u,'voice_numbers') + if (call_to in numbers.split(',') or numbers=="*"): + if (service==capisuite.SERVICE_VOICE): + curr_user=u + curr_service=capisuite.SERVICE_VOICE + break + if (service==capisuite.SERVICE_FAXG3): + curr_user=u + curr_service=capisuite.SERVICE_FAXG3 + break + + if config.has_option(u,'fax_numbers'): + numbers=config.get(u,'fax_numbers') + if (call_to in numbers.split(',') or numbers=="*"): + if (service in (capisuite.SERVICE_FAXG3,capisuite.SERVICE_VOICE)): + curr_user=u + curr_service=capisuite.SERVICE_FAXG3 + break + + except IOError,e: + capisuite.error("Error occured during config file reading: "+e+" Disconnecting...") + capisuite.reject(call,0x34A9) + return + # setuid to the user and answer the call with the right service + if (curr_user==""): + capisuite.log("call from "+call_from+" to "+call_to+" ignoring",1,call) + capisuite.reject(call,1) + return + try: + try: + userdata=pwd.getpwnam(curr_user) + if (curr_service==capisuite.SERVICE_VOICE): + udir=config.get("GLOBAL","voice_user_dir")+curr_user+"/" + elif (curr_service==capisuite.SERVICE_FAXG3): + udir=config.get("GLOBAL","fax_user_dir")+curr_user+"/" + if (not os.access(udir,os.F_OK)): + os.mkdir(udir) + os.chown(udir,userdata[2],userdata[3]) + if (not os.access(udir+"received/",os.F_OK)): + os.mkdir(udir+"received/") + os.chown(udir+"received/",userdata[2],userdata[3]) + os.setuid(userdata[2]) + except KeyError: + capisuite.error("user "+curr_user+" is not a valid system user. Disconnecting",call) + capisuite.reject(call,0x34A9) + return + if (curr_service==capisuite.SERVICE_VOICE): + capisuite.log("call from "+call_from+" to "+call_to+" for "+curr_user+" connecting with voice",1,call) + capisuite.connect_voice(call,int(cs_helpers.getOption(config,curr_user,"voice_delay"))) + voiceIncoming(call,call_from,call_to,curr_user,config) + elif (curr_service==capisuite.SERVICE_FAXG3): + capisuite.log("call from "+call_from+" to "+call_to+" for "+curr_user+" connecting with fax",1,call) + capisuite.connect_faxG3(call,cs_helpers.getOption(config,curr_user,"fax_stationID"),cs_helpers.getOption(config,curr_user,"fax_headline"),0) + faxIncoming(call,call_from,call_to,curr_user,config) + except capisuite.CallGoneError: # catch exceptions from connect_* + (cause,causeB3)=capisuite.disconnect(call) + capisuite.log("connection lost with cause 0x%x,0x%x" % (cause,causeB3),1,call) + +# @brief called by callIncoming when an incoming fax call is received +# +# @param call reference to the call. Needed by all capisuite functions +# @param call_from string containing the number of the calling party +# @param call_to string containing the number of the called party +# @param curr_user name of the user who is responsible for this +# @param config ConfigParser instance holding the config data +def faxIncoming(call,call_from,call_to,curr_user,config): + filename=cs_helpers.uniqueName(config.get("GLOBAL","fax_user_dir")+curr_user+"/received/","fax","sff") + try: + capisuite.fax_receive(call,filename) + (cause,causeB3)=capisuite.disconnect(call) + capisuite.log("connection finished with cause 0x%x,0x%x" % (cause,causeB3),1,call) + + except capisuite.CallGoneError: # catch this here to get the cause info in the mail + (cause,causeB3)=capisuite.disconnect(call) + capisuite.log("connection lost with cause 0x%x,0x%x" % (cause,causeB3),1,call) + + if (os.access(filename,os.R_OK)): + cs_helpers.writeDescription(filename, + "call_from=\""+call_from+"\"\ncall_to=\""+call_to+"\"\ntime=\"" + +time.ctime()+"\"\ncause=\"0x%x/0x%x\"\n" % (cause,causeB3)) + + mailaddress=cs_helpers.getOption(config,curr_user,"fax_email") + if (mailaddress=="" or mailaddress==None): + mailaddress=curr_user + if (cs_helpers.getOption(config,curr_user,"fax_action").lower()=="mailandsave"): + cs_helpers.sendMIMEMail(curr_user, mailaddress, "Fax received from "+call_from+" to "+call_to, "sff", + "You got a fax from "+call_from+" to "+call_to+"\nDate: "+time.ctime()+"\n\n" + +"See attached file.\nThe original file was saved to "+filename+"\n\n", filename) + +# @brief called by callIncoming when an incoming voice call is received +# +# @param call reference to the call. Needed by all capisuite functions +# @param call_from string containing the number of the calling party +# @param call_to string containing the number of the called party +# @param curr_user name of the user who is responsible for this +# @param config ConfigParser instance holding the config data +def voiceIncoming(call,call_from,call_to,curr_user,config): + userdir=config.get("GLOBAL","voice_user_dir")+curr_user+"/" + filename=cs_helpers.uniqueName(userdir+"received/","voice","la") + try: + capisuite.enable_DTMF(call) + userannouncement=userdir+cs_helpers.getOption(config,curr_user,"announcement") + pin=cs_helpers.getOption(config,curr_user,"pin") + if (os.access(userannouncement,os.R_OK)): + capisuite.audio_send(call,userannouncement,1) + else: + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"anrufbeantworter-von.la"),1) + cs_helpers.sayNumber(call,call_to,curr_user,config) + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"bitte-nachricht.la"),1) + + if (cs_helpers.getOption(config,curr_user,"voice_action").lower()!="none"): + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la"),1) + capisuite.audio_receive(call,filename,int(cs_helpers.getOption(config,curr_user,"record_length")), int(cs_helpers.getOption(config,curr_user,"record_silence_timeout")),1) + + dtmf_list=capisuite.read_DTMF(call,0) + if (dtmf_list=="X"): + if (os.access(filename,os.R_OK)): + os.unlink(filename) + capisuite.switch_to_faxG3(call,cs_helpers.getOption(config,curr_user,"fax_stationID"),cs_helpers.getOption(config,curr_user,"fax_headline")) + faxIncoming(call,call_from,call_to,curr_user,config) + elif (dtmf_list!="" and pin!=""): + dtmf_list+=capisuite.read_DTMF(call,3) # wait 5 seconds for input + count=1 + while (count<3 and pin!=dtmf_list): # try again if input was wrong + capisuite.log("wrong PIN entered...",1,call) + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la")) + dtmf_list=capisuite.read_DTMF(call,3) + count+=1 + if (pin==dtmf_list): + if (os.access(filename,os.R_OK)): + os.unlink(filename) + capisuite.log("Starting remote inquiry...",1,call) + remoteInquiry(call,userdir,curr_user,config) + + (cause,causeB3)=capisuite.disconnect(call) + capisuite.log("connection finished with cause 0x%x,0x%x" % (cause,causeB3),1,call) + + except capisuite.CallGoneError: # catch this here to get the cause info in the mail + (cause,causeB3)=capisuite.disconnect(call) + capisuite.log("connection lost with cause 0x%x,0x%x" % (cause,causeB3),1,call) + + if (os.access(filename,os.R_OK)): + cs_helpers.writeDescription(filename, + "call_from=\""+call_from+"\"\ncall_to=\""+call_to+"\"\ntime=\"" + +time.ctime()+"\"\ncause=\"0x%x/0x%x\"\n" % (cause,causeB3)) + + mailaddress=cs_helpers.getOption(config,curr_user,"voice_email") + if (mailaddress=="" or mailaddress==None): + mailaddress=curr_user + if (cs_helpers.getOption(config,curr_user,"voice_action").lower()=="mailandsave"): + cs_helpers.sendMIMEMail(curr_user, mailaddress, "Voice call received from "+call_from+" to "+call_to, "la", + "You got a voice call from "+call_from+" to "+call_to+"\nDate: "+time.ctime()+"\n\n" + +"See attached file.\nThe original file was saved to "+filename+"\n\n", filename) + + +# @brief remote inquiry function (uses german wave snippets!) +# +# commands for remote inquiry +# delete message - 1 +# next message - 4 +# last message - 5 +# repeat current message - 6 +# +# @param call reference to the call. Needed by all capisuite functions +# @param userdir spool_dir of the current_user +# @param curr_user name of the user who is responsible for this +# @param config ConfigParser instance holding the config data +def remoteInquiry(call,userdir,curr_user,config): + import time,fcntl,errno,os + # acquire lock + lockfile=open(userdir+"received/inquiry_lock","w") + try: + try: + # read directory contents + fcntl.lockf(lockfile,fcntl.LOCK_EX | fcntl.LOCK_NB) # only one inquiry at a time! + + messages=os.listdir(userdir+"received/") + messages=filter (lambda s: re.match("voice-.*\.la",s),messages) # only use voice-* files + messages=map(lambda s: int(re.match("voice-([0-9]+)\.la",s).group(1)),messages) # filter out numbers + messages.sort() + + # read the number of the message heard last at the last inquiry + lastinquiry=-1 + if (os.access(userdir+"received/last_inquiry",os.W_OK)): + lastfile=open(userdir+"received/last_inquiry","r") + lastinquiry=int(lastfile.readline()) + lastfile.close() + print lastinquiry + + # sort out old messages + oldmessages=[] + i=0 + while (ilastinquiry): + lastinquiry=curr_msgs[i] + lastfile=open(userdir+"received/last_inquiry","w") + lastfile.write(str(curr_msgs[i])+"\n") + lastfile.close() + i+=1 + elif (cmd=="5"): + i-=1 + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"keine-weiteren-nachrichten.la")) + + except IOError,err: + if (err.errno in (errno.EACCES,errno.EAGAIN)): + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"fernabfrage-aktiv.la")) + finally: + # unlock + fcntl.lockf(lockfile,fcntl.LOCK_UN) + lockfile.close() + os.unlink(userdir+"received/inquiry_lock") + +# @brief remote inquiry: record new announcement (uses german wave snippets!) +# +# @param call reference to the call. Needed by all capisuite functions +# @param userdir spool_dir of the current_user +# @param curr_user name of the user who is responsible for this +# @param config ConfigParser instance holding the config data +def newAnnouncement(call,userdir,curr_user,config): + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"bitte-neue-ansage-komplett.la")) + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la")) + cmd="" + while (cmd!="1"): + capisuite.audio_receive(call,userdir+"announcement-tmp.la",60,3) + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-ansage-lautet.la")) + capisuite.audio_send(call,userdir+"announcement-tmp.la") + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"wenn-einverstanden-1.la")) + cmd=capisuite.read_DTMF(call,0,1) + if (cmd!="1"): + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"bitte-neue-ansage-kurz.la")) + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la")) + userannouncement=userdir+cs_helpers.getOption(config,curr_user,"announcement") + os.rename(userdir+"announcement-tmp.la",userannouncement) + capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"ansage-gespeichert.la")) + +# +# History: +# +# $Log: incoming.py,v $ +# Revision 1.1 2003/02/19 08:19:54 gernot +# Initial revision +# +# Revision 1.11 2003/02/17 11:13:43 ghillie +# - remoteinquiry supports new and old messages now +# +# Revision 1.10 2003/02/03 14:50:08 ghillie +# - fixed small typo +# +# Revision 1.9 2003/01/31 16:32:41 ghillie +# - support "*" for "all numbers" +# - automatic switch voice->fax when SI says fax +# +# Revision 1.8 2003/01/31 11:24:41 ghillie +# - wrong user handling for more than one users fixed +# - creates user_dir/user and user_dir/user/received now separately as +# idle.py can also create user_dir/user now +# +# Revision 1.7 2003/01/27 21:57:54 ghillie +# - fax_numbers and voice_numbers may not exist (no fatal error any more) +# - accept missing email option +# - fixed typo +# +# Revision 1.6 2003/01/27 19:24:29 ghillie +# - updated to use new configuration files for fax & answering machine +# +# Revision 1.5 2003/01/19 12:03:27 ghillie +# - use capisuite log functions instead of stdout/stderr +# +# Revision 1.4 2003/01/17 15:09:49 ghillie +# - cs_helpers.sendMail was renamed to sendMIMEMail +# +# Revision 1.3 2003/01/16 12:58:34 ghillie +# - changed DTMF timeout for pin to 3 seconds +# - delete recorded wave if fax or remote inquiry is recognized +# - updates in remoteInquiry: added menu for recording own announcement +# - fixed some typos +# - remoteInquiry: delete description file together with call if requested +# - new function: newAnnouncement +# +# Revision 1.2 2003/01/15 15:55:12 ghillie +# - added exception handler in callIncoming +# - faxIncoming: small typo corrected +# - voiceIncoming & remoteInquiry: updated to new config file system +# +# Revision 1.1 2003/01/13 16:12:58 ghillie +# - renamed from incoming.pyin to incoming.py as all previously processed +# variables are moved to config and cs_helpers.pyin +# +# Revision 1.4 2002/12/18 14:34:56 ghillie +# - added some informational prints +# - accept voice calls to fax nr +# +# Revision 1.3 2002/12/16 15:04:51 ghillie +# - added missing path prefix to delete routing in remote inquiry +# +# Revision 1.2 2002/12/16 13:09:25 ghillie +# - added some comments about the conf_* vars +# - added conf_wavedir +# - added support for B3 cause now returned by disconnect() +# - corrected some dir entries to work in installed system +# +# Revision 1.1 2002/12/14 13:53:18 ghillie +# - idle.py and incoming.py are now auto-created from *.pyin +# +# Revision 1.4 2002/12/11 12:58:05 ghillie +# - read return value from disconnect() +# - added disconnect() to exception handler +# +# Revision 1.3 2002/12/09 15:18:35 ghillie +# - added disconnect() in exception handler +# +# Revision 1.2 2002/12/02 21:30:42 ghillie +# fixed some minor typos +# +# Revision 1.1 2002/12/02 21:15:55 ghillie +# - moved scripts to own directory +# - added remote-connect script to repository +# +# Revision 1.20 2002/12/02 20:59:44 ghillie +# another typo :-| +# +# Revision 1.19 2002/12/02 20:54:07 ghillie +# fixed small typo +# +# Revision 1.18 2002/12/02 16:51:32 ghillie +# nearly complete new script, supports answering machine, fax receiving and remote inquiry now +# +# Revision 1.17 2002/11/29 16:28:43 ghillie +# - updated syntax (connect_telephony -> connect_voice) +# +# Revision 1.16 2002/11/29 11:09:04 ghillie +# renamed CapiCom to CapiSuite (name conflict with MS crypto API :-( ) +# +# Revision 1.15 2002/11/25 11:43:43 ghillie +# updated to new syntax +# +# Revision 1.14 2002/11/23 16:16:17 ghillie +# moved switch2fax after audio_receive() +# +# Revision 1.13 2002/11/22 15:48:58 ghillie +# renamed pcallcontrol module to capicom +# +# Revision 1.12 2002/11/22 15:02:39 ghillie +# - added automatic switch between speech and fax +# - some comments added +# +# Revision 1.11 2002/11/19 15:57:18 ghillie +# - Added missing throw() declarations +# - phew. Added error handling. All exceptions are caught now. +# +# Revision 1.10 2002/11/18 12:32:36 ghillie +# - callIncoming lives now in __main__, not necessarily in pcallcontrol any more +# - added some comments and header +# diff --git a/scripts/remote-connect.py b/scripts/remote-connect.py new file mode 100644 index 0000000..7c81327 --- /dev/null +++ b/scripts/remote-connect.py @@ -0,0 +1,59 @@ +import string,os,time +import pcallcontrol +cc=pcallcontrol + +def callWaiting(CIP,callingParty,calledParty): + if (calledParty=="23"): + print "nehme Anruf von",callingParty,"an",calledParty,"an." + cc.connect(16) # 16 = telephony + else: + print "nehme Anruf von",callingParty,"an",calledParty,"nicht an." + cc.reject(2) # 2 = normal call clearing + +def callConnected(): + try: + cc.enableDTMF() + cc.audio_send("send.la") + code=cc.getDTMF() + print "Bekommen habe ich",code + if code=="3008": + # establish connection + start_t=time.time() + os.spawnl(os.P_NOWAIT,"ping","ping","-w","1","www.hillier.de") + c_status="" + # wait 30 secs for connect + while ((c_status!="CONNECTED") and (time.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 Cm~AEi literal 0 HcmV?d00001 diff --git a/scripts/waves/.cvsignore b/scripts/waves/.cvsignore new file mode 100644 index 0000000..282522d --- /dev/null +++ b/scripts/waves/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/scripts/waves/0.la b/scripts/waves/0.la new file mode 100644 index 0000000000000000000000000000000000000000..d11dc35bd0b8171f9dd227a7c084f8aa67523141 GIT binary patch literal 4800 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/1.la b/scripts/waves/1.la new file mode 100644 index 0000000000000000000000000000000000000000..717192f09acd49cdc9281e65bda12ba0f398bf16 GIT binary patch literal 3599 zcmWkw4@eu?x_`F?+s;73vtTakK43g}N^R?NdnXIgs!a^mYdHp`hIvNO%?6w+3{Hp!N(T_bP0=5t{dz|H(Pu z_x--#@AsWdObKV-4Gy0+)E^DK7|PT6jcmH?`qb`PJbgPkxSajhfBWUy^6k&x{_QK< zr*D3Ko%I!edNo*WfAgPzefXjK<;LjFaq#sIZ)T4t2kY*td|M$#){qKMLKGgEl?LYtR_LJSIhY#D^-v9JD6{${t_uZQp+KO%M znghwCWd3-Oms7ac$*wUREEhgjD&3JxR-}CjPAGm~_AK#v)9{%&czne>3 z|MjbX{CbKHr+;{QGI@IaezRxp_wRm~KIv(BIs7~(m4}|*SYLXwd}n$2eka~{G|`pN ze8aOV+ zJSBhn_aCO`qR+qo>R-P8@o{o``ok1oX?XegUH|&)8xN=Fjy86dms3sYPao&1{Ql|b zo#nw5^DrGYJDY2oV}1FBlezVtg5R%B6jLpk_s?(r>6f2x+@iy;etq@F&%cFV&&_?> z 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/12.la b/scripts/waves/12.la new file mode 100644 index 0000000000000000000000000000000000000000..152a1762f32a3a80aeb2baf09f75b70579c0aec9 GIT binary patch literal 3599 zcmWlc2}m5t_s63FGt>P;b3oS2fbjqwtFoF+z+(ntk_C@$h`)6qF$%j;L1(J-)5JiG zGm!OwIClTpT;MSSQ6t2$D|f(*JrJV-k8X&Y1UyFAcz{PWtD6mY%z*jV(lqo@@71gK zeXsbGF2j8>aBIFD>Yn%*Q!A%^q5S541 z1sSV4K?0O?z!D@O0n56GA>mn0=5#%X0hD9`V^$X-76}7^s562@SQ!g40o|R>UMXbq4 z26P}GP?j}a(jX)dD}<;r(MgFSLqvZQ%^L_ywlL2!Dgy&Dkp$WKvijUVy|e+SIPCLH?K%U`R<_z5t^MtJd41QwsmhFj%7KC9H!n^6k1liXyZh$eKuEEc z7gfJB;f;-&s?4p-eSJCB+UV;(gTbADejqtqZ%f|STHj-IbA{rYL28D(zk01Z>_-#Y}blHcWo_v4z#X^ej?cc}moNl;r zVmjjL)w{#9nY;eySMFTd6B~8tyNeMKjTZ-B!w2>o@t5M`{awQ!4*c|GxGZ(~&gK96 z&*NjKV|_he{`bEpe|<@q$mrgG?8^Rz{5Iq8lfORP?b%u{K7Q=j{^Q+I=>-XAUNj~+ za@KwJ?e5}Y!|2mj*>v)|W!d-b{$u~SeEIbAti3HSKK%9lix)@6xY)P*_cvT}7djfR ze*dJgGv|E#^E>~&bII_aDm!7JY+-N(CR@h+^xAYD%Go*aC(Elm$xQf960c$Aocf-_&b09{dT-9y}v%8C!xOXx41UD-<@x0 z+kJQ6c=y0?W9J2rJ?oFl`}faxTdLZJ?;g0ikYEhl+TQd0ZTI%|{@IATKMdE8>9KoG zAK%k39%pWyot@no{LVWvmG95D=eq}<8m~T~I`{0e6#w_~<2!HP`pvCpKKyhwB4RdX zv-|Y%zZ;6}DGO(Q`0`?*Kjfxv-!^m~Hc!{jUOduS@GWiBp5L)O?sJYgI?K*99!WhE zl{J6*&hhz<^CM$B3BzX^dnWSkQ7z^>rv5Bhndt1SH?C;W*Yjhu43=BJyMFDbg}$}# z(}UIGc>7Sn&fvmu<3gWh{`8(Jf9yX!E*X2uzC3wywI?&^?*4a4=TCj3pL*(33(E(_ zXX1;q>`~E^{S!Nb#?;p}X`6?qu2-JBRyCbEz18wrxMdm7%JLg5qhepXW1?U%do6Fe zYJK_EsL#IX%TRe16^XYfP!SZ1;h& zuAftk^#%Q|^_!!s#SQZ}^7HM>laBh?&Ys5Mp0&JR3wPby9a-c4ZFgljrs7%VTI$$P z%rmmS+~%8#`gpQxdTnAS+n9OsW$r4?czpM^JNIQwYENThS!3Bn$DvbG`7^XBwpT|_ z?X7DywmSMVZ=dtI4<{c~)Go*1VE^YXZ}e10e0S^(zk5@x+v({kqmA2I6@D<~FYcc4 zyH__uDVcS3)2Z!+Be@yTPPnv`;%Hslm&B*#+B>>usGX&<%eXU34HtH%Cj+N^+cP)D z-EQB_3cAmY<*i(+-W4{6KBsqGNSz)kPfO1D*UUfe-ZWom&Aynhkg(&Zn)^6v_vgDU zmbP8=tShDO+pADwV6w08_R+e`mS@qAa;H|eV{hc!2b?*X1+zUp7dt2N(u2!ByTNU@ zjJ}C%{+!Y`sX4{MeO15RZmAoqDXQG`xwB@9I}FQeQeMGqLc+x(6BnvTW_4G?qnCVe_DTZtIfW;nOq%IW!;)o zmA9`dWnW&(yQia5mhq0_ZOc?!bT3spY*D_wZWa8oq`K}8{T`%15wL=zAs8FACZPuQu_Li=G26%Gu-3$ zReFz(Nbd^wr5Yz_kJaTB3QdtO)6&j9jI2GO?2U<;{95{M&a~{aT+di_`*P!|i*QX# z|62crR9aTw3S)k>M!tOc^xW=)Q<2{L0deX6+~S+$(TwX;meupO-aJ(%GcTk%CMF#H zbybrm`Nj29yQdyiJ~~JU5yDAk&-+JFxz~Lb-$3$vrlxAE-(fVi_Sd!i_EmoQem5>I z?&j}BC88if7<~HXm*vrcfzjo-G-YmbWvuSI_PW<&E0aHaq329z2^?T{}{; z=)r;BxzhXT8#$AQ3UALXRUnn&LQRPePZT94K15aw78MuAssDJ@lB6c76qTy3`+E;9 zmG<^Rz;G1hoWv8Pgz(TAibNi(ml6$c(|ebkx;K#*7jZDadd)D&1)-+#7&1*(hG8Be zV3Ht`5=ki=wl%AZb3l?!lEf>VLcu|!uyV4L^>}QFs#snkNeZ-?16WjK${3ugODZGi zGA^a4$de$)${{GRA>>k(CC$m{b52o{0}7`zypFL|3s6X+ghYu`P=H4bV@covFjLrq zIu}r^vdN}kfh>taDTII%oKBt4Fr2!C&0d+2FaxYA_GnO=(*=gQq&TG^gk%^75U32% zh?kO8i28&c0Z6J2OuD{TQ7d$v6N4o(mN3K;!n{plLLPuC1k8&XlvyBS4vP$6C5{k@ z(hs1i1}2ZDX+X2_0o?_ynoMM5HmNpEV<`BkydGjitSHEXF?KPm#`1_}ju%WJRz@5Y z^spoNhy$TZ4ISwG8K{LU;hXxBth;kaHD>m~s2nhA*)Dh94 zWJQp(@iJyG0uV|HeH4OJ9SH{*K?F3blsR9fFxFP0Zaf;C6R%`oQ@eKEGvqDm{KGLR83U9 zqNK8bXeE@z=Kv& z|9Rv&#H%5e)-B>i0rHTe<%D&psu~b%NT-`7QIXB0V-R5~rAQlT~QH|g1piE&N4v>CsTgzy0Uxd<=EFowZ1g9x3jnz^- zM4%Bf=3@yhEen$o%BNwpU2>g|iolGCq)FM-7U1I}@kkZVN2W)-q<&lBwb?m&3?FT0 zFhryC;l4NTx}Cb^l~B*Ja{GBk{HtJ2#~#wNeyitnnleh!=DT_|39a|^&wlvx*Z0E7Im?^ZHbRr<>8_6W47_Bw zD$uEF?tI<|D~Sv7mFZb6-Q7@Ji8_n~%sxrEUF|wut*P;*XC&1YKmF?3N^}0r;j2IY z{9y1#&i?q*&GyBDmh#36ZvA@5uxNZXv}2!qrxP`E$t|8KzLz zD0`HxC$kU6CO3clar2WZ=JpR?{%|gSqQXffE4^CTkLn1;Lcj?>P4 zSzGW>QoZw0p>$2&h;?SkChCinaXm^YTs`~Zf%1z#{x+Ua5I8=g7U$BasMMlji0 zqKfl&>@>FJbR5*|=H~8FJHy}qqwl-le*AWGz4YsU`{$pB)8;S#?JrMD;?KRE&1=$b zUb%9gAIvB&J(hjioVzvcJ|4b(EVfiSo)HzuT<-P-XY>E~zkkWEE)J&t{EyL%sLPk@ z?iliFU+ug$AC-l63*t*>U-dp3S_v7X{+-#<*qh6_p^JgT)NfvY_hb9hucGdL{pJ6B z`QqnuB|m-<74^8UA?f?t?&Jc)=0e>H3omEhYg#lqi<56KvAs(bJ@{75@^o@#eC7YXij-+cM?H$UWE-24>v?ZSq2aI7u(;h0Fc7#hEPrz~YjvXk^7 zZziLdEd*PwVc4+HG$4 zY|?OVj>M=|l{F9LmZZJA;12e>V_P!4hPXm@;^p(!9_7m8OXCT*yy;D;wO7xb8!L@H zJ=)leTBsW@3z-`Sd-HD3_8y+#PjW-+o{K$+=bbr~8NtJy%y5|6S1RUJ&8mdCmGvj` zmde{>A8Kn;U**Uu`ZmTlqoOXYcw$a6@@^+3<*ijCDeE4U=V;j|D^Krs_r}LxC?9EG zdAzxK`Ej3NGsie& ze(;WH>UDQayOt9!-7o7??S_k+W|PKJZ|B7iN@8-$Htt-m8-H9jWPG_4EIl5+_bS-d zfzJ1|uisjaD@=PXeQAop;F_N_+cK-WmzI{glkdW<()WgCgYx~nx`E=u zmPhB`+_-VqqSuf-EeI4WpmbQ{h>>KimX9Wq6Z+N|OfemU1$ctNA)gn8N3>pZ0As+O zz{)Jq^;>!yEfD4^g~BnSr5K(^cpb}{{AqfB*ylrF%p8!LBN2|Gz-EEa%-IDM2!>`5 z62~D3!g(I2VG2bl0XPTS0*wGA@Z-S`M;HQNM9gMRpn2A23hR74!w4|O!2(a}G+_;d z(*y-`7={7zVB4ZaCp3@q$eA+{4X~mahCxw+03Hw;$WT-`J7}Ktu^NqrWGE0c24nhcu5_d)hpaMP&O2FXYU|66L67oSlpAH-ljtB(BVP5k{1mh?a0gxse z;L1XPKIYX%yd1@xapGwda2HSxhe+0^*8mp?MPa~Ji%|jsn9_hh0@BNq5bywI1qIQ; zFhK|?$O?~xxIln3h?5ax0RjYI5`Yv%2@C?vAd<%c)z1eF)PW}Gf&|cFB1CJ@6$GF_ z^B_@R0)QA`P+<25xIiL00T!Yg1`rpcrvWL^k`V~e6tvHrlfXY15K}9N!hb1=s=x;H zaG54?QZVbb{F!!pSP%k^hK7a+Oc4m{)3IR<8|D!PCO83xd4}Q<9>FM*7JQ5kN9xhU zX|LC((S2l18Vc2FmVO42p9@e4CP}umPAR6@gWFC zGXg|I2&bb_4S|vz4SU6zU5RW^|}4QmKSM}Zat=7Z5eJVUZPOcGu}ixU`0 zBR+^hd^m+5UIBy)PMk(@2qiSsVgQ3!#&6QuKq3ivWCZmJI7i@s1JBD?9ETA~D{wR# zVQ`WcWV{w4Xc{)rS`C6>aubB%5KF-jO>>-&rU@O6(wqh(NDLK7#LKfdFAxy$EZD#k z22);Ar= zqc|I;I3ER(2&*7$woFoa%5*_U{cB|JV?Ud%C%lS9N0BsJ$92gd-<05Iq3aD>kizcp zp*ES<#A~P+Zv%dvqs)3Paf%XTW*fp1Rcwq&cfjgxM3qKC@%v2px*oNsy;My4QLXQ$ z$(B8ZF%YeR>m4SnRliNLa)_fW8VbBQq6xW=@?%<^&kUlimy>C%U>|U34on&{M(dAm zFk93*y`lv{wu}?(Hbb*rwDzTL-|Mw^!8T}0Yl29vSF`9f$*KrHd>e3+rvI?WhyvFXoAya456`c){k6Tj(>948iH*og0)M~6S9taqn_A%2E zJ%tl+WWYkLDhSwr5`DefZ}47pdn`8{044fB4l`*B&mE?X8vGd+;K) zw<%tC(bL}S`9(NbeV^$Iwa=$Cx6kZkB)zSzO)9+|8&Rsp7dEdf+?g9HS_>X0-8&8* zKDW4fcAr(1ODuNlwt6l!E7G6IAvag~5<}wC?Gd+)KMKTbegK_Nr+uJ#cH~@kUf#S(&P^F=r>fG=8ux=LT)v zcdat@M4UGInP<5(G-QEgjey1R+u;i{OK(cX|3BI#I9`yf<4~I$N1#kuT3R-^$N_R#dY+6WcFw`sdYZsml@8cQ}{aZND(p zpT;|f@-Ge^N|I8OGJ^3FMwct$zUNlxnFjyZ*xfjxcUYBi^G~b|S)&CxL3fO%`~LWl zDt~=xunlA^zA1ima@UoRe@mrO!l=aAagxPJrOIdTD-sZr@_^;+!J#^R_A0~I2<+i^yPyhX}j88dYn3Z+$F@pk-XGa>0UeRzJbUq-k&OWSBsCKR@qs%~wb#)rpBk2RHZkt*h2w7`ug8v=*Kg4rs%B)v;}W zOvYMwzo}uq^Hk|NEv%T_SKpj$os?EctHWgw zWTCR=^!>bXbzhx$U_31>>?|~Ph4-e2s-o3Zb+#OuNRAx@+G{znEezaMF+X49dQxb$ zuHM}5Oi8J6ol1+s6I(o5HF>i^L2UIq0hOTpaK>rz2?ICP6{}BDq!oqM{rM*~N~PcM;i~_L3hqf`lBs264R=dcBT{p!p4fu8kbV}zG9m3GiE_PU`81I zUPmJ++}Cn)(lcNBX?b?ye&H!#vpV1T9_T7-Dkd?Osq$(E7S7BN7l{0p-HU0Eg=#!jxt| zU!I)a9h-AFvlN>?TF?@W%hmH>DfKKb_kiWH3o>^Q;kUBv8q?PG^RGLcjY~5#IXT6R z@5&!(ya&>np5>C<+>+(EU4Uww_VTu^v@Of^yA!WR0c`QiOfi7%2dJ&m8pCqQvu7o7 zfa~e1deKYzqt8$5wY2oVE=YDd)6>(P)hCkkb_m-ZFrI?vv8TtNtm&MWs;A`wYmYvk z33P$-^OKW;f`an?i7Y$IM7F0})vJY_Des?j7D|okfyFA0(fKWRZ)EQEw*Z5^z08R> zA79!{Is`Tc8j1!c)mEz%X#R505Qtzf?ql`!*YBQx^9GzB!MB9p#QI23oKoPcLD5x| z-8wLEFx}-a!vulgJgBO}Hod*xVzH#@WnmqJ^Ei(nppu0-?TCEDq1aXg;T^yzyyLo&5h$cPm855N&_+i zL&GB+_>!S%L@ZBf98l!N0hB=*8bLq>jDp5lm5hjT0R??T)TV(HD1kw62SA^d&RmL3 zao}w5Y+O`CMagFaU?#vYELxx<1D+%Se^6cmKLU8kP~a+n zF%*CST|`U>FNOtR04C2Pz%v*X2vnd1agP^3n;`%z5F}8=A(8~y6`g}Dfv8Z50KgPM zoWX$+5TY0*fCdRWQv_N9!2Em_1P}`Xz`z&-W(3ecMJgb(XE6d#fPzQ}NI~QC1wCt^ zpbyBNX^3(VH*h3!7DM{13M|it#90E~#kL3r{?5eU4zvMnU z(6kC#XTGn#r=T{0swo(q*=JrW_$PtdDyW?S-L+tCgxZauW@cAgh1N|#zcadLCdr+1 z&-tC-oqNao8%c)e5LpfyBvj`2{gPyoF`i*L0q405$wQ+7oUd|NQOV_UL^#agMpzlA z13ZhdNGwBCsT~rL%cCx{JIX)DFux^4l7S6B77uBS0_qmZg% z46dMz$vnwga1>Ri@UbQ?Wrh(8F}gb-(jsx1Y%rc7L{-}%J#}SxQIyM2waN{-J|7zJ`S^6Rwfg)O-m-M*%eAbxI`6H`%%+3n z4;syx^Fy7z?*{KYc<>>UXmQ?<40 zOFP3Ql_h7cy8EXdyo!l=@L2Vz>D0GB{`lj^`vcWS7h?YXpRd09_p1xNS01jOy8rFV z56Km6-TAds1yfnq`~8cJD>V-f4kou#!?o|8{ru_Go!7-zZ-4#gAOH8)KfmnLULXAT ze}4PNZ-2XUt!g;=+m9c=J~i<0NoiKWVA{K>ku81O_LHrOyva@y3qpa{qLdUi6rM7SUIYHpq?s2!ONzVBHnIaBlbW;W5FyS3GSDHq3MV@I>I z@7-*BBpmh5XVqrCp0MY%H7)?lgEMpQTb<#lf(yStF6e0AsB0W9DOnh9JI9MoZ@aBC zj-n%b*}%SfsH}WqVx%K{ZNw&QHkOnORIVOW46W^EbuR3C6@;VG|Ai5(Zr;1bJ{Nt1`FPn-mEwn z=sS2ApKWs5@{8x4rFNRDyLh#yX1K0%zBZxbLUYIIoE6B_e9$yi%oOEczcv{2EM_pv z`2EUcrKYmv^C*@(eY$rfU)R}6<=k6Yn5;;5Uat+qL?x`hU%0#qH3tqpciHaNzKi+v z&8ye9%6b-(HJYZHO|zl1J%1$Yu5D>8=U&g5$>GC{>$SBBuM3)QgQ06zijd1&7q4g>PAC;QWgy&D*4svEzkG%3at?awamWpl_z$O zRne5-3eC(E&RAUtMg`m%RU1`Rt*uoV7PkSFF~%PZ74CJNTQ2L$2}J~+aJS~>7PV$r zq9Tqsf*y}q9YJ`}ZHXJpoy}N_21E)&{0^5oX!e+0E;VdsdOss^RA3FZ3rh+eB`98E z^bWOItrs|!fPz(P0g5LPQ^etN==~-{!f=YEWZ2IniQ{E45HJJ?*bOm>VHiX(!NHK6 zOo@buq9Uq9f?E&}gu!@L6v4r99DI1?;Bj0zIgDW>Ou{f;#&H>!;bbWkmBH`VLsfy1 zcoqdj7AkSD4RVmg<1(d0AOH;>84_5Q0z4T?!1j7vRRTZIRWJb>YAH$Z3v>mbiU?NhE_X3K$REFdn`+g%!u~I4`p(OHnvSGGK^+NIXuVFiIe$uwY0q35EuS zNsEIs{ib3k%ACkq*5>Z;~jEro;elphN%>KtA~+dI_h1 z0MNxLj6=kt0FJT(;y0_)1xykHZnwdb0^{U(y#T>}o+MEg=O~uvB@Sm`mN`2 z7r>;a9A{z$M~WYp9K0mcDQl#O@JDbLZi>Ti3$diLc4eiOW$Jw&p=2o{Z;AE>6f~5VgpD!>pO&T&+@U zBw!$U)`@#J~w-jLTwpN?Hvb zS5=UUZ~>&1R|dqghzmp@hKRJGibvM)s9w!J7ek|Krl*MXMvOA)DYCWUESo{@Ti*zT zEc(lc*bP^U!HmO9a2o~^mWD;&LXo&=h`Qc*1VdLw#2RFMLZQKI$VE}I+gdOAizqXO ziWVs-MN?d6&df``gp8<33fIS)>0Ekaw_oSA*>3F$_q2I656{$` zkI$x^I@_0#-ia?0I`n*U=DfSQ_RgIf*QPqwAHKYO>ZiZ_^ZvkRSHgoIKK=aFH)(I< zdYbNktLf{j3C;J8Oq8D9I(j=Xo$xyAmc4XtJo&@z+qYj{F3FCoO-uXq-FJh{^M@-< z$pe=cdPd(&T+jDmyRNY>-4h8NNyS;Twx@F7)cu#Y`;yPvuRVVC!#AG>FSNTJ9yHw^ zXlm4=^CJnp-80-ec5lCSWVbgw5_Tc@LiYtd}fp8!@$D*%(35iK zHs4NU>&qio<`cZ_;e@Ku`t36s&1&*MlV^W-1f=1E$G?wkw9RGqomt6iLrT39TiveB zdq)3e*3_-ygaonWMNP>E&Fba8;Zoz>+QAFg67Ck~3{?+Zji&-d{q45Y*+uQ}wzQ)y zcsw8OcsmrI*|czbvZie_(lHS(em&tcX1nti_tZF>ue0fF&WNkJt?^8?ckJme1^FFY zaSIPCS4(aWU)*&i6|~=-@1=;v!r(YIHeGHzt=qC+q7NG@R_5sPt+dCr3B`C#$?8C* zW;_1HSpK!%-lT~){!sm>R!`^pbYHe?TTA=r;`4fj4?F3sw1TWGMS`qpzHDEZm$ zrTNnS)5h5-Gt{`UnYg68mX?ro*LtO7wQ{v-pyvv|uLE7P-+qaYq^IEJ-8P$Dr!!U| zWz~6~7aP1u1qkeHxDbYxcP`nyHl5MpIsc9}+k7;do?>zPcJ0&COU5PD(UEaox4v$)ClaxvGiEhggkpHk;=Q)K z#-8!`>MNu02XpPsrBVn^J#spY>t3%yC6Ne@cBMxILi9&w7H_uYeZCqWKeW>o0<9M< z+AUpj0;u*7}{pO^BVl8m~R9+w&lFx$4N7s(@~7*=)M?BYP1Q>dMK<3A$#w zMi-xGtK+YZU#%W`F&ZM>zEbD9cWX=M)p_A|z$-xmO*&kj!kO5;(Yo&sZ)!K&=Gr!I z9zN<)%Nx7X{a$!(V7++PoyH}5Vg?)Wn5}!yb4GU_y*LEfz1dc+egC5Fxj(RPH?D8B zgDHcXwqI(9@Z8N7bgon3tK;>Gu8 zv%6Yc4CRjN_U)>U`knnwV`- zF~Jcuhhk$}tf3GDIzZM({GeqiG+==ErxuH4Eye9lNudc5_A^EwiMSk&2)rklO!^3D zalgQTq65iHN25_t%_t!T2ozKjPf84eASSfC zOTmeA5+(tbviHKv1P4M9i~|+R!w2UTxvPjN8H_p>r{GaeB?fdVd@F(r=P3}G63>td z1`g=5k`D-=^}sL;PQ`vbm!zzgYK`A&uQAq^{4y8zj z5fISdCt=VH)u9d16sTDFk8+CZBm+|nE+HAnLM;x+D4j|-{Bp{WIcUqu6}TZ7T#x~N zrOh8#f-A*&(pJJwdT)G0f`A57ga6@xp&cjlJV2fBhy3GSB?i)Y0D($4AS6J6?Nuh3yHLBta5kFNFg%$i+yqW)W0KoI7(>kM zbi##)Fd8ppv@mQJWCM@bq>#(#LRmpT1PNma0ig*a5h4hdr3HyZ7=o1~0hLIJL-OMxo=^isrN>~k9PiI^P}R?-$y@}NkzSH{{Hjd zzmoWTT|0GQ9&-{NrAr9<3GL>yeYZ=>Di+T7jFLZ zAO93zyq(|j?biSN`CCb^db;`Hh1WebLUQU&!ONz-?1`GU#h+i4m7S#3`Q@KZWg{Bw zefQx*!TooRCkL;KNB{eun^!+BKJP90`|n#NE%{t`^XsqoCQJ=0kH39u+0f+ZCX0)U z-}+}C1U9DH+f9c?@5uAs@*5>D-(_AkKDsyh`_0j_p=%veU!ML^@};-QyZrX*b${zl zM|;8j52yX&=E=eJ7tIG(X0MTR4ku$qoz_b?${&}sl=trry?;?Wdf{`Af3>al-OI;6 zJ}fi^r?2=YD!2xdYGJ`=m$jg*%GW!5wuxp4`yan}U)=Mzvnf{_jBf-xb%*dsowJ5-rcsctCRXqz2#HA z?M*&xb-iqIvZu$toRjNLb!?~{Du>>&q1Pu(d4n=URkgOxckg%S?7!O8b$8c!)JaGC z!ov53Q)`(0*;saPQ0Biow4TcyhxN&g6^E*}X=BCb>_6t9quignIw09Se{DZ!xw{YZ zS%=FPrluBFd~H?xAA2Sz56Z68=%^*NPj6MJR1V;BGSi<5cG*LRn{~VM-8nh?-OEGE z^ND(3^i%nz^5Nu`l$+!4=_$KATeBW;SL&1cjg^%Rhc6yC1=lbJfwHzMR_mV3>*l+c zPnNqArV&T}aCz@SQ@lE>b=_Z9<{w&L-@om4`jTLQRyM5qqD+5-nf2nc+uW?H z+x$7dudA?|^cyPG>98X_0|XmCo(nz*2u}Sn=CPy;d-@sAk+lPmJeTUnDiU9a+ zo!`G!8msgrlN+^;BDJNnb-r=-#hogbAt4s!pPFE9CpH#=#E>|c8n+9U zJ@HxH>U7<{uCA^%f^vJ+d6f-bEr=i3UYES574733&C;A5mIZl`gk?>-D$BDSiQKM= zA~sE$B6$On#=8nFv1}4+knI@eMu2vL@`Tb1%?P4E3Ls{M+;#-TNXYCWJW$BM^B9O* zY>X^q7&k33D1J=s6-KjdP&!JP8MB6ljf6p#s2PaFB#v~81V_@PHZ}{@7$A{`L{VTk z4nw_mTrzMP*v;S|^mT$b84{^9$qJ-|+i?u0_b8U6X&!NDc%w*ZvP4Nk6C^J0;cK#& zG)hK1DpEFXEJWdQLcmzk#siNznT049A+anki4bC@A6x#KHu{QZR3m5ST^x2o@2Ahzv(D24^_7l-KYg0%I_N zaHv_bb0SW<>?9qKrKk}pr0E7m zk`azgOEhDPx+%e}VIiZPH!@mFryLb60~Qe(PjtCNCcSpYOCXphTZFXss2ruz=3pno zVlt17NgMfC60|_J(qHzd8QHsH7aK=%DKna+jSc+y4f)YrP;Uols zIG&dYf@fd|$Bm4LN=67pahQ-;QKE4^B&K;$3h^?Iae^$NFw3MN!d1%TVc58fuDVR! zkvbs`DP)$k+>+p?35LLm?a`@KoeP_mVi>c1zJojT8uF_ zS1qG79iog8nRj^zwFWVVw6UEC&u~nF7=vjfO_1CUX25Zh8z5bb)MrT`U6=)G5Gb6d zvs}E!?r}rJpyqM@fhqp zM2D8fqc)0QSRNB8!GKBxC#4}8MoB^Bxln=V(m-_1R0i0!z>04XNZ$fg5}I1G$C+N#7#ml%F}L< zl+A=7AqIq}AaEXJ0Y*^RfQl#xvI!iQ5SN>VD3Hg4gAMj5DH5nM3sxjy5Gq89NBIOx zG9-Zr7*3KL1M+;7A&jgW92;5&`wJyOzz3U~Kp7xR5+Fn{6fKE}q{Iza0`n}yaF!Kt z-bJwpZ!j`a2$E1J4N>3@gBVvS#SntltHrk;^r1Ueefy0M-yN#K-LlX3zJ7gJ++4$q zTzd2Lx2Jy;OpQES?|Je1(Zz$4SJOHq+5Ymzhd-V^em-2Z{Qknt|NZ@+-#@>7V0!oT z)}R0WuYVP^=jFWqdg0-{7d-*1^;2(0xG1wRJo2gKQ`1(mb^YM;y$cU7T)kcvvi>Oe z`?p)SzDzmf$+JiI&fedhi1~cA;Xsz1^jXy{KYABFm5V={ufG24!p+yO4_H;pn}0q1 z&szn(P3Yv?v(GOkhvs9Zjup65$jXXa9WNiZ^e&vv&t82vdUN#a`!a9&yEosSe!0KE z1RBfUUs?WH9j`4fSD6mqANb$z!uo)s2$p|<5i$&DNNbL4K$+2yLJsDEDj zynSV)%0CIxS$ySX$&dTJrseBrqoboQ-uq?!)I!VD!tgP6xVheXyEK?QJzc3y1~1Q+ z9emvHuPrFKQP7LeHebE)@axrsA$PL(`}gm|O(>?>ts5|68LO&Rr3y>kJ^rCas`k`qN=+NA}|E!O8lVv$o*Phj-=K>&<6hUw?h! z&$-j~-I0H4^-1s%G3|2JC#|Q$8})nU6aUFjRNYkYxcAbEd~)&Z_0{6*cOMLlv|ATC ztVM2D0NRuX<4JwfX_M1?r*Yz>JCzxJ_o4Tbv-70q#pkov7f)L4dev!us-rEI6|t2b z9~UJ-$&n1Z@61n3MDpSbQ{}@xDKOM?c6N6CVCazc=~q-4Yl<^K9>Tjb*P(Z)^z}g| z_tpGmB$!lfShvVhP!{xDoGh!U+qP%s!f{{9IT(Z8T|sqQ#uu;81kpQn(^+KYoG;no z?6Pg@mi-4k{`KxBHoKfRVoI5ugM&-eshze={nkh(D2EnHxxCC=QIT^{s@Pq>a?o=y z+c!L2z{xFbA+8*ONIe|^}r)j%dzCx$>#`mN%-^oc(x#mCnG8t()p={q%vOC- z$6UWw3`yF&%3%MF3=KT#J}EmW`#7|Jc({l)RGLg9ag!+=+%b*R*B8Y{OvfPIe|6}wiF-TF{(e(E_v~b|;pXmxoR!=2wr*8p@p)%uAW-i+tcM2&kcbbEX~G7EK${EFCJ%+I+B| z(^zu?n0Nawk4as5YSV~MuauGw70_It(n6h!Rgp25E3z0^y{4OJY;4Tw*8S`oa7o#z z`Z(YOMXtkPO?G6Q*_?6UaJ%YgF;W%is{=LtI)Luo)qsY~c8-AkrrOv5oXYG{)i`0^ z-oAXN;;=7w_2)HEiBC-I&(}>?g}8=P*k=W-DhGJK5_YC`#&JSZRWY60+Pc}gx?1=1 z6{u-jrw=cW8R^(@YAe&B*Xw;gu+pI7%Z`zhZS1nbz1q4t4GPi z6G#*lp^zq#bu=)reR(mGm58Q8JjJjQ48xq;67%kLWsi4hy>1L#nkXrW6vZ3T8c(9r z(^+Z`@dgp0Bot&xumEGYjM+inWw+zt&6X%JlqeDu1o5Cegg{wEKoCKQ;A$f!62@Uk zl7JAn4@q#}AxbVS5+VU9ufxt$MFNN;2*~FFpvo8k1c*x{ErBGSp&+0PWYZE0NXnz! zmn=)tq$0o|6h(j(fn^nGKoJ3e%n?=6*K=bE4OIZ(KoJ6zD6T0S3_&4^GKB_+02KPU d=r09;axSMxf;ox6G%Nk$W`7AviYA53{vVXjTJ`_{ literal 0 HcmV?d00001 diff --git a/scripts/waves/16.la b/scripts/waves/16.la new file mode 100644 index 0000000000000000000000000000000000000000..acee5e0a5984311435ace336e7688fb2a11bab89 GIT binary patch literal 5199 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

88-a5z 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/19.la b/scripts/waves/19.la new file mode 100644 index 0000000000000000000000000000000000000000..36299f1a1f21773b051d3d905294be40d837566f GIT binary patch literal 5599 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 Idl=^bKZxu2@Bjb+ literal 0 HcmV?d00001 diff --git a/scripts/waves/2.la b/scripts/waves/2.la new file mode 100644 index 0000000000000000000000000000000000000000..1b5f3c1ef61d1997e8b9f61b1b6fa803508ba7d4 GIT binary patch literal 3599 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/20.la b/scripts/waves/20.la new file mode 100644 index 0000000000000000000000000000000000000000..9d2ce97235e5986de8c39db2d156f630642d8f72 GIT binary patch literal 4800 zcmXX}4NMf-mIlcxRK0;wl0m!oAwL@$Zq2|9CZOGkFpwxz&(j%6{DF=9W%#L@0Y?*X zx?lLsf>ym1#RRc?2Z7${hPpmbNKm?WR5S}t)f;dm0j2jT&{=+}6jTx#yXVQCa`tvt z-L7-K^PO{V-FwezmtHV)Jena}W99po=Zjh2o|#62CNI}1?I;#vGK;Jd-MEFf5`MiA z)2xO@K5U@SjDh8X6rLls-A01pX}()F2yVe7a10WxEaqnjqMH&GEpqeNz*87W5<%)| zjuSN4$6(27ak*v0b0XzeX0U{LfueZHsb^@t#3Pr%bW3Y-F5ZEGU>lSfsGba)Lw&G9{4|a? DIO^# z&m-*YCU}XZ5XbtlEXY_!0z=}BIG)&76w@uTT3gcL z$u(o*xq!O(%&Xshb!nF_wrXReGvMOId!2WB>pvU0S-&egwempwY(T)-1;_HP!nv-i z<+h%)v-bjyKOCY`%g=`_A3TF%`dZzURNKAlQC%;8`0%fph1)ttH9#@%g(61S<{PyNgP{FWZH^0qo;?)AvhjgMahWPkhZ%coPb0a<0SW^2f#jz=66f87az5L@b@48Ju?0Jhv@|YpZ0ve{nt55`l;Eb zMQ?TL)#$G2__}-N?q<)O*tf5`D7YY?eZ?D6KE_ovEoE&i%|{FmC+K36Q}zaY_~i9J ze)uI}XZowU^c$znhJ5zjQ|pt0cDsAP<2_LyU0u}~HRV|xtA1M;eI-61{mIF+($%!x z&3jU^o;-|fE?=I$ReE=P@4bsr(Lep;gRY^6QL{I$e_1eA`1yINDZZ^KZmDG3iNLul zCwt-#jJLASvkME$x9*(XxxW7~+B_I>tt4xsti-nM%CX*KyZ2{B)jc`($peQE?p3Ce#=FsU{W{wQ?L{>(WC#04hZds!vJILyg z<=W7#L!&8EQzba;`Gb|X(I)!^%jBZhyC`g3dN|Y@_}xfI!j=0yo#_|r;`@Rc$j!0R z=GAABEkE<;2 z>Pnc7sLktG+Ijr!xuZQ*ajjhG^sQ~pI)`q}?>yqX99G+WF!f-qwJ$QRq-k`k{lbwg z1E-Jly_rFNk4+o9<}pena+O9nHd7sU7c236Xxzd_OH)%vhsNLJ+A3R~=kLy6&8%$M zQ<$U7Cu7DSpQqu;OSEyZKa*QSqc^WK{9$Be&AS&EO3 zKUxxc!8F-&H-Go;um|C*;r*)?6(Us~PX)R$b;X!9s z(>6EnCOzQAjPaTp`$C*Oa(`%IvISd%8XF&YtR#U}OC5{eF^8_!#%!r6SwA}U(Nv24 z&JkhHYW|{5=Qw=#a8lSrQe{}qpj)9Ej)d;7vD=?T9!lJ?MMFqhcb?m7HSp}7-44gt zw%ym#im8}8aidjjRaHZAYjYgNtWl(7`!x0={fQtq;BnU`A48R4>No^7L<~#qg9d=8*dWzSmP5kNG4s3!NMZK zsDX-)fb~dHy=x5>-|QTVDYfItcH4n zw2Dbj*!F0Wy7eB>y+g5eBb($BWAq#>43mW=T_lHd4S8D-@(D(RlU3N_!8K7KVb^0~ zMyMD>_LfAEH(!49z-N|tU$=~M{25x-l6HCzv69wjLm8PC-X;0htOHvAfUhsNF~ii| z#}h_(qRmZsW-@s*D-P0%MZ0-s(&Gv%>NsMLJh^{|xY{xiUbz|+))1T19lMgu9+A5f zwX()33JQ0hr97U7AVX%}Y07U_h+LMU814+~@>lKpfs?4ToPhQUa$Bq27eK&&@;}Y86Prxif}Gr zrLf*f(PoNw`wWcL27f^O>O>U5&V${g!8H7-WtA(F!|21%~(A#k!zB4LnV`-?PCAP$waCLFp+}w0X_LX<> zWoM&4PCp*->Bd-$ckcDgo1cx$-C8Ybi|TCOdA8?5;zFAvt;0wY|Bei#XO*pKx`qJf}q7D5y&>cvR5Y zHubpc=lZ})fpgKz)e8${8|{1J%i_yMi~GIj%jbrN=gPeemybprPcOJw*LgPnWcGB| z+(=+_{cy+{F&bQ!b?)K0;Gv>!Z|`z+`PH`x5#y^5Z@16Z-8dT+98Z^)K7BeDUD!2z zek|7B)V35{c6+_a-tRb=9ddrSy7$3k$_ohA!SZ~|)tj9Y6pyI_k5?-MVQu^=;QDn=AmJDH%NagYs3qu*?h zVAo;TNgTmJo`zINVaVwMs_+i!+Q2{#0|-0Ac_(6$G?0f@cL9R2J|Hfr#N4H5>&9 zffn4M_nfL71_^W&L+NDzRS1#U$( z2c#ea1{gu0Myn#!8-klUMo_(%%OF!>>P-E9ff9Zq7zbbh^Sd+EAE49=0IF10nV^9o OeB}2~P^;FeE&e~qwvJ8! literal 0 HcmV?d00001 diff --git a/scripts/waves/3.la b/scripts/waves/3.la new file mode 100644 index 0000000000000000000000000000000000000000..8d226446159df2d58023aabc6a5df203a71a55cc GIT binary patch literal 3599 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/30.la b/scripts/waves/30.la new file mode 100644 index 0000000000000000000000000000000000000000..9a10deb49c5fe84d495c543a248e80b7d2a2051e GIT binary patch literal 5199 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 HqCox+#``d> literal 0 HcmV?d00001 diff --git a/scripts/waves/40.la b/scripts/waves/40.la new file mode 100644 index 0000000000000000000000000000000000000000..ec0ba762566ee8123186a8b578769964b1bff936 GIT binary patch literal 6400 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 haRWdCE&`iNTNjEqUA-PqEw+en#fr3IlWb+mR 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/50.la b/scripts/waves/50.la new file mode 100644 index 0000000000000000000000000000000000000000..f0a40a2e915f8cdfcdfc0bd470508f820fa886d3 GIT binary patch literal 5599 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 On8>FX*-~u^gZv+}t`wR8 literal 0 HcmV?d00001 diff --git a/scripts/waves/6.la b/scripts/waves/6.la new file mode 100644 index 0000000000000000000000000000000000000000..e56bc102f844399d286afb01b1ea2c8a027e18b3 GIT binary patch literal 3200 zcmXX|3uqNb7p@UvvI{LJ_(&jD19{9qEDA>N1tS)G>_P<#+9Y6V0x`~j7b=)~1N9Nm zI0Fh5j7^|637BLCV*kGo<1Qq{LK1hOLWQIzP^&_`nE@>peB^@tzfJ$)K4#9G^PO|P zb7pplV_^VMb|7EhE}hl=QGP6-o)2PM7LziI7IKAXL64oToh_ zmLivC(|O$+6GMU?H&2u)F}|jJd?8n2+{P{E5|uk5AQdS*7W*6cke#;MbX9i_6^4{`VLHat=Fe-yxm_l+3@`G-JQiprdn?0rpI;^4UN7i z%{;GtQ(JrIQf21V>FKfD_jSb^`c^#IIosU!!@0ly`rywUt=ER@kDWjE?zk^b=W@BK3Vp3MaIpB%gYcy-&yKmYj2x%F*j`-(Rld$Q@f6A!kwRuxU>PM_>} z+&(*8eEIlC`HI(vySfgaZf(x5+_>>&W(`*Z1KS=ve0BKk_PR{*rVT5eR9_wM`e66& zAHTdYL>KQqI(EMPz!5wCdV0rn+x6zmRQ2V>%f-)qwlFl*Ry6ebV!^{LmHR3O2Rpr> z)N)l9n-nV&z#J@_BZW!ntt(9dura7eP6i!*3pp@8>Zg2*@X4R+KW>Yk zclT}T>wC0Vd3&;|ZTfB7>xC`r+D|+xe)Hz)(bqHk$GURccLksCYu{Jfed%_mcs0KF z@ZsyHOAoYnujs2jap|)*)a{3lf6sl`KehABqrUpfM{e#}INi0Qt7x>tJG-;`O>uQb z-`+bkl&hM#Sm-=EHMnlyE6yxw=qv~fU{9aL;qZV zNf0m<_coRe%;(dRvnarErA_^LoIG7;Rypnniy&;CFh)KN}xn= zHy{Fz%TGj+z}GQ4QIRgZB|#BvDXoa2lrY?55NHa|mQoEdVL-+vw;VPQ5*9g8#F&6< zE@L0gEEE$Q+*wdf%r(O;#TcgW%_aOOCWLT=AvE^g*tNK|3$-`ACU&^>6hcbY5E#ag8mx#$-f%_}sn!G@L5f8HNuEpq>i;3RFx6mC zL7Y5KH0Z*Bh+rs%wW7)>{3YBFERvWKA=bdg;Q)f{OiyH7eGt^Dq(#&!9--B11Os8V+lLNm>`qrLc_sTpV$1Fb>%ts+7k?OD!o>w9goS;pJOdFqbAd@JY<;u_! zKQxBNnln5AnL}v`jUQU(=BY5H2^Kr(~!Z z(UOT+a2my)u|jhbZ99-4VVKXf>#C??d37;q^z6bTBa?HEGLE#>P8WT zp;4~2#G?NqkK&z$D;^WAEXvBz8b+go4b#L*$CXR5a?m*}_NAq;6cJ&TE1`UA9QftN zppD=%q=6DdM#AA~5J9*hl;G6}u5I#$gnor5#Tn=UJ~1gxRV*!9jZ30D0(>H4oFhWG zMI6XnO9~~+BbGEacM!N#lp=C!9A=G{T1m=K0>vp(Fq$aWVc3VV0~G2SMh(T8u+(#? zj+LL)Eo1E~#~5X_LW0O!6S`bdYEdu6G$T%;Zj=pG92?^Jj#i!$lvocv!*yOH z(c0Bc=r*Dj|x42Bj3@2K`7EoFiPpi~?_#b8ab48B2;wF$052BP7%y`CFEAYdHl9 z!M4I5;PAyRwUj7>+G%C6N(K}d_`oja+F$@C5ibv*CvK=sG!lI1fqJS4fq%G0pfJNo nBxYF^%FEWt&%d(-9DydP3X1qi)Co!O&^ieUB+Eb)bFBUc2VqYy literal 0 HcmV?d00001 diff --git a/scripts/waves/60.la b/scripts/waves/60.la new file mode 100644 index 0000000000000000000000000000000000000000..24b3b43a166dc533a59578e59a3784b0e293cd42 GIT binary patch literal 5599 zcmXw74@?x-*0+$T%(Pe~u{<`hvLybww*?+1t;{59A+a)(Pg+cD-A!77#KO$0ASAWS zCjNn>%AJ0qkW{;yDC{O!XX>s(qIKqrR!ClT=Eb&}Jaso|l})JLtDu;)btmzCXO`E? z?(Dti{Lb&3bLY;zXRs`uDrU@Rt?<0E$v)Apn_{M0ug21bo0OW4yr?m?U>R?^CEems zwq&R^Y}$!&=BbFzIa=kipr#6%nN(~|cMy{Uc3&ou@C?E!j+)415OxSn$HxSh47Nyy zt7(B$RjW?D!dxgGMXpjn^a_S;Gh4x2S)$0v5yL8r<|L`5u7HZV9m)13GHxPfa71)< zOjTWG4pvNRaYC>lN+~78z(PTKqBk8)Te-0i9`E?0|6za8nRh#0tl3h!a^-Mo+cY`# z@cBr2`AARE>YJMv|5BwHEZNZ~| zJbL5RO(#<2Pk(;%(>H%EKfPi7f}`(s9ysyQ2lt;Yefm!M+oPqMI-gW8II(Nn+S(oE z<#*flEh|gEyRiDV+NH<#9DIK0!9C!3^i%bgj{8sFfBg9D`C940aOZokR+qkYYvk;k zOP@9u?H=wt+S$43^B2E8tQ~0{=~H7@jn41y%oH7bdiL@A<+o>spSDj0E&uBY4-#dEr$#-+FweM)YU;FU(#~*#ZY15tMSK52dzOrXe z^WF>9-+uY!%a1m#=WCCh{q)VHdwNu14c=F`Qxy1`( zzxCX`R9jU0cFmd(ci#Es(2|Cqe?I?CdHeDOo!>rs`Mm`vW*%NWcJ}A9d;a#vio^G+ zpWG{5`ODprk$3+7(%Rsv!w0JG4G(YGaO&cs{*j(Dw@+QzQd;@&WZ-;rZF$f2sSkJF zd-c(|oh!eq?>YF=)w6qcTv{u9S9<5bz0&Oiht}RL@9DqYH+}8Ii4%9)rgtSS-YzOS zvv*0&!sXKJ<}(K$UTV0$=&LP%4j(;tZ)NH7#`dr8UVYkJ{&oA3&25W6zq945%|~Vr zz5U0vLjo6d-zFzd3({r#(@Xx*AH)6F#KWLg{r*`Jr0f&H%9;Y(aC+1>)m=bX`9-znVI4jGlPRQ4kN0U z4Me(UpQWx0%%)Q7FgzufPo5kd-IwetOH@@ge!PDmHO?a`9@02$hbvmMnHWPtLGQ9; z z+iK~#$&}YoA8kzX>gCyemVRThOo_$yp)N5{xiQKbw{A-$w?*S&M>3^Qri@hFOoeMw zQB*iK6paRoBXKFyrywy9B+mUXcbSy7j44liF8V81L#h(i*AU7BpK!lLo3F zSV4j-P+hCgcxv$J$l!Px=hbxF9P*;GgJIT^P_$>uwC%hWu5&U2(} zO>5aiJ=FstSJb)*Ypn?cD!Yxc!epPs)sMxbvY29+T+}2qJP{5DpOxjjWH>UFa?GI? zk;V&iWFXxX~(v$(UEBhaJy}S{g@mnYR&_;02w4h(lAB?gZ8eL~yda*cS<~ zG9_3*HLP14F(aa6rj;=DT*b49ZW%_aTxJJ3Hzev>b|}Q$5T-d!N1W(6hCwsLVKqoM z^$^l!9cL`rvus4*6h)S)JFdcrX&VM33^{N{Y6f<6#f%ub&B@4w+hoFFshEnXVwE9m zV8vjH$x(zEW{5F{kVy@SOq0PMI79~6V8&F_#4tjpfeg?>GfadqH0XiUFhBqp7*OcL zZ~h8u0QiGEsow)L;FuSKfsf3a{`UvaJ_3;FFU7n)0DVvXHE--=061?vPn7p#7#REh z@)Qs;bo?;$=1?ktI6pA3nO6ZDkoswYp&zOr0GPngj}vsz2MP4^Eb}_x@qr+OF+hD2 zUy$z`2*hCaUqv42kLLvl&G!L|5TZUbA4=XF0~la_K>juF)YqGg0SKBOhfkPS`eQ#R zXuc8@ehzT)sUdW*1C3APYx6=363k2ro`J&G`9E-hNkc#m<(J^y5)C7y#LE;-$EvGC z(WRQIN(@6mj-qfrDJ&PL#E>CGSzp3XB;vs4uTY&AU0cQspd3|*Lb&|WQgxMK)n9vl zAmGP$idEfbQuDDZlv5~V1xpf^B|=O5mQ+%18#B!?IrmUT~N zvOy?^lsqfO$X{NtP$2*elV(sEh6)^i40u%q8_eJggek)kc~=I zm8zx$DX9j8sv9~}T*7z_@suLOBi&GSjW-+&8JQ+e7^kX|s_M)!MG0~-I8ymxIAq3l3oVsWR9?|f+*2(Vr`fRo@Yzo1fH(qRbvEt;7woxEI9WdnmSd!=jzqJKfQZqpl|bu(k+YEe-q?4 zUr@e&c%(_|@B}zhP+kwl7W$m!3EaESAO$D+2i$2+V=J>@w-tE|SiyRrN+gd!vwR~BpI4u8U2BNER66y>JvZOdZ5P%j-IZ%7T;N3`B6M zfk*HJc&bo^aD)&C!Xgx_s;}Zz*g+9e2!$vx1Gg-Hfnyn5QsPJw=Q#Or0IRBS1VDog z`oD1_*pI;g%&-YwAzz~S8-S|ox-Tr!` z^BUhF{6PcndAPwCa08`5DTwn#0D#B|Od7y}EYA$ed>^QR3{)Whs{s=vKi`0C-Z3D2 z1ApW@XJBs{@G&7|p!6@k8~B9+0{hB5vpK@->1k0VpX*y2Jap!#)_#U1=h{9Y`$q!{WUT@u<43m9>mJ>{y>k9jB zb<1wYwpdsU9;r;exG6a(Qa@Rt3iRXCgDv&ijorcIzTFmYM^aZdc2o_7se>nygLQLV z7Qtd7GZfB*4ZUtIUDKpFgI()Z8#hl~$>H(Bx~Q(DyV4d%^~޾nz*cߧ'_ߟkJRnN>~>zǿgoZNFvFr/_)WwGߟ#ZV(h訖N2ϧwWg+ ;cc訨VFޮJ7W)))Wgs{rƖ(Vvf~.s'GWWW'O:*;3 +⦞FV(緗))翯kb&66&sgWW's.N₫ó/#R&vVV&ǗWW7SOj R~&~vvVV6v>2kgWש_os¢" +3FV(֨vS_)Wӻ.fF6Vv6f>zgGww';R2"z:{#3{.V2SWWwGGg?S[Ҿ&vFF^ΒO77ǧҎ2ZkC+ξ6F^K/wחw7'';z6v6Ff ?7Ggs Rn>.z[#[:ΦF6f.og7w?Sˣ:.&F6vFb+CgG7777?>ˊkZ>NK/w7GS#*"6v66N2*O_OrΎrbzj˛[#;Z2R.n.[ӓ3C#[{;+j:ZBҒrBZ:sӯs3;k* +:zz:jjj*jjꪫ++++kk ˻{[ۛ +*JJZb""ښ: + +kK{#cK+j:ڢ"Zzj+˻KKK;K 몊JzzzzzZJ#k*j#k+k ˋ***jjJ +jj +ʊʺjj*j +++ K;K;K+ +j +:J +*jJʊj ++kK;;;{ k+jJJ +J:j+++k ˋk++**jʺ +**ʊCó{ +2.Z;S/{j‚2B2BZzڢ:s__S"~f>c/ӳ#+*bnn.k[SoOo/J2榞>rcϿOﯯsۻ{*j"bb222B2bZ+ï_/ +R&&&~R*;S/S/oSSCcB2Ү..rBZ +뫋[#C#{{b2NN>Rk/O//sck늺 +Z:Z‚BBrb+#S??K"R~ަ&+ s//CsC[ K *rr +zj*k˻[S￿Oo; +>ޞNj[[3/Os/"zښzb""bZZ3o/O??Cn^R K۳cSsCSS{ʚ2rb22Z2⢚ + +{C?Ͽ/Kn~~.joo3c ; +z"ڂ"¢⚊K3?O??#R~~N +{ï/3{{kJbbbbZz + + Ooo/;zbΎNBj{/o/SC[K +JJʺZ"ZjsocK \ No newline at end of file diff --git a/scripts/waves/70.la b/scripts/waves/70.la new file mode 100644 index 0000000000000000000000000000000000000000..bd36d0518068ec8df0eb4c31fc0d0db81fba313c GIT binary patch literal 5599 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 Z+)$5c!w3$t+gg$ULWmQjurvc^{|EEvOP>G$ literal 0 HcmV?d00001 diff --git a/scripts/waves/80.la b/scripts/waves/80.la new file mode 100644 index 0000000000000000000000000000000000000000..95040f8929d11c306ebc885d711eeda5268d47ee GIT binary patch literal 5199 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 pkPH!0(MuyKQpZEDyU6B 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/Makefile.am b/scripts/waves/Makefile.am new file mode 100644 index 0000000..e067d85 --- /dev/null +++ b/scripts/waves/Makefile.am @@ -0,0 +1,12 @@ +dist_pkgdata_DATA = 0.la 1.la 2.la 3.la 4.la 5.la 6.la 7.la 8.la 9.la ..la am.la \ + von.la beep.la erklaerung.la fuer.la keine-weiteren-nachrichten.la \ + nachricht-gelscht.la nachricht.la nachrichten.la neue-nachricht.la \ + neue-nachrichten.la uhr.la um.la anrufbeantworter-von.la \ + bitte-nachricht.la fuer-neue-ansage-9.la zum-abhoeren-1.la \ + bitte-neue-ansage-komplett.la neue-ansage-lautet.la wenn-einverstanden-1.la \ + bitte-neue-ansage-kurz.la ansage-gespeichert.la und.la ein.la 10.la 11.la \ + 12.la 13.la 14.la 15.la 16.la 17.la 18.la 19.la 20.la 30.la 40.la 50.la 60.la \ + 70.la 80.la 90.la fernabfrage-aktiv.la README + +uninstall-hook: + -rmdir $(DESTDIR)$(pkgdatadir) diff --git a/scripts/waves/README b/scripts/waves/README new file mode 100644 index 0000000..3f41f68 --- /dev/null +++ b/scripts/waves/README @@ -0,0 +1,2 @@ +This directory holds the global audio snippets used by the scripts distributed +with CapiSuite. Please see the CapiSuite documentation for further details. diff --git a/scripts/waves/am.la b/scripts/waves/am.la new file mode 100644 index 0000000000000000000000000000000000000000..09cbae74d801144de8f224df8647b543f84ae592 GIT binary patch literal 5599 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/anrufbeantworter-von.la b/scripts/waves/anrufbeantworter-von.la new file mode 100644 index 0000000000000000000000000000000000000000..170d6b5fb4f9e8149e771289bd2fc0ad6cbf43b0 GIT binary patch literal 18799 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/ansage-gespeichert.la b/scripts/waves/ansage-gespeichert.la new file mode 100644 index 0000000000000000000000000000000000000000..ee06c74ce4537fbd331bfaa0252104c9e20d9897 GIT binary patch literal 12000 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/beep.la b/scripts/waves/beep.la new file mode 100644 index 0000000..59903fa --- /dev/null +++ b/scripts/waves/beep.la @@ -0,0 +1 @@ ++J \ No newline at end of file diff --git a/scripts/waves/bitte-nachricht.la b/scripts/waves/bitte-nachricht.la new file mode 100644 index 0000000000000000000000000000000000000000..c6eb8cde1889e94c522c651f5b7f4d9986d6cebe GIT binary patch literal 24000 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/bitte-neue-ansage-komplett.la b/scripts/waves/bitte-neue-ansage-komplett.la new file mode 100644 index 0000000000000000000000000000000000000000..0f0796514bc9918b99003df0e4a01f93e65a58ed GIT binary patch literal 84800 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/bitte-neue-ansage-kurz.la b/scripts/waves/bitte-neue-ansage-kurz.la new file mode 100644 index 0000000000000000000000000000000000000000..4e23d5b842c90b25a1dd0abd458654aecc904f51 GIT binary patch literal 26400 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/ein.la b/scripts/waves/ein.la new file mode 100644 index 0000000000000000000000000000000000000000..f3a5f71300fbb04eb17add2d271cf910fea2079b GIT binary patch literal 2319 zcmWkue`s1+8r@03qz05VkPZskQP4zL-w>ghQSc24$>a$N2GXg42nw1i=un}VDkz(P zP85_?NV*D|Rq$B_Weu2#4|IuO(mYT$fp)8)MM15CZ`VN6F%b97{qe`;a?U;9_nq&0 z-R8`dZo5aPw)H#uYBF5Nz!>LtnG@|@ICY84pa1e_2;Ux|7e6atAue+{yD#t7iMg!p4($G!NyO zExtT%Z-so5qOTf2u0aL7$|072z39c){`b#khryYTKm7M&kJsFP^}Ors&8)D6hrd62 zwcl0n54K-=Ax!`L;Yn;Ov5-itNO(0GE15Ng#aRvNgJE^?7;|84pm?1xFZ6oNufF>5 zW_tMB<8S}<<7e^t_YXh$Ixkn|pS0hcp00g*#IVo5ds0@yi=W<4+v16t?fH12M&^)- zX}WzYy!#-chFyB_SXlwcJ>#O}k+sj{m_|uQSZiM#t-@E!*`oYGP`sB^S zwf)0(p`1v>S@7+r!%2Uo*IP*FvS<@FqY^fO?cJ-F%pHgK~mmf!0>2iHEl-G8{l{&MvAr=Mo%oRgEq+b)w5rVp9T#qc*I;B32Z9WlTr^L> zS!QduQw}_Oy7>0_xj)zY`snB)p;K`Vmv2{HD6>WFqt)}<)e|_Q4Td%2`__X&nwk~M zbz5SwILoC%vuNLl+yEA!S;TznNr?)o5Rj+m%fIidRh5J9etA7JGw;p7k)54$Od2vo z^T$OF>~e8#Yyj7khK6B^*F;LE{oX>BqNL=bL7uIIDL{w9Oa=zb;B1soIbmR@`>si! zzJ2lK$BPRcb@S%@a9Q7s+#tj{>p?)FhRwdNWh$R+uqlO3;cTc}QU+T}m0P0e&!So> z2Kqn;R=>>KhMidVZu-=v*5`He+uvY`w;?YDdH zbsbaTKHbs;g|&Wb%4$d&+ZnPiGV#UwHm*0w$oLi?`CtJlKEM5IZ)V;ZrUI}Ur-L2E zzyrh<3T)CQ5p06VR4=^tjx*k&la|hyV}nuE_ZYdf2W`MyRhVNm7E?nuXHJ$m9KcV) zjk>VCkR#kf7LcQNop<4huSC<<(Q7hL#nwxPsTU-6yupskE9q7g>gV%D9uH5%`Z_ju zN2?p897p%?oTJ{nx-V}edfN*x%lAQ%5AM2LdXBm}2Us=(nPUJDlr|x%h9oaE;`VTu zWa96tf~f(b-NWxPf;tT~0u1%)&WXzrtC7kF8#vQ^F7EFO6bziI3pc9(wr&B|r8ipH zELSD*n@E*=+!lojY4~Ca8X)zOrDU^d1-_ocNIgzTA zm~IftWv7lE_MRDNbL3(XERxM;AW*7{W_c;D!A%rWRT_z(a&<-VkCw6{Wx6HGDBwQ` z;0ZaVs&vXLWkP35%vT|n>SQtjK?NQOW{KI*0T&9Ym}mZ>*MvWh`#U@GFInqYq>C5S z#cBFz){1T=m%{~Yd7~Z+8FV;61s;MAkh1Fa7!6HcQqTO#G~Yxj6e@DDc-Y4xjBb~s z=S;9tp0tH4kH_s^B^0ul6=2nj*KF>Fg$_K(olTa4YRp#FKceu5L$7x@#S>J)h??O5 zebT|1nC*0S*!0R$O?FLoG#L~8x5sW`cY{kJH^~jQG%cp8HF`nen=10OL5fwui6a7< zO9)fm=;%7^hS-$#2+R?+8pkTIz@BK3OeK@<@i;in*Um<16UlDpt7z8%xe9{S>zGke ziQ~AiJUt?6GYSVo-NgHQt`r)i3`A(0zl5N^L0H?BTvhCHO-d%nSTJ%%a4c(G+3_Ih zBxRPnDIiB+wR%FvuBCS#u299S1tD(A!!r=`Dt|?;6eee5^}2K~W)tO?hW2nHY*J2f z@1)fx%z+~cKpI2cd6gzg74|)-$mqTt<2`AT;O>ecw7n3@b4Rn4aDy&aYJ$m4pvd5G z)DH8_UL8qVYgQqVmoy@t6j2zQN|Q`WHNzmqL+=pOC}fUI1-a>}l~1vNKT-u&fnyf! zj^~CTE{``rML6uz8XXNu{IizOaA`=Thz2Mar40E>Cf8%z3uG`e?p|#D0S%F%9u(ia zRFZo+uC>l6LoYweyzE16w*jA4Dzn9;UZzka6g#$DW0)Ds9Je<^h|GvX`ffKQw5q1E zFO)-*QWAA@xx85;FKICJ-TAQnKl3b literal 0 HcmV?d00001 diff --git a/scripts/waves/erklaerung.la b/scripts/waves/erklaerung.la new file mode 100644 index 0000000000000000000000000000000000000000..86295b2d88da8d242c9d6f9e33a255c1b15cec07 GIT binary patch literal 75200 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%0DiXNVmwJoO5jxw9F)#TdK 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 Ykf1T3C`bX6q)@#{P2B<(SitfB2hea;@Bjb+ literal 0 HcmV?d00001 diff --git a/scripts/waves/fernabfrage-aktiv.la b/scripts/waves/fernabfrage-aktiv.la new file mode 100644 index 0000000000000000000000000000000000000000..e86c9a69cdd858d4265bc270fd45db1fbd5d41dd GIT binary patch literal 40000 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 }0fvXBBwg1ejf3-BM)nGV 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/fuer-neue-ansage-9.la b/scripts/waves/fuer-neue-ansage-9.la new file mode 100644 index 0000000000000000000000000000000000000000..c09c38305f2e57d2b2696ab196dc99f9537b6417 GIT binary patch literal 30400 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 C?zfcy literal 0 HcmV?d00001 diff --git a/scripts/waves/nachricht-gelöscht.la b/scripts/waves/nachricht-gelöscht.la new file mode 100644 index 0000000000000000000000000000000000000000..f9504b91800d69b1b1d478d718d7c8dd1fda5476 GIT binary patch literal 10400 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 Y#73+Hn1dJ`92o0>7tA*JoDp>Y2QneEl>h($ literal 0 HcmV?d00001 diff --git a/scripts/waves/nachrichten.la b/scripts/waves/nachrichten.la new file mode 100644 index 0000000000000000000000000000000000000000..8254abde9a27af9c6f3112330d9b2b6ac28501d5 GIT binary patch literal 8000 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 MVq&m;Ea8a$5A!?-NB{r; literal 0 HcmV?d00001 diff --git a/scripts/waves/neue-ansage-lautet.la b/scripts/waves/neue-ansage-lautet.la new file mode 100644 index 0000000000000000000000000000000000000000..7dd125e604de8ce574f39bfd8477465414050cec GIT binary patch literal 16000 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 literal 0 HcmV?d00001 diff --git a/scripts/waves/uhr.la b/scripts/waves/uhr.la new file mode 100644 index 0000000000000000000000000000000000000000..1168fc8c75e20924c83811e28bedabe85749fd13 GIT binary patch literal 5199 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 h=net&Shs*_ literal 0 HcmV?d00001 diff --git a/scripts/waves/um.la b/scripts/waves/um.la new file mode 100644 index 0000000000000000000000000000000000000000..2f4d988df250324ea65db76c8596b8c1b6df0da3 GIT binary patch literal 6400 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 y9sfhHHȨίII)7#fk[zK;jR^#c[[s;ʛs;; I >((88v߷B뺲.BK#ZR&ƦS [{{{{;'i /fvx8."k)i)w'/;2R;k*Nξ&^΂ꛛS/okKSJJR榎BcO_?㛫ʺbZڢB"ZB2"J:kk +K_OSۺfFvv6>[_gGOCK*Jڂn>NNn"+˻#CCc/?'?ӛz.^66F~ZK/gOkk+ +zr.>>>Nn2:JJC{?_?&F&>ߟ?O/[K +jz"B.RZjk[{C/'_c2^fFNﯯ3 +j +nnRrB+K s?S N^f."[S??OS# +zbҮҒ2BBb:ˋk;co?O+>^*{C/O??ϏۻjJ:".2bbJjK;Kkc/oc{+J.N~~jïOOOoۻ:bBRB2b"* [soo/c{nN>NrZJ+{oo/Sã+:BRr2‚ʪ K;c3snn.z + ۃS/SsC#K+ +:b""bb"Zʊ +ʊ+k [#33[ +"""":ʪ+ [c##ۛKk+*jJ +J + + +jj + +Jʊ kkˋkK{* + +jjjjj****++k뫫*+k+**j***j*++++뫫++뫫**ꪫ++뫪***+몪**++++뫪+*******+++******++뫫+**j++**+++**++++k뫪******뫫******+++****+++*ꪪ+++**+++****+++jꪫ*+**++++*+***j**++++***++***+뫪**++++****++*++++++**+**+++++*+***+*++**++**+++****+++***+++j** +bΒ7I;o/.J3['OOo"^ +n2/:.ʯ?zOz:{ӊ毺zN" +/rc:oK.c:j;Z/oZO:kz[BcnCz[ +z{"o:ۺ>znJjˊ cojKˮjˋ{k :J+ʫ+Jk:+KKʫ +S**Kk:Bۣo{kz"r[{sK +:j+;{ëj{ +.Kk㪻2J:*JZK+#zZzZJK[J;ˣK""Jʛ;k*+K+kJJ+Kckʊk +"Z+;k +k*{ۋkkjJJ +"Z"*{ۋJ˻j* ++; Kj*z +Jz+kk+Kk**k+ +j*j+*몊*k**+*+*k+*j*j*+k*++++*++++++jꪫ+*++**+++****ꪪ++++***+++++**+*+***++*+++*++**++++++j***+++++*j*+ \ No newline at end of file diff --git a/scripts/waves/von.la b/scripts/waves/von.la new file mode 100644 index 0000000000000000000000000000000000000000..9730e61b1c6f9ee883491ced8d142df0d3c99500 GIT binary patch literal 5599 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 X43~gcrKnfH`!6G^3|zhE1fl;2RdE8~ literal 0 HcmV?d00001 diff --git a/scripts/waves/wenn-einverstanden-1.la b/scripts/waves/wenn-einverstanden-1.la new file mode 100644 index 0000000000000000000000000000000000000000..fe345248a7add57b3f67e161d3983dc9bcf0a33d GIT binary patch literal 31199 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 + +clean-local: + rm -f capisuite.conf diff --git a/src/application/.cvsignore b/src/application/.cvsignore new file mode 100644 index 0000000..e995588 --- /dev/null +++ b/src/application/.cvsignore @@ -0,0 +1,3 @@ +.deps +Makefile +Makefile.in diff --git a/src/application/Makefile.am b/src/application/Makefile.am new file mode 100644 index 0000000..2c213fc --- /dev/null +++ b/src/application/Makefile.am @@ -0,0 +1,5 @@ +noinst_LIBRARIES = libccapplication.a +libccapplication_a_SOURCES = capisuite.cpp capisuite.h capisuitemodule.h \ + capisuitemodule.cpp incomingscript.cpp incomingscript.h pythonscript.h \ + pythonscript.cpp idlescript.h idlescript.cpp applicationexception.h + diff --git a/src/application/applicationexception.h b/src/application/applicationexception.h new file mode 100644 index 0000000..f4342c6 --- /dev/null +++ b/src/application/applicationexception.h @@ -0,0 +1,121 @@ +/** @file applicationexception.h + @brief Contains ApplicationError - Exception class for errors in the application layer + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef APPLICATIONEXCEPTION_H +#define APPLICATIONEXCEPTION_H + +#include +#include + +using namespace std; + +/** @brief Exception class for errors in the application layer + + Each exception gets a severity (Warning, Error or Fatal), a message and the name of the function where it occurred. + + @author Gernot Hillier +*/ +class ApplicationError +{ + public: + + /** @brief Constructor. Create an object, print error message and abort if severity FATAL was chosen. + + @param errormsg some informal message describing the error + @param function_name name of the function which throws this exception + */ + ApplicationError(string errormsg,string function_name): + errormsg(errormsg),function_name(function_name) + {} + + /** @brief Return nice formatted error message + + Returns the string "Classname: error message occured in function()" + + @return error message + */ + virtual string message() + { + return ("ApplicationError: "+errormsg+" occured in "+function_name); + } + + + protected: + string errormsg; ///< textual error message + string function_name; ///< function/method where this error occured +}; + +/** @brief Overloaded operator for output of error classes +*/ +inline ostream& operator<<(ostream &s, ApplicationError &e) +{ + s << e.message(); + return s; +} + +#endif + +/* History + +$Log: applicationexception.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.12 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.11 2002/12/13 09:57:10 ghillie +- error message formatting done by exception classes now + +Revision 1.10 2002/12/10 15:03:04 ghillie +- added missing include, using namespace std + +Revision 1.9 2002/12/09 15:19:53 ghillie +- removed severity WARNING +- removed printing of error message in ERROR severity + +Revision 1.8 2002/11/29 10:20:44 ghillie +- updated docs, use doxygen format now + +Revision 1.7 2002/11/27 15:54:02 ghillie +updated docu for doxygen + +Revision 1.6 2002/11/25 21:00:53 ghillie +- improved documentation, now doxygen-readabl + +Revision 1.5 2002/11/18 14:21:07 ghillie +- moved global severity_t to ApplicationError::severity_t +- added throw() declarations to header files + +Revision 1.4 2002/11/17 14:34:17 ghillie +small change in header description + +Revision 1.3 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.2 2002/10/27 12:47:20 ghillie +- added multithread support for python +- changed callcontrol reference to stay in the python namespace +- changed ApplicationError to support differen severity + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.1 2002/10/24 09:58:12 ghillie +definition of application exceptions will stay here + +*/ diff --git a/src/application/capisuite.cpp b/src/application/capisuite.cpp new file mode 100644 index 0000000..8f8b299 --- /dev/null +++ b/src/application/capisuite.cpp @@ -0,0 +1,590 @@ +/* @file capisuite.cpp + @brief Contains CapiSuite - Main application class, implements ApplicationInterface + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include "../backend/capi.h" +#include "../backend/connection.h" +#include "incomingscript.h" +#include "idlescript.h" +#include "capisuite.h" + +/** @brief Global Pointer to current CapiSuite instance +*/ +CapiSuite* capisuiteInstance=NULL; + +void exit_handler(int) +{ + if (capisuiteInstance) + capisuiteInstance->finish(); +} + +void hup_handler(int) +{ + if (capisuiteInstance) + capisuiteInstance->reload(); + signal(SIGHUP,hup_handler); +} + +CapiSuite::CapiSuite(int argc,char **argv) +:capi(NULL),waiting(),instances(),config(),idle(NULL),py_state(NULL),debug(NULL),error(NULL),finish_flag(false),custom_configfile(),daemonmode(false) +{ + if (capisuiteInstance!=NULL) { + cerr << "FATAL error: More than one instances of CapiSuite created"; + exit(1); + } + capisuiteInstance=this; + + readCommandline(argc,argv); + readConfiguration(); + + try { + if (daemonmode) + // set daemon mode + if (daemon(0,0)!=0) { + (*error) << prefix() << "FATAL error: Can't fork off daemon" << endl; + exit(1); + } + + debug_level=atoi(config["log_level"].c_str()); + + (*debug) << prefix() << "CapiSuite " << VERSION << " started." << endl; + string info; + if (debug_level>=2) + info=Capi::getInfo(true); + else + info=Capi::getInfo(false); + (*debug) << prefix(); + for (int i=0;iregisterApplicationInterface(this); + + capi->setListenTelephony(0); // TODO: 0 = all, evtl. einstellbar? + capi->setListenFaxG3(0); // TODO: 0 = all, evtl. einstellbar? + + // initialization of the Python interpreter to be thread safe (taken out of PyApache 4.26) + Py_Initialize(); + PycString_IMPORT; // initialize cStringIO module + if (!(save_cStringIO=PycStringIO)) { // save it as it will be overwritten by each #include with NULL (sic) + (*error) << prefix() << "FATAL error: error during Python interpreter initialization (cString)"; + exit(1); + } + PyEval_InitThreads(); // init and acquire lock + py_state=PyEval_SaveThread(); // release lock, save thread context + if (!py_state) { + (*error) << prefix() << "FATAL error: can't release python lock"; + exit(1); + } + + // idle script object + int interval=atoi(config["idle_script_interval"].c_str()); + if (interval && config["idle_script"]!="") + idle=new IdleScript(*debug,debug_level,*error,capi,config["idle_script"],interval,py_state,save_cStringIO); + + // signal handling + signal(SIGTERM,exit_handler); + signal(SIGINT,exit_handler); // this must be located after pyhton initialization + signal(SIGHUP,hup_handler); + } + catch (CapiError e) { + capisuiteInstance=NULL; + if (idle) { + idle->requestTerminate(); + } + if (py_state) { + PyEval_RestoreThread(py_state); // switch to right thread context, acquire lock + py_state=NULL; + Py_Finalize(); + } + if (capi) + delete capi; + (*error) << prefix() << "Can't start Capi abstraction. The given error message was: " << e << endl << endl; + exit(1); + } + catch (ApplicationError e) { + capisuiteInstance=NULL; + if (idle) { + idle->requestTerminate(); + } + if (py_state) { + PyEval_RestoreThread(py_state); // switch to right thread context, acquire lock + py_state=NULL; + Py_Finalize(); + } + if (capi) + delete capi; + (*error) << prefix() << "Can't start application. The given error message was: " << e << endl; + exit(1); + } +} + +CapiSuite::~CapiSuite() +{ + if (idle) + idle->requestTerminate(); // will self-delete! + + // thread-safe shutdown of the Python interpreter (taken out of PyApache 4.26) + if (py_state) { + PyEval_RestoreThread(py_state); // switch to right thread context, acquire lock + py_state=NULL; + Py_Finalize(); + } + + delete capi; + + (*debug) << prefix() << "CapiSuite finished." << endl; + (*error) << prefix() << "CapiSuite finished." << endl; + + capisuiteInstance=NULL; +} + + +void +CapiSuite::finish() +{ + if (debug_level >= 2) + (*debug) << prefix() << "requested finish" << endl; + finish_flag=true; +} + +void CapiSuite::reload() +{ + if (debug_level >= 2) + (*debug) << prefix() << "requested reload" << endl; + if (idle) + idle->activate(); +} + +void +CapiSuite::callWaiting (Connection *conn) +{ + waiting.push(conn); +} + +void +CapiSuite::mainLoop() +{ + timespec delay_time; + delay_time.tv_sec=0; delay_time.tv_nsec=100000000; // 100 msec + int count,errorcount=0; + while (!finish_flag) { + nanosleep(&delay_time,NULL); + count++; + while (waiting.size()) { + Connection* conn=waiting.front(); + waiting.pop(); + if (instances.count(conn)) + throw ApplicationError("double used connection reference","CapiSuite::mainLoop()"); + + IncomingScript *instance; + try { + instance=new IncomingScript(*debug,debug_level,*error,conn,config["incoming_script"],save_cStringIO); + } + catch (ApplicationError e) + { + (*error) << prefix() << "ERROR: can't start CallControl thread, message was: " << e << endl; + delete instance; + } + // otherwise it will self-delete! + } + } +} + +string +CapiSuite::prefix() +{ + time_t t=time(NULL); + char* ct=ctime(&t); + ct[24]='\0'; + stringstream s; + s << ct << " CapiSuite " << hex << this << ": "; + return (s.str()); +} + +void +CapiSuite::parseConfigFile(ifstream &configfile) +{ + while (!configfile.eof()) { + string l; + getline(configfile,l); + if (l.size() && l[0]!='#') { + int pos=l.find("="); + if (pos>0) { + int key_f=l.find_first_not_of("\" \t"); // strip blanks and " chars... + int key_l=l.find_last_not_of("\" \t",pos-1); + int value_f=l.find_first_not_of("\" \t",pos+1); + int value_l=l.find_last_not_of("\" \t"); + string key,value; + if (key_f>=0 && key_f<=key_l) { // if we have a valid key + key=l.substr(key_f,key_l-key_f+1); + if (value_f>0 && value_f<=value_l) + value=l.substr(value_f,value_l-value_f+1); + else + value=""; + config[key]=value; + } + } + } + } +} + +void +CapiSuite::checkOption(string key, string value) +{ + if (!config.count(key)) { + cerr << "Warning: Can't find " << key << " variable. Using default (" << value << ")." << endl; + config[key]=value; + } +} + +void +CapiSuite::logMessage(string message, int level) +{ + if (debug_level >= level) + (*debug) << prefix() << message << endl; +} + +void +CapiSuite::errorMessage(string message) +{ + (*error) << prefix() << message << endl; +} + +void +CapiSuite::readConfiguration() +{ + ifstream configf; + + if (custom_configfile.size()) { + configf.open(custom_configfile.c_str()); + if (configf) + parseConfigFile(configf); + else + cerr << "Warning: Can't open custom file " << custom_configfile << "."<< endl; + configf.close(); + } else { + configf.open((string(PKGSYSCONFDIR)+"/capisuite.conf").c_str()); + if (configf) + parseConfigFile(configf); + else + cerr << "Warning: Can't open " << PKGSYSCONFDIR <<"/capisuite.conf." << endl; + configf.close(); + } + + checkOption("incoming_script",string(PKGLIBDIR)+"/incoming.py"); + checkOption("idle_script",string(PKGLIBDIR)+"idle.py"); + checkOption("idle_script_interval","60"); + checkOption("log_file",string(LOCALSTATEDIR)+"/log/capisuite.log"); + checkOption("log_level","2"); + checkOption("log_error",string(LOCALSTATEDIR)+"/log/capisuite.error"); + + string t(config["idle_script_interval"]); + for (int i=0;i'9') + throw ApplicationError("Invalid idle_script_interval given.","main()"); + + if (config["log_file"]!="" and config["log_file"]!="-") { + debug = new ofstream(config["log_file"].c_str(),ios::app); + if (! (*debug)) { + cerr << "Can't open log file. Writing to stdout." << endl; + delete debug; + debug = &cout; + } + } else + debug=&cout; + + t=config["log_level"]; + if (t.size()!=1 && (t[0]<'0' || t[0]>'3')) + throw ApplicationError("Invalid log_level given.","main()"); + + if (config["log_error"]!="" and config["log_error"]!="-") { + error = new ofstream(config["log_error"].c_str(),ios::app); + if (! (*error)) { + cerr << "Can't open error log file. Writing to stderr." << endl; + delete error; + error = &cerr; + } + } else + error=&cerr; + + if (daemonmode) { + if (debug==&cout) { + cerr << "FATAL error: not allowed to write to stdout in daemon mode."; + exit(1); + } + if (error==&cerr) { + cerr << "FATAL error: not allowed to write to stderr in daemon mode."; + exit(1); + } + } +} + + +void +CapiSuite::readCommandline(int argc, char** argv) +{ + struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"config", required_argument, NULL, 'c'}, + {"daemon", no_argument, NULL, 'd'}, + {0, 0, 0, 0} + }; + + int result=0; + do { + result=getopt_long(argc,argv,"hdc:",long_options,NULL); + switch (result) { + case -1: // end + break; + + case 'c': + custom_configfile=optarg; + break; + + case 'd': + daemonmode=true; + break; + case 'h': + default: + help(); + exit(1); + break; + } + } while (result!=-1); +} + +void +CapiSuite::help() +{ + cout << "CapiSuite " << VERSION << " (c) by Gernot Hillier " << endl << endl; + cout << "CapiSuite is an python-scriptable ISDN Telecommunication Suite providing some" << endl; + cout << "ISDN services like fax, voice recording/playing, etc. For further documentation" << endl; + cout << "please have a look at the HTML documents provided with it." << endl << endl; + cout << "syntax: capisuite [-h] [-c file]" << endl << endl; + cout << "-h, --help show this help" << endl; + cout << "-c file, --config=file use a custom configuration file" << endl; + cout << " (default: " << PKGSYSCONFDIR <<"/capisuite.conf)" << endl; + cout << "-d, --daemon run as daemon" << endl; +} + + +/* History + +$Log: capisuite.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.14 2003/02/17 16:49:24 ghillie +- cosmetic... + +Revision 1.13 2003/02/10 14:14:39 ghillie +merged from NATIVE_PTHREADS to HEAD + +Revision 1.12.2.1 2003/02/09 14:58:13 ghillie +- rewritten parseConfigFile to use STL strings instead of CommonC++ strtokenizer +- no need to call detach on thread classes any more (because of their new + usage of native pthreads) + +Revision 1.12 2003/02/05 15:57:57 ghillie +- improved error handling, replaced some outputs to cerr with + correct error stream + +Revision 1.11 2003/01/31 11:25:53 ghillie +- moved capisuiteInstance from header to cpp (mustn't be defined in + each file including capisuite.h, use extern there instead!) + +Revision 1.10 2003/01/27 19:25:14 ghillie +- config moved to etc/capisuite/* + +Revision 1.9 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.8 2003/01/19 12:06:05 ghillie +- support starting as daemon with "-d" (resolving TODO) +- nicer formatting for getInfo output at startup (resolves TODO) +- new methods logMessage() and errorMessage() for use in python scripts + (resolves TODO) + +Revision 1.7 2003/01/18 12:51:08 ghillie +- initialization of cStringIO needed for printing tracebacks to error log + (solved one TODO item), pass on reference to *Script + +Revision 1.6 2003/01/13 21:24:17 ghillie +- corrected typos +- reverted change to config options + +Revision 1.5 2003/01/08 15:59:05 ghillie +- updated for new configuration variables for pathes, new method checkOption() + +Revision 1.4 2003/01/07 14:51:19 ghillie +- added commandline parsing (new methods help(), readCommandLine(): + -h/--help and -c/--config (other configuration file) + +Revision 1.3 2003/01/06 21:00:24 ghillie +- added SIGHUP support (new functions hup_handler, CapiSuite::reload) + +Revision 1.2 2003/01/06 16:20:36 ghillie +- added error check for idle->detach() +- removed superfluous delete idle calls (SEGV) + +Revision 1.1 2003/01/05 12:28:09 ghillie +- renamed FlowControl to CapiSuite +- the code from main() was moved to this class + +Revision 1.23 2003/01/04 15:57:03 ghillie +- added exit handler for SIGINT and SIGTERM +- added static FlowControl pointer +- added finish method for exit handler +- added timestamp in log files +- log_level support + +Revision 1.22 2002/12/14 14:01:06 ghillie +- just terminate() IdleScript instance, don't delete it (will delete self!) + +Revision 1.21 2002/12/11 13:01:59 ghillie +- executeIdleScript() removed, its function is now done by IdleScript + object (changes in constructor and mainLoop()) +- removed getCapi() + +Revision 1.20 2002/12/10 15:52:18 ghillie +- begin changes for moving functionality from executeIdleScript() to + IdleScript class + +Revision 1.19 2002/12/10 15:05:14 ghillie +- changed CallControl to IncomingScript +- added some missing =NULL statements in executeIdleScript() + +Revision 1.18 2002/12/09 15:26:11 ghillie +- callWaiting does add Connection to queue only, real handling moved + to mainLoop() as it may block in error handling +- error output/exception improvements (WARNING -> ERROR severity, ...) +- removed obsolete debug() method + +Revision 1.17 2002/12/07 22:37:56 ghillie +- moved capisuitemodule_init() call from executeIdleScript() to constructor +- get __main__ as it's not returned by capisuitemodule_init() any more +- MEMORY FIX: added missing fclose call + +Revision 1.16 2002/12/05 15:55:10 ghillie +- removed callCompleted() as now exception will be thrown in python script any more +- removed instances attribute + +Revision 1.15 2002/12/05 14:52:31 ghillie +- in executeIdleScript(): removed ugly strncpy, used const_cast() instead to satisfy PyRun_SimpleFile() +- cleaned up python reference counting +- new method getCapi() + +Revision 1.14 2002/12/02 12:28:09 ghillie +- FlowControl constructor now takes 3 additional parameters for the scripts to be used +- added support for regular execution of an idle script in mainLoop(), added executeIdleScript() + +Revision 1.13 2002/11/29 10:20:44 ghillie +- updated docs, use doxygen format now + +Revision 1.12 2002/11/27 15:57:25 ghillie +added missing throw() declaration + +Revision 1.11 2002/11/23 16:40:19 ghillie +removed unnecessary include + +Revision 1.10 2002/11/21 11:35:54 ghillie +- in case of call Completion just call instance->callCompleted() instead of deleting it. + This allows us to continue w/o waiting for the thread to finish. + +Revision 1.9 2002/11/20 17:17:46 ghillie +- FIX: instances weren't erased when exception occurred -> this lead to double used PLCI +- in mainLoop(): changed sleep to nanosleep + +Revision 1.8 2002/11/19 15:57:18 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.7 2002/11/18 14:21:07 ghillie +- moved global severity_t to ApplicationError::severity_t +- added throw() declarations to header files + +Revision 1.6 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.5 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.4 2002/11/10 17:02:54 ghillie +mainLoop prepared for Python execution + +Revision 1.3 2002/11/07 08:19:04 ghillie +some improvements and fixes in Python global lock and thread state handling + +Revision 1.2 2002/10/27 12:47:20 ghillie +- added multithread support for python +- changed callcontrol reference to stay in the python namespace +- changed ApplicationError to support differen severity + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.8 2002/10/24 09:55:52 ghillie +many fixes. Works for one call now + +Revision 1.7 2002/10/23 15:40:15 ghillie +added python integration... + +Revision 1.6 2002/10/09 14:36:22 gernot +added CallModule base class for all call handling modules + +Revision 1.5 2002/10/05 20:43:32 gernot +quick'n'dirty, but WORKS + +Revision 1.4 2002/10/05 13:53:00 gernot +changed to use thread class of CommonC++ instead of the threads-package +some cosmetic improvements (indentation...) + +Revision 1.3 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.2 2002/10/04 13:27:15 gernot +some restructuring to get it to a working state ;-) + +does not do anything useful yet nor does it even compile... + +Revision 1.1 2002/10/02 14:10:07 gernot +first version + +*/ diff --git a/src/application/capisuite.h b/src/application/capisuite.h new file mode 100644 index 0000000..662c117 --- /dev/null +++ b/src/application/capisuite.h @@ -0,0 +1,277 @@ +/** @file capisuite.h + @brief Contains CapiSuite - Main application class, implements ApplicationInterface + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CAPISUITE_H +#define CAPISUITE_H + +#include +#include +#include +#include "../backend/applicationinterface.h" +#include "applicationexception.h" +#include "capisuitemodule.h" +class Capi; +class IdleScript; +class PycStringIO_CAPI; + +/** @brief Main application class, implements ApplicationInterface + + This class realizes the main application and thus implements the + ApplicationInterface. It firstly generates the necessary objects from the CAPI + abstraction layer (one object of class Capi) and enables call listening. + + It contains the mainLoop() and creates handling objects of IncomingScript for + all incoming calls and one object of IdleScript which will start the idle script + at regular intervals. The scripts are informed about disconnection / program termination + but will delete themselves. + + main() should create one CapiSuite object and then call mainLoop(). + + @author Gernot Hillier +*/ +class CapiSuite: public ApplicationInterface +{ + public: + /** @brief Constructor. General initializations. + + Creates a Capi object, enables listening, calls readConfiguration, + and initializes the Python interpreter in multithreading mode. + It immediately releases the global Python lock after doing initialization. + + Also an IdleScript object is created and will regularly call the given script. + + @param argc commandline argument count as given to main() + @param argv commandline arguments as given to main() + */ + CapiSuite(int argc, char** argv); + + /** @brief Destructor. Kill Python interpreter. + + Takes lock and calls CallControl::callCompleted(). + */ + ~CapiSuite(); + + /** @brief Callback: enqueue Connection in waiting + */ + virtual void callWaiting (Connection *conn); + + /** @brief Main Loop. Event Loop (handling incoming connections) + + For each incoming connection, an object of IncomingScript is created + which handles this call in an own thread. + + This loop will run until the program is finished. + */ + void mainLoop(); + + /** @brief Request finish of mainLoop + */ + void finish(); + + /** @brief Parse a given configuration file + + This function reads the given configuration file. It must consist of key=value pairs + separated on different lines. + + Lines beginning with "#" are treated as comments. Leading and trailing whitespaces + and quotation marks (") surrounding the values will be ignored. + */ + void parseConfigFile(ifstream &configfile); + + /** @brief Read configuration and set default values for options not found + + The configuration is read from PREFIX/etc/capisuite.conf (should exist), ~/.capisuite.conf (optional) and perhaps + a given custom config file (optional), while the latter has higher priority. After that all configuration options + are checked and set to default values if not found. + */ + void readConfiguration(); + + /** @brief Read commandline options + */ + void readCommandline(int argc, char**argv); + + /** @brief Print help message + */ + void help(); + + /** @brief restart some aspects if the process gets a SIGHUP + + Currently, this only reactivates the idle script if it was deactivated by too much errors in a row. + */ + void reload(); + + /** @brief print a message to the log + + Prints message to the log if it's level is high enough. + + @param message the message + @param level level of the message + */ + void logMessage(string message, int level); + + /** @brief print a message to the error log + + Prints message to the error log + + @param message the message + */ + void errorMessage(string message); + + private: + /** @brief return a prefix containing this pointer and date for log messages + + @return constructed prefix as stringstream + */ + string prefix(); + + /** @brief Test a configuration variable and set default if undefined + + @param key name of the config variable + @param value default value to set if key is not defined in config map + */ + void checkOption(string key, string value); + + + map instances; ///< saving pointers to all created CallControl instances indexed by Connection pointers + queue waiting; ///< queue for waiting connection instances + IdleScript *idle; ///< reference to the IdleScript object created + + PyThreadState *py_state; ///< saves the created thread state of the main python interpreter + PycStringIO_CAPI* save_cStringIO; ///< holds a pointer to the Python cStringIO C API + Capi* capi; ///< reference to Capi object to use, set in constructor + ostream *debug, ///< debug stream + *error; ///< stream for error messages + + unsigned short debug_level; ///< verbosity level for debug stream + + bool finish_flag; ///< flag to finish mainLoop() + + bool daemonmode; ///< flag set when we're running as daemon + + map config; ///< holds the configuration read from the configfile + string custom_configfile; ///< holds the name of the custom config file if given + +}; + +#endif + +/* History + +$Log: capisuite.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.8 2003/01/31 11:25:53 ghillie +- moved capisuiteInstance from header to cpp (mustn't be defined in + each file including capisuite.h, use extern there instead!) + +Revision 1.7 2003/01/19 12:06:25 ghillie +- new methods logMessage() and errorMessage() + +Revision 1.6 2003/01/18 12:51:48 ghillie +- added save_cStringIO attribute for Python cStringIO C API + +Revision 1.5 2003/01/13 21:24:47 ghillie +- added new method checkOption + +Revision 1.4 2003/01/07 14:52:36 ghillie +- added support for custom config files +- added support for parsing commandline options + +Revision 1.3 2003/01/06 21:00:48 ghillie +- added SIGHUP support (new method reload) + +Revision 1.2 2003/01/06 16:20:51 ghillie +- updated comment + +Revision 1.1 2003/01/05 12:28:09 ghillie +- renamed FlowControl to CapiSuite +- the code from main() was moved to this class + +Revision 1.13 2003/01/04 15:58:38 ghillie +- log improvements: log_level, timestamp +- added finish() method +- added static FlowControl pointer + +Revision 1.12 2002/12/11 13:02:56 ghillie +- executeIdleScript() removed, its function is now done by IdleScript + object (changes in constructor and mainLoop()) +- removed getCapi() +- minor docu bugs fixed + +Revision 1.11 2002/12/09 15:29:13 ghillie +- debug stream given in constructor +- doc update for callWaiting() and mainLoop() +- obsolete debug() method removed + +Revision 1.10 2002/12/06 12:54:30 ghillie +-removed callCompleted() + +Revision 1.9 2002/12/05 14:54:15 ghillie +- constructor gets Capi* now +- new method getCapi() +- python idle script gets called with pointer to FlowControl now + +Revision 1.8 2002/12/02 12:30:30 ghillie +- constructor now takes 3 additional arguments for the scripts to use +- added support for an idle script which is started in regular intervals + +Revision 1.7 2002/11/29 10:20:44 ghillie +- updated docs, use doxygen format now + +Revision 1.6 2002/11/27 15:58:13 ghillie +updated comments for doxygen + +Revision 1.5 2002/11/19 15:57:18 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.4 2002/11/18 14:21:07 ghillie +- moved global severity_t to ApplicationError::severity_t +- added throw() declarations to header files + +Revision 1.3 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.2 2002/10/27 12:47:20 ghillie +- added multithread support for python +- changed callcontrol reference to stay in the python namespace +- changed ApplicationError to support differen severity + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.6 2002/10/24 09:55:52 ghillie +many fixes. Works for one call now + +Revision 1.5 2002/10/23 15:40:15 ghillie +added python integration... + +Revision 1.4 2002/10/09 14:36:22 gernot +added CallModule base class for all call handling modules + +Revision 1.3 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.2 2002/10/04 13:27:15 gernot +some restructuring to get it to a working state ;-) + +does not do anything useful yet nor does it even compile... + +Revision 1.1 2002/10/02 14:10:07 gernot +first version + +*/ diff --git a/src/application/capisuitemodule.cpp b/src/application/capisuitemodule.cpp new file mode 100644 index 0000000..ff2fb7c --- /dev/null +++ b/src/application/capisuitemodule.cpp @@ -0,0 +1,1158 @@ +/* @file capisuitemodule.cpp + @brief Contains the Python module and integration routines + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// IMPORTANT: every python function MUST call PyErr_Occured() before using the associated +// Connection object! (connection can be already deleted while the python script is still running + +#include +#include "../backend/connection.h" +#include "../modules/audiosend.h" +#include "../modules/audioreceive.h" +#include "../modules/faxreceive.h" +#include "../modules/faxsend.h" +#include "../modules/connectmodule.h" +#include "../modules/disconnectmodule.h" +#include "../modules/switch2faxG3.h" +#include "../modules/readDTMF.h" +#include "../modules/calloutgoing.h" +#include "capisuitemodule.h" +#include "capisuite.h" + +#define TEMPORARY_FAILURE 0x34A9 // see ETS 300 102-1, Table 4.13 (cause information element) + +extern CapiSuite* capisuiteInstance; + +static PyObject* CallGoneError=NULL; +static PyObject* BackendError=NULL; + +/** @defgroup python C/Python wrapper functions + @brief These functions define the python commands you can use in your scripts. + + All CapiSuite-commands available in Python will stay in a python + module called capisuite. This module and all its functions are defined here. + + There's a general scheme for mapping the names of the C wrapper functions + to python names: + + Python command "capisuite.command()" will be defined in the wrapper function + "capisuite_command()". + + So you can use this document as reference to all available CapiSuite Python + commands. For example, if you read the documentation for the + capisuite_audio_send function here, you can use it as capisuite.audio_send in + your Python scripts. + +*/ + +void +capisuitemodule_destruct_connection(void* ptr) +{ + Connection *conn=(static_cast(ptr)); + conn->debugMessage("Python: deleting connection object",2); + if (conn->getState()!=Connection::DOWN) { + try { + conn->errorMessage("Warning: Connection still established in capisuitemodule_desctruct_conn(). Disconnecting."); + DisconnectModule active(conn,TEMPORARY_FAILURE,true); + active.mainLoop(); + } + catch (CapiError e) { + conn->errorMessage("ERROR: disconnection also failed. Too bad..."); + } + } + delete conn; +} + +/** @brief Private converter function to extract the contained Connection* from a PyCObject + + This function is defined for the use in PyArg_ParseTuple() calls. + + @param conn_ref - PyCObject pointer + @param conn address of the Connection pointer where the result will be stored + @return 1=successful, 0=error +*/ +bool +convertConnRef(PyObject *conn_ref, Connection** conn) +{ + if (!PyCObject_Check(conn_ref)) { + PyErr_SetString(PyExc_TypeError,"First parameter must be the call reference."); + return 0; + } + + if (! ( *conn=static_cast(PyCObject_AsVoidPtr(conn_ref)) ) ) { + PyErr_SetString(PyExc_TypeError,"Call reference is NULL. This is not allowed."); + return 0; + } + return 1; +} + +/** @brief Private converter function to extract the contained Capi* from a PyCObject + + This function is defined for the use in PyArg_ParseTuple() calls. + + @param capi_ref - PyCObject pointer + @param capi address of the Capi pointer where the result will be stored + @return 1=successful, 0=error +*/ +bool +convertCapiRef(PyObject *capi_ref, Capi** capi) +{ + if (!PyCObject_Check(capi_ref)) { + PyErr_SetString(PyExc_TypeError,"First parameter must be the Capi reference."); + return 0; + } + + if (! ( *capi=static_cast(PyCObject_AsVoidPtr(capi_ref)) ) ) { + PyErr_SetString(PyExc_TypeError,"Capi reference is NULL. This is not allowed."); + return 0; + } + return 1; +} + +/** @brief Write an informational message to the CapiSuite log. + @ingroup python + + This function writes a message to the CapiSuite log. It's helpful if you want to write + messages in the debug log in your scripts. + + The message can be either logged with the general CapiSuite prefix if they are of global + nature or with the Connection prefix if they're associated with a special connection. + + @param args Contains the python parameters. These are: + - message (string) the log message + - level (integer) parameter for log_level + - call (optional) call reference - if given, the message is logged with Connection prefix + @return None +*/ +static PyObject* +capisuite_log(PyObject*, PyObject *args) +{ + + Connection* conn=NULL; + char *message; + int level; + + if (!PyArg_ParseTuple(args,"si|O&:log",&message,&level,convertConnRef,&conn)) + return NULL; + + if (conn) + conn->debugMessage(message,level); + else if (capisuiteInstance) + capisuiteInstance->logMessage(message,level); + + Py_XINCREF(Py_None); + return (Py_None); +} + +/** @brief Write an error message to the CapiSuite error log. + @ingroup python + + This function writes a message to the CapiSuite error log. It + should be used to output error messages so they appear in the + normal error log. + + @param args Contains the python parameter: + - message (string) the log message + @return None +*/ +static PyObject* +capisuite_error(PyObject*, PyObject *args) +{ + + Connection* conn=NULL; + char *message; + + if (!PyArg_ParseTuple(args,"s|O&:error",&message,convertConnRef,&conn)) + return NULL; + + if (conn) + conn->errorMessage(message); + else if (capisuiteInstance) + capisuiteInstance->errorMessage(message); + + Py_XINCREF(Py_None); + return (Py_None); +} + +/** @brief Send an audio file in a speech mode connection. + @ingroup python + + This function sends an audio file. The audio file must be in bit-inversed A-Law format. It can be created for example + with sox using the suffix ".la". It supports abortion if DTMF signal is received. + + If DTMF abort is enabled, the command will also abort immediately if DTMF was received before it is called. That allows + you to abort subsequent audio receive and send commands with one DTMF signal w/o needing to check for received DTMF + after each command. + + The connction must be in audio mode (use capisuite_connect_voice()), otherwise an exception will be caused. + + @param args Contains the python parameters. These are: + - call Reference to the current call + - filename (string) file to send + - exit_DTMF (integer, optional) if set to 1, sending is aborted when a DTMF signal is received (0=off, default) + @return int containing duration of send in seconds +*/ +static PyObject* +capisuite_audio_send(PyObject*, PyObject *args) +{ + Connection* conn; + char *filename; + PyThreadState *_save; + int exit_DTMF=0; + long duration=0; + + if (!PyArg_ParseTuple(args,"O&s|i:audio_send",convertConnRef,&conn,&filename,&exit_DTMF)) + return NULL; + + try { + Py_UNBLOCK_THREADS + AudioSend active(conn,filename,exit_DTMF); + active.mainLoop(); + duration=active.duration(); + Py_BLOCK_THREADS + } + catch (CapiWrongState e) { + Py_BLOCK_THREADS + PyErr_SetString(CallGoneError,"Call was finished from partner."); + return NULL; + } + catch (CapiMsgError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + catch (CapiExternalError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + + PyObject *r=PyInt_FromLong(duration); + return (r); +} + +/** @brief Receive an audio file in a speech mode connection. + @ingroup python + + This functions receives an audio file. It can recognize silence in the signal and timeout after + a given period of silence, after a general timeout or after the reception of a DTMF signal. + + If the recording was finished because of silence_timeout, the silence will be truncated away. + + If DTMF abort is enabled, the command will also abort immediately if DTMF was received before it is called. That allows + you to abort subsequent audio receive and send commands with one DTMF signal w/o needing to check for received DTMF + after each command. + + The connction must be in audio mode (use capisuite_connect_voice()), otherwise an exception will be caused. + + The created file will be saved in bit-reversed A-Law format, 8 kHz mono. Use sox to convert it to a normal wav file. + + @param args Contains the python parameters. These are: + - call Reference to the current call + - filename (string) where to save received file + - timeout (integer) receive length in seconds (-1 = infinite) + - silence_timeout (integer, optional) abort after x seconds of silence (0=off, default) + - exit_DTMF (integer, optional) if set to 1, sending is aborted when a DTMF signal is received (0=off, default) + @return int containing duration of receive in seconds +*/ +static PyObject* +capisuite_audio_receive(PyObject *, PyObject *args) +{ + Connection* conn; + char *filename; + int timeout, silence_timeout=0; + PyThreadState *_save; + int exit_DTMF=0; + long duration=0; + + if (!PyArg_ParseTuple(args,"O&si|ii:audio_receive",convertConnRef,&conn,&filename, &timeout, &silence_timeout,&exit_DTMF)) + return NULL; + + try { + Py_UNBLOCK_THREADS + AudioReceive active(conn,filename,timeout,silence_timeout,exit_DTMF); + active.mainLoop(); + duration=active.duration(); + Py_BLOCK_THREADS + } + catch (CapiWrongState e) { + Py_BLOCK_THREADS + PyErr_SetString(CallGoneError,"Call was finished from partner."); + return NULL; + } + catch (CapiExternalError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + + PyObject *r=PyInt_FromLong(duration); + return (r); +} + +/** @brief Receive a fax in a fax mode connection + @ingroup python + + This command receives an analog fax (fax group 3). It starts the reception and waits for the end of the connection. + So it should be the last command before capisuite_disconnect. + + The connction must be in fax mode (use capisuite_connect_faxG3 or capisuite_switch_to_faxG3), otherwise an exception will be caused. + + The created file will be saved in the Structured Fax File (SFF) format. + + @param args Contains the python parameters. These are: + - call Reference to the current call + - filename (string) where to save received fax + @return None +*/ +static PyObject* +capisuite_fax_receive(PyObject *, PyObject *args) +{ + Connection *conn; + char *filename; + PyThreadState *_save; + + if (!PyArg_ParseTuple(args,"O&s:fax_receive",convertConnRef,&conn,&filename)) + return NULL; + + try { + Py_UNBLOCK_THREADS + FaxReceive active(conn,filename); + active.mainLoop(); + Py_BLOCK_THREADS + } + catch (CapiWrongState e) { + Py_BLOCK_THREADS + PyErr_SetString(CallGoneError,"Call was finished from partner."); + return NULL; + } + catch (CapiExternalError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + + Py_XINCREF(Py_None); + return (Py_None); +} + +/** @brief Send a fax in a fax mode connection + @ingroup python + + This command sends an analog fax (fax group 3). It starts the send and waits for the end of the connection. + So it should be the last command before capisuite_disconnect. + + The connction must be in fax mode (use capisuite_call_faxG3 or capisuite_switch_to_faxG3), otherwise an exception will be caused. + + The created file will be saved in the Structured Fax File (SFF) format. + + @param args Contains the python parameters. These are: + - call Reference to the current call + - filename (string) file to send + @return None +*/ +static PyObject* +capisuite_fax_send(PyObject *, PyObject *args) +{ + Connection *conn; + char *filename; + PyThreadState *_save; + + if (!PyArg_ParseTuple(args,"O&s:fax_send",convertConnRef,&conn,&filename)) + return NULL; + + try { + Py_UNBLOCK_THREADS + FaxSend active(conn,filename); + active.mainLoop(); + Py_BLOCK_THREADS + } + catch (CapiWrongState e) { + Py_BLOCK_THREADS + PyErr_SetString(CallGoneError,"Call was finished from partner."); + return NULL; + } + catch (CapiError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + + Py_XINCREF(Py_None); + return (Py_None); +} + +/** @brief Disconnect connection. + @ingroup python + + This will cause an immediate disconnection. It should be always the last command in every flow of a script. + It will return a tuple of two result values. The first is the disconnect cause of the physical connection, + the second the disconnect cause of the logical connection. See CAPI spec for the logical causes and + ETS 300 102-01 for the physical causes. + + @param args Contains the python parameters. These are: + - call Reference to the current call + @return Tuple containing (ReasonPhysical,ReasonLogical) +*/ +static PyObject* +capisuite_disconnect(PyObject *, PyObject *args) +{ + Connection *conn; + PyThreadState *_save; + + if (!PyArg_ParseTuple(args,"O&:disconnect",convertConnRef,&conn)) + return NULL; + + try { + Py_UNBLOCK_THREADS + DisconnectModule active(conn); + active.mainLoop(); + Py_BLOCK_THREADS + } + catch (CapiMsgError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + catch (CapiExternalError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + + PyObject *r=Py_BuildValue("ii",conn->getCause(),conn->getCauseB3()); + return (r); +} + +/** @brief Reject an incoming call. + @ingroup python + + If you don't want to accept an incoming call for any reason (e.g. if it has a service or comes from a number + you don't want to accept), use this command. There are several reasons you can give when rejecting a call. + Some important ones are: + - 1=ignore call + - 2=normal call clearing + - 3=user busy + - 7=incompatible destination + - 8=destination out of order + - 0x34A9=temporary failure + + You can find many more reasons in the ETS 300 201-01 specification or on the web (search for ISDN cause) + if you really need them. + + @param args Contains the python parameters. These are: + - call Reference to the current call + - rejectCause (integer) which cause to signal when rejecting call (see above) + @return None +*/ +static PyObject* +capisuite_reject(PyObject *, PyObject *args) +{ + Connection *conn; + int rejectCause; + PyThreadState *_save; + + if (!PyArg_ParseTuple(args,"O&i:reject",convertConnRef,&conn,&rejectCause) ) + return NULL; + + try { + Py_UNBLOCK_THREADS + DisconnectModule active(conn,rejectCause); + active.mainLoop(); + Py_BLOCK_THREADS + } + catch (CapiMsgError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + catch (CapiExternalError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + + Py_XINCREF(Py_None); + return (Py_None); +} + + +/** @brief helper function for capisuite_connect_voice() and capisuite_connect_faxG3() + + @param delay delay in seconds before connection will be established + @param service with which service we should connect + @param faxStationID only used for fax connections + @param faxHeadline only used for fax connections +*/ +static PyObject* +capisuite_connect(Connection *conn, int delay, Connection::service_t service, string faxStationID, string faxHeadline) +{ + PyThreadState *_save; + try { + Py_UNBLOCK_THREADS + if (delay) { + conn->acceptWaiting(); // so that connection doesn't timeout + sleep(delay); + } + ConnectModule active(conn,service, faxStationID, faxHeadline); + active.mainLoop(); + Py_BLOCK_THREADS + } + catch (CapiMsgError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + catch (CapiWrongState e) { + Py_BLOCK_THREADS + PyErr_SetString(CallGoneError,"Call was finished from partner."); + return NULL; + } + catch (CapiExternalError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + Py_XINCREF(Py_None); + return (Py_None); +} + +/** @brief Accept an incoming call and connect with voice service. + @ingroup python + + This will accept an incoming call and choose voice as service, so you can use the audio commands + (like audio_receive and audio_send) with this connection. After this command has finished, the call + is connected successfully. + + It's also possible to accept a call with some delay. This is for example useful for an answering + machine if you want to have the chance to get a call with your phone before your computer answers it. + + @param args Contains the python parameters. These are: + - call Reference to the current call + - delay (integer, optional) delay in seconds _before_ connection will be established (default: 0=immediate connect) + @return None +*/ +static PyObject* +capisuite_connect_voice(PyObject *, PyObject *args) +{ + Connection *conn; + int delay=0; + + if (!PyArg_ParseTuple(args,"O&|i:connect_voice",convertConnRef,&conn,&delay)) + return NULL; + + return capisuite_connect(conn,delay,Connection::VOICE,"",""); +} + +/** @brief Accept an incoming call and connect with fax (analog, group 3) service. + @ingroup python + + This will accept an incoming call and choose fax group 3 as service, so you can use the fax commands + (like fax_receive) with this connection. After this command has finished, the call is connected successfully. + + It's also possible to accept a call with some delay. This is for example useful if you want to have the chance + to get a call with your phone before your computer answers it. + + @param args Contains the python parameters. These are: + - call Reference to the current call + - faxStationID (string) the station ID to use + - faxHeadline (string) the fax headline to use + - delay (integer, optional) delay in seconds _before_ connection will be established (default: 0=immediate connect) + @return None +*/ +static PyObject* +capisuite_connect_faxG3(PyObject *, PyObject *args) +{ + Connection *conn; + int delay=0; + char *faxStationID, *faxHeadline; + + if (!PyArg_ParseTuple(args,"O&ss|i:connect_faxG3",convertConnRef,&conn,&faxStationID,&faxHeadline,&delay)) + return NULL; + + return capisuite_connect(conn,delay,Connection::FAXG3,faxStationID,faxHeadline); +} + +/** @brief helper function for capisuite_call_voice() and capisuite_call_faxG3() + + @param capi reference to object of Capi to use + @param controller controller number to use + @param call_from string containing the own number to use + @param call_to string containing the number to call + @param service service to call with as described in Connection::service_t + @param timeout timeout to wait for connection establishment + @param faxStationID fax station ID, only necessary when connecting in FAXG3 mode + @param faxHeadline fax headline, only necessary when connecting in FAXG3 mode + @param clir set to true to disable sending of own number + @return tuple (call,result) - call=reference to the created call object / result(int)=result of the call establishment +*/ +static PyObject* +capisuite_call(Capi *capi, unsigned short controller, string call_from, string call_to, Connection::service_t service, int timeout, string faxStationID, string faxHeadline, bool clir) +{ + PyThreadState *_save; + Connection* conn=NULL; + int result; + try { + Py_UNBLOCK_THREADS + CallOutgoing active(capi,controller,call_from,call_to,service,timeout,faxStationID,faxHeadline,clir); + active.mainLoop(); + conn=active.getConnection(); + result=active.getResult(); + Py_BLOCK_THREADS + } + catch (CapiMsgError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + catch (CapiExternalError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + + PyObject *r=Py_BuildValue("Ni",PyCObject_FromVoidPtr(conn,capisuitemodule_destruct_connection),result); + return (r); +} + +/** @brief Initiate an outgoing call with service voice and wait for successful connection + @ingroup python + + This will initiate an outgoing call and choose voice as service, so you can use the audio commands + (like audio_receive and audio_send) with this connection. After this command has finished, the call + is connected successfully or the given timeout has exceeded. See the return value. + + A python tuple (call,result) is returned by this function. It contains of 2 values: + - call reference to the created call object - use this for subsequent calls like audio_send + - result (int) result of the call establishment process. + - 0 = connection established + - 1 = connection timeout exceeded, no connection was established + - 2 = connection wasn't successful and no reason for this failure is available + - 0x3301-0x34FF: Error reported by CAPI. For a complete description see the CAPI specification + and ETS 300102-1 from http://www.etsi.org or search for "ISDN cause" in Google ;-). + Here are some important values: + - 0x3301 Protocol error, Layer 1 + - 0x3302 Protocol error, Layer 2 + - 0x3303 Protocol error, Layer 3 + - 0x3480 normal call termination + - 0x3481 Unallocated (unassigned) number + - 0x3490 Normal call clearing + - 0x3491 User busy + - 0x3492 No user responding + - 0x3493 No answer from user (user alerted) + - 0x3495 Call rejected + - 0x3496 Number changed + - 0x349B Destination out of order + - 0x349C Invalid number format + - 0x34A2 No circuit / channel available + - 0x34A9 Temporary failure + - 0x34D8 Incompatible destination + + @param args Contains the python parameters. These are: + - capi reference to object of Capi to use (given to the idle function as parameter) + - controller (int) ISDN controller ID to use + - call_from (string)own number to use + - call_to (string)the number to call + - timeout (int)timeout to wait for connection establishment in seconds + - clir (int, optional)set to 1 to disable sending of own number (0=default) + @return tuple (call,result) - see above. +*/ +static PyObject* +capisuite_call_voice(PyObject *, PyObject *args) +{ + Capi *capi; + int controller, timeout, clir=0; + char *call_from,*call_to; + + if (!PyArg_ParseTuple(args,"O&issi|i:call_voice",convertCapiRef,&capi,&controller,&call_from,&call_to,&timeout,&clir)) + return NULL; + + return capisuite_call(capi,controller,call_from,call_to,Connection::VOICE,timeout,"","",clir); +} + +/** @brief Initiate an outgoing call with service faxG3 and wait for successful connection + @ingroup python + + This will initiate an outgoing call and choose fax group 3 as service, so you can use the fax commands + (like fax_send and fax_receive) with this connection. After this command has finished, the call + is connected successfully or the given timeout has exceeded. See the return value. + + A python tuple (call,result) is returned by this function. It contains of 2 values: + - call reference to the created call object - use this for subsequent calls like audio_send + - result (int) result of the call establishment process. + - 0 = connection established + - 1 = connection timeout exceeded, no connection was established + - 2 = connection wasn't successful and no reason for this failure is available + - 0x3301-0x34FF: Error reported by CAPI. For a complete description see the CAPI specification + and ETS 300102-1 from http://www.etsi.org or search for "ISDN cause" in Google ;-). + Here are some important values: + - 0x3301 Protocol error, Layer 1 + - 0x3302 Protocol error, Layer 2 + - 0x3303 Protocol error, Layer 3 + - 0x3480 normal call termination + - 0x3481 Unallocated (unassigned) number + - 0x3490 Normal call clearing + - 0x3491 User busy + - 0x3492 No user responding + - 0x3493 No answer from user (user alerted) + - 0x3495 Call rejected + - 0x3496 Number changed + - 0x349B Destination out of order + - 0x349C Invalid number format + - 0x34A2 No circuit / channel available + - 0x34A9 Temporary failure + - 0x34D8 Incompatible destination + + @param args Contains the python parameters. These are: + - capi reference to object of Capi to use (given to the idle function as parameter) + - controller (int) ISDN controller ID to use + - call_from (string)own number to use + - call_to (string)the number to call + - timeout (int)timeout to wait for connection establishment in seconds + - faxStationID (string)fax station ID + - faxHeadline (string) fax headline to print on every page + - clir (int, optional)set to 1 to disable sending of own number (0=default) + @return tuple (call,result) - see above. +*/ +static PyObject* +capisuite_call_faxG3(PyObject *, PyObject *args) +{ + Capi *capi; + int controller, timeout, clir=0; + char *call_from,*call_to,*faxStationID,*faxHeadline; + + if (!PyArg_ParseTuple(args,"O&ississ|i:call_faxG3",convertCapiRef,&capi,&controller,&call_from,&call_to,&timeout,&faxStationID,&faxHeadline,&clir)) + return NULL; + + return capisuite_call(capi,controller,call_from,call_to,Connection::FAXG3,timeout,faxStationID,faxHeadline,clir); +} + +/** @brief Switch a connection from voice mode to fax mode. + @ingroup python + + This will switch from voice mode to fax group 3 after you have connected, so you can use the fax commands afterwards. + + Attention: This command isn't supported by every ISDN card / CAPI driver! + + @param args Contains the python parameters. These are: + - call Reference to the current call + - faxStationID (string) the station ID to use + - faxHeadline (string) the fax headline to use + @return None +*/ +static PyObject* +capisuite_switch_to_faxG3(PyObject *, PyObject *args) +{ + Connection *conn; + char *faxStationID, *faxHeadline; + PyThreadState *_save; + + if (!PyArg_ParseTuple(args,"O&ss:switch_to_faxG3",convertConnRef,&conn,&faxStationID,&faxHeadline)) + return NULL; + + try { + Py_UNBLOCK_THREADS + Switch2FaxG3 active(conn,faxStationID,faxHeadline); + active.mainLoop(); + Py_BLOCK_THREADS + } + catch (CapiWrongState e) { + Py_BLOCK_THREADS + PyErr_SetString(CallGoneError,"Call was finished from partner."); + return NULL; + } + catch (CapiExternalError e) { + Py_BLOCK_THREADS + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + + Py_XINCREF(Py_None); + return (Py_None); +} + +/** @brief Enable recognition of DTMF tones. + @ingroup python + + You have to enable the recognition of DTMF tones if you want to use them in your script. + + @param args Contains the python parameters. These are: + - call Reference to the current call + @return None +*/ +static PyObject* +capisuite_enable_DTMF(PyObject *, PyObject *args) +{ + Connection *conn; + + if (!PyArg_ParseTuple(args,"O&:enable_DTMF",convertConnRef,&conn) ) + return NULL; + + try { + conn->enableDTMF(); + } + catch (CapiWrongState) { // issued when we have no connection + PyErr_SetString(CallGoneError,"Call was finished from partner."); + return NULL; + } + catch (CapiMsgError e) { + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + + Py_XINCREF(Py_None); + return (Py_None); +} + +/** @brief Disable recognition of DTMF tones. + @ingroup python + + You can disable the recognition of DTMF tones again if you want to. + + @param args Contains the python parameters. These are: + - call Reference to the current call + @return None +*/ +static PyObject* +capisuite_disable_DTMF(PyObject *, PyObject *args) +{ + Connection *conn; + + if (!PyArg_ParseTuple(args,"O&:disable_DTMF",convertConnRef,&conn) ) + return NULL; + + try { + conn->disableDTMF(); + } + catch (CapiWrongState) { // issued when we have no connection + PyErr_SetString(CallGoneError,"Call was finished from partner."); + return NULL; + } + catch (CapiMsgError e) { + PyErr_SetString(BackendError,(e.message()).c_str()); + return NULL; + } + + Py_XINCREF(Py_None); + return (Py_None); +} + +/** @brief Read the received DTMF tones or wait for a certain amount of them. + @ingroup python + + This function allows to just read in the DTMF tones which were already received. But it also supports to + wait for a certain amount of DTMF tones if you want the user to always input some digits at a certain + step in your script. + + You can specify how much DTMF tones you want in several ways - see the parameter description. To just + see if something was entered before, use capisuite.read_DTMF(0). If you want to get at least 1 and mostly 4 digits + and want to wait 5 seconds for additional digits, you'll use capisuite.read_DTMF(5,1,4). + + Valid DTMF characters are '0'...'9','A'...'D' and two special fax tones: 'X' (CNG), 'Y' (CED) + + @param args Contains the python parameters. These are: + - call Reference to the current call + - timeout (integer) timeout in seconds after which reading is terminated, only applied when min_digits are reached! (-1 = infinite) + - min_digits (integer, optional) minimum number of digits which must be read in ANY case, i.e. timout doesn't count here (default: 0) + - max_digits (integer, optional) maximum number of digits to read (aborts immediately if this number is reached) (0=infinite, i.e. only timeout counts, default) + @return python string containing the received DTMF characters +*/ +static PyObject* +capisuite_read_DTMF(PyObject *, PyObject *args) +{ + Connection *conn; + PyThreadState *_save; + int timeout, min_digits=0, max_digits=0; + + if (!PyArg_ParseTuple(args,"O&i|ii:read_DTMF",convertConnRef,&conn, &timeout, &min_digits, &max_digits) ) + return NULL; + + string dtmf_received; + try { + Py_UNBLOCK_THREADS + ReadDTMF active(conn,timeout,min_digits,max_digits); + active.mainLoop(); + dtmf_received=conn->getDTMF(); + conn->clearDTMF(); + Py_BLOCK_THREADS + } + catch (CapiWrongState e) { + Py_BLOCK_THREADS + PyErr_SetString(CallGoneError,"Call was finished from partner."); + return NULL; + } + + PyObject* result=Py_BuildValue("s",dtmf_received.c_str()); + return (result); +} + + +/** PCallControlMethods - array of functions in module capisuite +*/ +static PyMethodDef PCallControlMethods[] = { + {"audio_receive", capisuite_audio_receive, METH_VARARGS, "Receive audio. For further details see capisuite module reference."}, + {"audio_send", capisuite_audio_send, METH_VARARGS, "Send audio. For further details see capisuite module reference."}, + {"fax_receive", capisuite_fax_receive, METH_VARARGS, "Receive fax. For further details see capisuite module reference."}, + {"fax_send", capisuite_fax_send, METH_VARARGS, "Send fax. For further details see capisuite module reference."}, + {"disconnect", capisuite_disconnect, METH_VARARGS, "Disconnect call. For further details see capisuite module reference."}, + {"connect_voice", capisuite_connect_voice, METH_VARARGS, "Connect pending call with Telephony services. Arguments: call, delay"}, + {"connect_faxG3", capisuite_connect_faxG3, METH_VARARGS, "Connect pending call with FaxG3 services. For further details see capisuite module reference."}, + {"call_voice", capisuite_call_voice, METH_VARARGS, "Initiate an outgoing call with service voice. For further details see capisuite module reference."}, + {"call_faxG3", capisuite_call_faxG3, METH_VARARGS, "Initiate an outgoing call with service FaxG3. For further details see capisuite module reference."}, + {"switch_to_faxG3", capisuite_switch_to_faxG3, METH_VARARGS, "Switch from telephony to FaxG3 services. For further details see capisuite module reference."}, + {"reject", capisuite_reject, METH_VARARGS, "Reject waiting call. For further details see capisuite module reference."}, + {"enable_DTMF", capisuite_enable_DTMF, METH_VARARGS, "Enable DTMF recognition. For further details see capisuite module reference."}, + {"disable_DTMF", capisuite_disable_DTMF, METH_VARARGS, "Disable DTMF recognition. For further details see capisuite module reference."}, + {"read_DTMF", capisuite_read_DTMF, METH_VARARGS, "Read and clear received DTMF. For further details see capisuite module reference."}, + {"log", capisuite_log, METH_VARARGS, "Write log message. For further details see capisuite module reference."}, + {"error", capisuite_error, METH_VARARGS, "Write error message. For further details see capisuite module reference."}, + {NULL,NULL,0,NULL} +}; + +void +capisuitemodule_init () throw (ApplicationError) +{ + PyObject *mod,*d; + try { + if ( ! ( mod=Py_InitModule3("capisuite", PCallControlMethods, "Python module for controlling CapiSuite") ) ) // m=borrowed ref + throw ApplicationError("unable to init python module capisuite (InitModule failed)","capisuite_init()"); + + if ( ! ( d=PyModule_GetDict(mod) ) ) // d=borrowed ref + throw ApplicationError("unable to init python module capisuite (GetDict(mod) failed)","capisuite_init()"); + + if (!CallGoneError) + if (! (CallGoneError=PyErr_NewException("capisuite.CallGoneError", NULL, NULL) ) ) + throw ApplicationError("unable to init python module capisuite (NewException for CallGoneError failed)","capisuite_init()"); + if (!BackendError) + if (! (BackendError=PyErr_NewException("capisuite.BackendError", NULL, NULL) ) ) + throw ApplicationError("unable to init python module capisuite (NewException for BackendError failed)","capisuite_init()"); + + if (PyDict_SetItemString(d, "CallGoneError", CallGoneError)!=0) + throw ApplicationError("unable to init python module capisuite (SetItemString for CallGoneError failed)","capisuite_init()"); + if (PyDict_SetItemString(d, "BackendError", BackendError)!=0) + throw ApplicationError("unable to init python module capisuite (SetItemString for BackendError failed)","capisuite_init()"); + + if (PyModule_AddIntConstant(mod, "SERVICE_VOICE", Connection::VOICE) != 0) + throw ApplicationError("unable to init python module capisuite (adding constant SERVICE_VOICE failed)","capisuite_init()"); + if (PyModule_AddIntConstant(mod, "SERVICE_FAXG3", Connection::FAXG3) != 0) + throw ApplicationError("unable to init python module capisuite (adding constant SERVICE_FAXG3 failed)","capisuite_init()"); + if (PyModule_AddIntConstant(mod, "SERVICE_OTHER", Connection::OTHER) != 0) + throw ApplicationError("unable to init python module capisuite (adding constant SERVICE_OTHER failed)","capisuite_init()"); + } + catch(ApplicationError) { + if (CallGoneError) + Py_DECREF(CallGoneError); + if (BackendError) + Py_DECREF(BackendError); + throw; + } +} + + +/* History + +$Log: capisuitemodule.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.21 2003/01/31 11:26:46 ghillie +- add "extern capisuiteInstance" +- add two missing Py_BLOCK_THREADS in capisuite_reject() + +Revision 1.20 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.19 2003/01/19 12:07:47 ghillie +- new functions capisuite_log and capisuite_error (resolves TODO) + +Revision 1.18 2003/01/16 13:01:21 ghillie +- updated comment of audio_receive: truncates silence now + +Revision 1.17 2003/01/04 15:54:58 ghillie +- use new *Message functions of Connection + +Revision 1.16 2002/12/16 13:12:06 ghillie +- destruct_connection now uses correct debug stream +- changed disconnect() to return cause and causeB3 + +Revision 1.15 2002/12/13 11:44:34 ghillie +added support for fax send + +Revision 1.14 2002/12/13 09:57:10 ghillie +- error message formatting done by exception classes now + +Revision 1.13 2002/12/11 13:37:25 ghillie +- use quick_disconnect in destruct when connection is still established + because this is the result of some error + +Revision 1.12 2002/12/11 13:00:19 ghillie +- modified capisuitemodule_call_* and convertFlowControlRef (now named + convertCapiRef) to use Capi ptr instead of FlowControl ptr + +Revision 1.11 2002/12/10 15:03:53 ghillie +- capisuitemodule_destruct_connection does nice disconnection now + +Revision 1.10 2002/12/07 22:35:46 ghillie +- capisuite_connect(): removed PyErr_Occured() check +- capisuitemodule_init: doesn't return __main__ namespace any more +- FIX in capisuitemodule_init: don't double create exceptions any more! +- capisuitemodule_init: use shorter PyModule_AddIntConstant() instead of + manual creation and registration + +Revision 1.9 2002/12/06 15:24:25 ghillie +- reject uses DisconnectModule, too, now and waits for disconnection + +Revision 1.8 2002/12/06 12:53:08 ghillie +- added destruction function for Connection objects +- removed capisuitemodule_call_gone() +- changed capisuite_disconnect(): use DisconnectModule, wait for disconnect, + return ISDN disconnect cause +- fixed some typos in PyArg_ParseTuple() calls +- changed capisuite_call* to return a tuple (call,result) + +Revision 1.7 2002/12/05 15:54:22 ghillie +- removed checks for PyErr_Occured() as exceptions won't be thrown in from outside any more + +Revision 1.6 2002/12/05 14:49:47 ghillie +- added convertFlowControlRef() +- new python functions: call_voice and call_faxG3 for initiating outgoing calls + +Revision 1.5 2002/12/04 10:38:14 ghillie +- audio_send and audio_receive do return duration now + +Revision 1.4 2002/12/02 16:53:23 ghillie +- some minor fixes in the docu strings +- typo (SERVICE_SPEECH->SERVICE_VOICE) + +Revision 1.3 2002/12/02 12:26:31 ghillie +- renamed Connection::SPEECH to Connection::VOICE +- 3 new constants defined in init function: SERVICE_VOICE, SERVICE_FAXG3, SERVICE_OTHER +- moved DECREF's to exception handler where appropriate + +Revision 1.2 2002/11/29 11:37:39 ghillie +- missed some changes from CapiCom to CapiSuite + +Revision 1.1 2002/11/29 11:06:22 ghillie +renamed CapiCom to CapiSuite (name conflict with MS crypto API :-( ) + +Revision 1.5 2002/11/29 10:20:44 ghillie +- updated docs, use doxygen format now + +Revision 1.4 2002/11/27 15:56:56 ghillie +renamed connect_telephony to connect_voice + +Revision 1.3 2002/11/25 11:48:48 ghillie +- improved parameter descriptions for python functions +- made more python parameter optional +- removed CIPvalue from application layer, use service type instead +- renamed get_DTMF() to read_DTMF(), added additional parameters (timeout, min_/max_digits), uses ReadDTMF-module now + +Revision 1.2 2002/11/23 15:55:51 ghillie +changed some misleading function names + +Revision 1.1 2002/11/22 15:44:54 ghillie +renamed pcallcontrol.* to capicommodule.* + +Revision 1.18 2002/11/22 15:07:09 ghillie +- new python function switch_to_faxG3 +- new parameter exit_DTMF for audio_send() and audio_receive() +- new parameters faxStationID and faxHeadline for connect_faxG3 +- get_DTMF calls conn->clearDTMF() now + +Revision 1.17 2002/11/21 15:26:19 ghillie +- removed some unnecessary commands (we don't need result of mainLoop() any more, no need to use pointers for call modules any more) +- removed python command connect(call,CIP), added connect_telephony() and connect_faxG3() +- connect_telephony() and connect_faxG3 take delay in seconds before connect as parameter now +- unified python function names (enableDTMF -> enable_DTMF, disableDTMF -> disable_DTMF, getDTMF -> get_DTMF + +Revision 1.16 2002/11/20 17:23:04 ghillie +- fixed locking in the case of an exception. Python locks weren't set-up again when an exception occurred +- removed unnecessary conn->isUp() calls (handled via exceptions now) +- CapiWrongState exception causes CallGoneError to be issued in Python now +- removed unnecessary PyErr_Occured() calls at the end of each function +- added missing Py_XDECREF in _init() + +Revision 1.15 2002/11/19 15:57:18 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.14 2002/11/18 14:21:07 ghillie +- moved global severity_t to ApplicationError::severity_t +- added throw() declarations to header files + +Revision 1.13 2002/11/18 12:08:46 ghillie +return reference to GetDict(__main__) instead of GetDict(pcallcontrol), so that callIncoming can live in __main__ + +Revision 1.12 2002/11/17 14:36:32 ghillie +improved error handling: script will now terminate if B3 connection is lost +(each function checks for PyErr_Occurred() and conn->isUp() now) + +Revision 1.11 2002/11/14 17:04:05 ghillie +* major structural changes - much is easier, nicer and better prepared for the future now: + - added DisconnectLogical handler to CallInterface + - DTMF handling moved from CallControl to Connection + - new call module ConnectModule for establishing connection + - python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) + - call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +* converter function for PyCObject -> Connection pointer introduced + -> this saves us many identical code lines in all Python/C integration + functions + +Revision 1.10 2002/11/13 15:23:21 ghillie +added new parameter to audio_receive (silence_timeout) +fixed deadlock: in deletion of call modules must happen w/o holding python global lock + +Revision 1.9 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.8 2002/11/10 17:03:45 ghillie +now CallControl reference is passed directly to the called Pyhton functions + +Revision 1.7 2002/11/06 16:16:07 ghillie +added code to raise CallGoneError in any case so the script is cancelled when the call is gone surely + +Revision 1.6 2002/10/31 12:35:58 ghillie +added DTMF support + +Revision 1.5 2002/10/30 16:05:20 ghillie +cosmetic fixes... + +Revision 1.4 2002/10/30 14:25:54 ghillie +added connect,disconnect,reject functions, changed init function to return the module dictionary + +Revision 1.3 2002/10/29 14:07:38 ghillie +changed to implecete pass call parameter so the user doesn't have to type it in the script... + +Revision 1.2 2002/10/27 12:47:20 ghillie +- added multithread support for python +- changed callcontrol reference to stay in the python namespace +- changed ApplicationError to support differen severity + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.3 2002/10/24 09:55:52 ghillie +many fixes. Works for one call now + +Revision 1.2 2002/10/23 15:42:11 ghillie +- added standard headers +- changed initialization code (object references now set in extra function) +- added some missing Py_None + +*/ diff --git a/src/application/capisuitemodule.h b/src/application/capisuitemodule.h new file mode 100644 index 0000000..c045fb3 --- /dev/null +++ b/src/application/capisuitemodule.h @@ -0,0 +1,124 @@ +/** @file capisuitemodule.h + @brief Contains the Python module integration routines + + This file contains the implementation of thy python module + capisuite which contains all commands available in python scripts + for programming capisuite. + + There are two groups of functions: functions used from C++ to init + and access the python module and functions used from python implementing + the functions of the python module. + + Here you'll only find the functions used from C++. If you're interested + in the commands usable from python, please have a look at the documentation + found in @ref python. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef PCAPICOMMODULE_H +#define PCAPICOMMODULE_H + +#include +#include "applicationexception.h" + +class Connection; +class CallControl; + +/** @brief Initializes and registers C implementation of python module capisuite + + This function creates a new python module named "capisuite" containing the + functions for the control of capisuite and two exception types: CallGoneError + and BackendError (see @ref python). Also there are three constants defined: + SERVICE_VOICE, SERVICE_FAXG3, SERVICE_OTHER, see also Connection::service_t. + + @return borrowed reference to the __main__-Dictionary of the created python interpreter + @throw ApplicationError Thrown if some step of the module initialization fails. See errormsg for details. +*/ +void capisuitemodule_init() throw (ApplicationError); + +/** @brief Destructor function for Connection reference given to Python scripts. + + This function will be called by Python if the given connection reference is not used any + more in the script. This will lead to the destruction of the Connection object. + + This function has the right signature to pass as destructor function for PyCCobject_FromVoidPtr() calls. + + @param conn Connection reference +*/ +void capisuitemodule_destruct_connection(void* conn); + +#endif + +/* History + +$Log: capisuitemodule.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.5 2002/12/07 22:36:21 ghillie +- capisuitemodule_init: doesn't return __main__ any more + +Revision 1.4 2002/12/06 12:54:11 ghillie +- removed capisuitemodule_call_gone() (CallGoneException won't be thrown in + from somewhere any more) +- added destruction function for Connection objects + +Revision 1.3 2002/12/05 14:50:05 ghillie +- comment improvement + +Revision 1.2 2002/12/02 12:26:51 ghillie +- update description to new behaviour of service parameter + +Revision 1.1 2002/11/29 11:06:22 ghillie +renamed CapiCom to CapiSuite (name conflict with MS crypto API :-( ) + +Revision 1.2 2002/11/29 10:20:44 ghillie +- updated docs, use doxygen format now + +Revision 1.1 2002/11/22 15:44:54 ghillie +renamed pcallcontrol.* to capicommodule.* + +Revision 1.7 2002/11/18 14:21:07 ghillie +- moved global severity_t to ApplicationError::severity_t +- added throw() declarations to header files + +Revision 1.6 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.5 2002/11/10 17:03:45 ghillie +now CallControl reference is passed directly to the called Pyhton functions + +Revision 1.4 2002/11/06 16:16:07 ghillie +added code to raise CallGoneError in any case so the script is cancelled when the call is gone surely + +Revision 1.3 2002/10/30 14:25:54 ghillie +added connect,disconnect,reject functions, changed init function to return the module dictionary + +Revision 1.2 2002/10/27 12:47:20 ghillie +- added multithread support for python +- changed callcontrol reference to stay in the python namespace +- changed ApplicationError to support differen severity + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.3 2002/10/24 09:55:52 ghillie +many fixes. Works for one call now + +Revision 1.2 2002/10/23 15:42:11 ghillie +- added standard headers +- changed initialization code (object references now set in extra function) +- added some missing Py_None + +*/ diff --git a/src/application/idlescript.cpp b/src/application/idlescript.cpp new file mode 100644 index 0000000..544ef92 --- /dev/null +++ b/src/application/idlescript.cpp @@ -0,0 +1,186 @@ +/* @file incomingscript.cpp + @brief Contains IncomingScript - Incoming call handling. One object for each incoming call is created. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "idlescript.h" +#include "capisuitemodule.h" + +void* idlescript_exec_handler(void* arg) +{ + if (!arg) { + cerr << "FATAL ERROR: no IdleScript reference given in idlescript_exec_handler" << endl; + exit(1); + } + pthread_cleanup_push(idlescript_cleanup_handler,arg); + IdleScript *instance=static_cast(arg); + instance->run(); + pthread_cleanup_pop(1); // run the cleanup_handler and then deregister it +} + +void idlescript_cleanup_handler(void* arg) +{ + if (!arg) { + cerr << "FATAL ERROR: no IdleScript reference given in idlescript_exec_handler" << endl; + exit(1); + } + IdleScript *instance=static_cast(arg); + instance->final(); +} + +IdleScript::IdleScript(ostream &debug, unsigned short debug_level, ostream &error, Capi *capi, string idlescript, int idlescript_interval, PyThreadState *py_state, PycStringIO_CAPI* cStringIO) throw (ApplicationError) +:PythonScript(debug,debug_level,error,idlescript,"idle",cStringIO),idlescript_interval(idlescript_interval),py_state(py_state),capi(capi),active(true) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); + int ret=pthread_create(&thread_handle, &attr, idlescript_exec_handler, this); // start thread as detached + if (ret) + throw ApplicationError("error while creating thread","IdleScript::IdleScript()"); + + if (debug_level>=3) + debug << prefix() << "IdleScript created." << endl; +} + +IdleScript::~IdleScript() +{ + if (debug_level>=3) + debug << prefix() << "IdleScript deleted" << endl; +} + +void +IdleScript::run() throw() +{ + int count=0,errorcount=0; + timespec delay_time; + delay_time.tv_sec=0; delay_time.tv_nsec=100000000; // 100 msec + while (1) { + pthread_testcancel(); // cancellation point + nanosleep(&delay_time,NULL); + count++; + if (active && (count>=idlescript_interval*10)) { + count=0; + PyObject *capi_ref=NULL; + try { + if (debug_level>=3) + debug << prefix() << "executing idlescript..." << endl; + PyEval_RestoreThread(py_state); // acquire lock, switch to right thread context + + capisuitemodule_init(); + + capi_ref=PyCObject_FromVoidPtr(capi,NULL); // new ref + if (!capi_ref) + throw ApplicationError("unable to create CObject from Capi reference","IdleScript::run()"); + + args=Py_BuildValue("(O)",capi_ref); // args = new ref + if (!args) + throw ApplicationError("can't build arguments","IdleScript::run()"); + + PythonScript::run(); + + Py_DECREF(args); + args=NULL; + + Py_DECREF(capi_ref); + capi_ref=NULL; + + if (PyEval_SaveThread()!=py_state) // release lock + throw ApplicationError("can't release thread lock","IdleScript::run()"); + errorcount=0; + if (debug_level>=3) + debug << prefix() << "idlescript finished..." << endl; + } + catch (ApplicationError e) { + errorcount++; + error << prefix() << "IdleScript " << this << " Error occured. " << endl; + error << prefix() << "message was: " << e << endl; + if (errorcount>9) { + error << prefix() << "Too much subsequent errors. Disabling idle script." << endl; + active=false; + } + + if (args) + Py_DECREF(args); + if (capi_ref) + Py_DECREF(capi_ref); + if (PyEval_SaveThread()!=py_state) // release lock + throw ApplicationError("can't release thread lock","IdleScript::run()"); + } + } + } +} + +void +IdleScript::requestTerminate() +{ + pthread_cancel(thread_handle); +} + +void +IdleScript::activate() +{ + active=true; +} + +/* History + +$Log: idlescript.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.12 2003/02/10 14:17:09 ghillie +merged from NATIVE_PTHREADS to HEAD + +Revision 1.11.2.1 2003/02/09 15:03:41 ghillie +- rewritten to use native pthread_* calls instead of CommonC++ Thread + +Revision 1.11 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.10 2003/01/18 12:52:50 ghillie +- pass on reference to Python C API to PythonScript + +Revision 1.9 2003/01/17 15:11:34 ghillie +- added debug output for finish of idlescript + +Revision 1.8 2003/01/06 21:02:01 ghillie +- won't exit if script causes too much errors - only temporarily deactivate + script execution, can be re-enabled with activate() +- added debug output for script execution + +Revision 1.7 2003/01/06 16:21:58 ghillie +- renamed terminate() to requestTerminate() to avoid endless recursion +- use finish flag instead of call to terminate() in requestTerminate() and run() + +Revision 1.6 2003/01/04 16:00:53 ghillie +- log improvements: log_level, timestamp + +Revision 1.5 2002/12/16 13:12:44 ghillie +- removed double output of error message + +Revision 1.4 2002/12/14 14:02:22 ghillie +- added terminate() method (make terminate public) +- added error counting code to de-activate idle-script after 10 errors + +Revision 1.3 2002/12/13 09:57:10 ghillie +- error message formatting done by exception classes now + +Revision 1.2 2002/12/11 13:03:50 ghillie +- finished (use Thred::sleep() instead of nanosleep(), fix in if condition) + +Revision 1.1 2002/12/10 15:54:08 ghillie +- initial checkin, will take over functionality from FlowControl::executeIdleScript() + +*/ diff --git a/src/application/idlescript.h b/src/application/idlescript.h new file mode 100644 index 0000000..5f6a78f --- /dev/null +++ b/src/application/idlescript.h @@ -0,0 +1,160 @@ +/** @file idlescript.h + @brief Contains IdleScript - Implements calling of python script in regular intervals for user defined activity (e.g. sending faxes). + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef IDLESCRIPT_H +#define IDLESCRIPT_H + +#include +#include "applicationexception.h" +#include "pythonscript.h" + +class Capi; +class PycStringIO_CAPI; + +/** @brief Thread exec handler for IdleScript class + + This is a handler which will call this->run() for the use in pthread_create(). + It will also register idlescript_cleanup_handler +*/ +void* idlescript_exec_handler(void* arg); + +/** @brief Thread clean handler for IdleScript class + + This is a handler which is called by pthreads at cleanup. + It will call this->final(). +*/ +void idlescript_cleanup_handler(void* arg); + +/** @brief Implements calling of python script in regular intervals for user defined activity (e.g. sending faxes). + + Executes a given idle script at regular intervals thus giving the user the ability to + do arbitrary things. The main use is surely initiating outgoing calls, e.g. to send faxes. + + It creates one new thread which will execute the idle script over and over... + + If the script fails too often, it's deactivated. After fixing the script, it can be reactivated + with activate(). + + @author Gernot Hillier +*/ + +class IdleScript: public PythonScript +{ + friend void* idlescript_exec_handler(void*); + friend void idlescript_cleanup_handler(void*); + public: + /** @brief Constructor. Create Object and start a detached thread + + @param debug stream for debugging info + @param debug_level verbosity level for debug messages + @param error stream for error messages + @param capi reference to Capi object + @param idlescript file name of the python script to use as incoming script + @param idlescript_interval interval between two subsequent calls to the idle script in seconds + @param py_state thread state of the main python interpreter which must be initialized an Py_SaveThread()'d before. + @param cStringIO pointer to the Python cStringIO C API + @throw ApplicationError Thrown if thread can't be started + */ + IdleScript(ostream &debug, unsigned short debug_level, ostream &error, Capi *capi, string idlescript, int idlescript_interval, PyThreadState *py_state, PycStringIO_CAPI* cStringIO) throw (ApplicationError); + + /** @brief Destructor. Destruct object. + */ + virtual ~IdleScript(); + + /** @brief terminate thread + */ + void requestTerminate(void); + + /** @brief reactivate the script execution in the case it was deactivated by too much errors + */ + void activate(void); + + private: + /** @brief Thread body. Calls the python function idle(). + + The read Python idle script must provide a function named idle with the following signature: + + def idle(capi): + # function body + + The parameters given to the python function are: + - capi: reference to the capi providing the interface to the ISDN hardware (meeded for the call_*() function class) + + The script is responsible for clearing each call it initiates, even in the exception handlers! + + If the call is disconnected by the other party, the Python exception CallGoneError is raised and should be caught + by the script (don't forget to call disconnect() there). + + If the script produces too much errors in a row, it will be deactivated. Use activate() to re-enable. + + The python global lock will be acquired while the function runs. + */ + virtual void run(void) throw(); + + PyThreadState *py_state; ///< py_state of the main python interpreter used for run(). + string idlescript; ///< name of the python script which is called at regular intervals + int idlescript_interval; ///< interval between subsequent executions of idle script + Capi *capi; ///< reference to Capi object + bool active; ///< used to disable IdleScript in case of too much errors + + pthread_t thread_handle; ///< handle for the created pthread thread +}; + +#endif + +/* History + +$Log: idlescript.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.9 2003/02/10 14:17:09 ghillie +merged from NATIVE_PTHREADS to HEAD + +Revision 1.8.2.2 2003/02/10 14:04:57 ghillie +- made destructors virtual, otherwise wrong destructor is called! + +Revision 1.8.2.1 2003/02/09 15:03:41 ghillie +- rewritten to use native pthread_* calls instead of CommonC++ Thread + +Revision 1.8 2003/01/18 12:53:06 ghillie +- pass on reference to Python C API to PythonScript + +Revision 1.7 2003/01/13 21:25:13 ghillie +- improved comment for finish flag + +Revision 1.6 2003/01/06 21:02:56 ghillie +- added support for deactivating/activating script execution w/o exiting the + thread (new method activate, comment changes) + +Revision 1.5 2003/01/06 16:22:24 ghillie +- renamed terminate() to requestTerminate() to avoid name-conflict +- added finish flag + +Revision 1.4 2003/01/04 16:00:53 ghillie +- log improvements: log_level, timestamp + +Revision 1.3 2002/12/14 14:02:51 ghillie +- added terminate() method +- added error counting code to run() to deactivate after 10 errors + +Revision 1.2 2002/12/11 13:04:35 ghillie +- minor improvements in comments, ... + +Revision 1.1 2002/12/10 15:54:08 ghillie +- initial checkin, will take over functionality from FlowControl::executeIdleScript() + +*/ diff --git a/src/application/incomingscript.cpp b/src/application/incomingscript.cpp new file mode 100644 index 0000000..464977a --- /dev/null +++ b/src/application/incomingscript.cpp @@ -0,0 +1,318 @@ +/* @file incomingscript.cpp + @brief Contains IncomingScript - Incoming call handling. One object for each incoming call is created. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "incomingscript.h" +#include "../modules/disconnectmodule.h" +#include "capisuitemodule.h" + +#define TEMPORARY_FAILURE 0x34A9 // see ETS 300 102-1, Table 4.13 (cause information element) + +void* incomingscript_exec_handler(void* arg) +{ + if (!arg) { + cerr << "FATAL ERROR: no IncomingScript reference given in incomingscript_exec_handler" << endl; + exit(1); + } + pthread_cleanup_push(incomingscript_cleanup_handler,arg); + IncomingScript *instance=static_cast(arg); + instance->run(); + pthread_cleanup_pop(1); // run the cleanup_handler and then deregister it +} + +void incomingscript_cleanup_handler(void* arg) +{ + if (!arg) { + cerr << "FATAL ERROR: no IncomingScript reference given in incomingscript_exec_handler" << endl; + exit(1); + } + IncomingScript *instance=static_cast(arg); + instance->final(); +} + +IncomingScript::IncomingScript(ostream &debug, unsigned short debug_level, ostream &error, Connection *conn, string incoming_script, PycStringIO_CAPI* cStringIO) throw (ApplicationError) +:PythonScript(debug,debug_level,error,incoming_script,"callIncoming",cStringIO),conn(conn) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); + int ret=pthread_create(&thread_handle, &attr, incomingscript_exec_handler, this); // start thread as detached + if (ret) + throw ApplicationError("error while creating thread","PythonScript::PythonScript()"); + + if (debug_level>=2) + debug << prefix() << "Connection " << conn << " created IncomingScript" << endl; +} + +IncomingScript::~IncomingScript() +{ + if (conn) { + error << prefix() << "Warning: Connection still established in IncomingScript destructor. Disconnecting." << endl; + try { + DisconnectModule active(conn,TEMPORARY_FAILURE); + active.mainLoop(); + } + catch (CapiError e) { + error << prefix() << "ERROR: disconnection also failed. Too bad..." << endl; + } + delete conn; + } + if (debug_level>=2) + debug << prefix() << "IncomingScript deleted" << endl; +} + +void +IncomingScript::run() throw() +{ + PyObject *conn_ref=NULL; + PyThreadState *py_state=NULL; + + try { + // thread safe Python init, taken out of PyApache 4.26 + PyEval_AcquireLock(); + + if (!(py_state=Py_NewInterpreter() )) { + PyEval_ReleaseLock(); + capisuitemodule_destruct_connection(conn); + throw ApplicationError("error while creating new python interpreter","IncomingScript::run()"); + } + + capisuitemodule_init(); + + conn_ref=PyCObject_FromVoidPtr(conn,capisuitemodule_destruct_connection); // new ref + if (!conn_ref) { + capisuitemodule_destruct_connection(conn); + throw ApplicationError("unable to create CObject from Connection reference","IncomingScript::run()"); + } + + args=Py_BuildValue("Oiss",conn_ref,conn->getService(),conn->getCallingPartyNumber().c_str(),conn->getCalledPartyNumber().c_str()); + if (!args) + throw ApplicationError("error during argument building","IncomingScript::run()"); + + PythonScript::run(); + + Py_DECREF(args); + args=NULL; + + Py_DECREF(conn_ref); + conn_ref=NULL; + conn=NULL; // Connection object will be deleted by Python destruction handler... + + Py_EndInterpreter(py_state); + py_state=NULL; + PyEval_ReleaseLock(); // release lock + } + catch(ApplicationError e) { + error << prefix() << "Error occured. message was: " << e << endl; + + if (args) + Py_DECREF(args); + if (conn_ref) { + Py_DECREF(conn_ref); + conn=NULL; + } + if (py_state) { + Py_EndInterpreter(py_state); + py_state=NULL; + PyEval_ReleaseLock(); + } + } +} + +/* History + +$Log: incomingscript.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.9 2003/02/10 14:17:09 ghillie +merged from NATIVE_PTHREADS to HEAD + +Revision 1.8.2.1 2003/02/09 15:03:41 ghillie +- rewritten to use native pthread_* calls instead of CommonC++ Thread + +Revision 1.8 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.7 2003/01/19 12:08:47 ghillie +- changed some debug_levels + +Revision 1.6 2003/01/18 12:53:06 ghillie +- pass on reference to Python C API to PythonScript + +Revision 1.5 2003/01/04 16:00:53 ghillie +- log improvements: log_level, timestamp + +Revision 1.4 2002/12/14 14:03:27 ghillie +- added throw() declaration to run() method + +Revision 1.3 2002/12/13 09:57:10 ghillie +- error message formatting done by exception classes now + +Revision 1.2 2002/12/10 15:52:32 ghillie +- removed debug output + +Revision 1.1 2002/12/10 15:01:08 ghillie +- class IncomingScript now takes over the functionality of the old CallControl + class defined in callcontrol.*, but uses a base class now + +Revision 1.29 2002/12/09 15:23:49 ghillie +- moved start() out of constructor in creator (was unspecified this way!) +- moved disconnection to destructor so it's assured it happens +- exception severity cleanup (no more WARNING exceptions) +- python reference counting cleanup +- added debug stream as constructor parameter, debug output improvement + +Revision 1.28 2002/12/07 22:30:48 ghillie +- removed copying of filename to new char*, used const_cast from (const char*) + to (char*) instead +- moved python initialization code from constructor to run(), makes some + attributes obsolete +- getting __main__ namespace now taken out of capisuitemodule_init(), done + here instead +- use DisconnectModule for error handling now + +Revision 1.27 2002/12/06 15:23:14 ghillie +- wait for successful disconnection when an error in the script occured + +Revision 1.26 2002/12/06 12:50:02 ghillie +- passed the destruction function capisuitemodule_destruct_connection to the PyCObject containing connection reference + +Revision 1.25 2002/12/05 15:52:48 ghillie +- begin restructuring for self deletion of Connection object after it gets its OK from CallControl/FlowControl + +Revision 1.24 2002/12/05 14:48:25 ghillie +- cleaned up some python reference counting + +Revision 1.23 2002/12/02 12:21:56 ghillie +- incoming script name is now a parameter to constructor, not #define'd any more +- service parameter to python script now uses constants defined in Connection::service_t +- SEGV FIX: isRunning is now set to false before ending Python interpreter in run(), callCompleted() acquires + python global lock _before_ reading isRunning -> race condition fixed +- exception handler in run() ends python interpreter correctly now + +Revision 1.22 2002/11/29 11:09:04 ghillie +renamed CapiCom to CapiSuite (name conflict with MS crypto API :-( ) + +Revision 1.21 2002/11/29 10:20:44 ghillie +- updated docs, use doxygen format now + +Revision 1.20 2002/11/25 11:44:48 ghillie +removed CIP value from application, use service type instead + +Revision 1.19 2002/11/23 15:54:40 ghillie +pcallcontrol was renamed to capicommodule + +Revision 1.18 2002/11/21 11:34:08 ghillie +- changed Reject cause when we have a problem from "Destination Out Of Order" to "Temporary Failure" +- moved Py_EndInterpreter from destructor to run() +- new method callCompleted() which throws CallGoneError into Python +- new method final() which is called automatically after thread has finished, now CallControl objects will delete themselves + to allow cleanup routines in Python scripts which may take some time to finish w/o freezing the whole application + +Revision 1.17 2002/11/20 17:16:24 ghillie +- SEGV-Fix: CallGoneError only triggered if python script is still running in CallControl::~CallControl +- changed sleep in mainLoop() to nanosleep +- small typo fixed + +Revision 1.16 2002/11/19 15:57:18 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.15 2002/11/18 14:21:07 ghillie +- moved global severity_t to ApplicationError::severity_t +- added throw() declarations to header files + +Revision 1.14 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.13 2002/11/13 15:21:22 ghillie +added some error handling for python states + +Revision 1.12 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.11 2002/11/12 15:47:27 ghillie +added dataIn-handler + +Revision 1.10 2002/11/10 17:02:22 ghillie +changed to pass CallControl reference to the called python functions + +Revision 1.9 2002/11/07 08:19:04 ghillie +some improvements and fixes in Python global lock and thread state handling + +Revision 1.8 2002/11/06 16:16:07 ghillie +added code to raise CallGoneError in any case so the script is cancelled when the call is gone surely + +Revision 1.7 2002/10/31 12:35:58 ghillie +added DTMF support + +Revision 1.6 2002/10/30 16:05:20 ghillie +cosmetic fixes... + +Revision 1.5 2002/10/30 14:24:41 ghillie +added support for python call handling before call is connected + +Revision 1.4 2002/10/30 10:45:51 ghillie +added #define for value which should go to config file later + +Revision 1.3 2002/10/29 14:06:36 ghillie +several fixes in run method + +Revision 1.2 2002/10/27 12:47:20 ghillie +- added multithread support for python +- changed callcontrol reference to stay in the python namespace +- changed ApplicationError to support differen severity + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.9 2002/10/24 09:55:52 ghillie +many fixes. Works for one call now + +Revision 1.8 2002/10/23 15:40:15 ghillie +added python integration... + +Revision 1.7 2002/10/10 12:45:40 gernot +added AudioReceive module, some small details changed + +Revision 1.6 2002/10/09 14:36:22 gernot +added CallModule base class for all call handling modules + +Revision 1.5 2002/10/09 11:18:59 gernot +cosmetic changes (again...) and changed info function of CAPI class + +Revision 1.4 2002/10/05 20:43:32 gernot +quick'n'dirty, but WORKS + +Revision 1.3 2002/10/05 13:53:00 gernot +changed to use thread class of CommonC++ instead of the threads-package +some cosmetic improvements (indentation...) + +Revision 1.2 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.1 2002/10/04 13:28:43 gernot +CallControll class added + +*/ diff --git a/src/application/incomingscript.h b/src/application/incomingscript.h new file mode 100644 index 0000000..34d0134 --- /dev/null +++ b/src/application/incomingscript.h @@ -0,0 +1,216 @@ +/** @file incomingscript.h + @brief Contains IncomingScript - Incoming call handling. One object for each incoming call is created. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef INCOMINGSCRIPT_H +#define INCOMINGSCRIPT_H + +#include "applicationexception.h" +#include "pythonscript.h" + +class Connection; +class PycStringIO_CAPI; + +/** @brief Thread exec handler for IncomingScript class + + This is a handler which will call this->run() for the use in pthread_create(). + It will also register incomingscript_cleanup_handler +*/ +void* incomingscript_exec_handler(void* arg); + +/** @brief Thread clean handler for IncomingScript class + + This is a handler which is called by pthreads at cleanup. + It will call this->final(). +*/ +void incomingscript_cleanup_handler(void* arg); + +/** @brief Incoming call handling. One object for each incoming call is created. + + IncomingScript handels an incoming connection. For each connection, one object + of it is created by FlowControl. It mainly creates a new thread with an own + python subinterpreter, initializes the capisuitemodule, and calls run() of + PythonScript which will execute the defined function in the script. + + @author Gernot Hillier +*/ +class IncomingScript: public PythonScript +{ + friend void* incomingscript_exec_handler(void*); + friend void incomingscript_cleanup_handler(void*); + + public: + /** @brief Constructor. Create Object and start detached thread + + @param debug stream for debugging info + @param debug_level verbosity level for debug messages + @param error stream for error messages + @param conn reference to according connection (disconnected if error occurs) + @param incoming_script file name of the python script to use as incoming script + @param cStringIO pointer to the Python cStringIO C API + @throw ApplicationError Thrown if thread can't be started + */ + IncomingScript(ostream &debug, unsigned short debug_level, ostream &error, Connection *conn, string incoming_script, PycStringIO_CAPI* cStringIO) throw (ApplicationError); + + /** @brief Destructor. Destruct object and assure the call is disconnected. + */ + virtual ~IncomingScript(); + + private: + /** @brief Thread body. Calls the python function callIncoming() which will handle the call. + + Create python sub-interpreter, read script for incoming calls, + + The read Python script for incoming calls must provide a function named callIncoming with the following signature: + + def callIncoming(call, service, callingParty, calledParty): + # function body + + The parameters given to the python function are: + - call: reference to the incoming call. Must be given to all capisuite-provided python functions as first parameter. + - service (integer): service as signalled by ISDN, set to one of the SERVICE_* constants defined in capisuitemodule_init + - callingParty (string): the number of the calling party (source of the call) + - calledParty (string): the number of the called party (destination of the call) + + At the moment callIncoming() is called, the call is waiting for an answer, so the first thing the script must do + is to call connect_*() or reject(). It must also disconnect the call in any case (even in the exception handlers!) + before finishing using disconnect(). + + If the call is disconnected by the other party, the Python exception CallGoneError is raised and should be caught + by the script (but even there you must call disconnect()). + + The python global lock will be acquired while the function runs. + */ + virtual void run(void) throw(); + + Connection *conn; ///< reference to according connection object + + pthread_t thread_handle; ///< handle for the created pthread thread +}; + +#endif + +/* History + +$Log: incomingscript.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.5 2003/02/10 14:17:09 ghillie +merged from NATIVE_PTHREADS to HEAD + +Revision 1.4.2.2 2003/02/10 14:04:57 ghillie +- made destructors virtual, otherwise wrong destructor is called! + +Revision 1.4.2.1 2003/02/09 15:03:41 ghillie +- rewritten to use native pthread_* calls instead of CommonC++ Thread + +Revision 1.4 2003/01/18 12:53:06 ghillie +- pass on reference to Python C API to PythonScript + +Revision 1.3 2003/01/04 16:00:53 ghillie +- log improvements: log_level, timestamp + +Revision 1.2 2002/12/14 14:03:27 ghillie +- added throw() declaration to run() method + +Revision 1.1 2002/12/10 15:01:08 ghillie +- class IncomingScript now takes over the functionality of the old CallControl + class defined in callcontrol.*, but uses a base class now + +Revision 1.17 2002/12/09 15:24:21 ghillie +- new parameter debug to constructor +- doc changes + +Revision 1.16 2002/12/07 22:31:37 ghillie +- remove unnecessary attributes py_state, py_dict, isRunning +- added attribute incoming_script + +Revision 1.15 2002/12/05 15:53:41 ghillie +- began restructuring for COnnection to self-delete after getting OK from FlowControl / CallControl +- callCompleted() removed, not needed any more + +Revision 1.14 2002/12/02 12:23:06 ghillie +- incoming_script is now a parameter to constructor +- service parameter now uses constants from Connection::service_t + +Revision 1.13 2002/11/29 11:09:04 ghillie +renamed CapiCom to CapiSuite (name conflict with MS crypto API :-( ) + +Revision 1.12 2002/11/29 10:20:44 ghillie +- updated docs, use doxygen format now + +Revision 1.11 2002/11/27 15:56:14 ghillie +updated comments for doxygen + +Revision 1.10 2002/11/23 15:55:09 ghillie +added missing (?) include + +Revision 1.9 2002/11/21 11:34:33 ghillie +- new methods final() and callCompleted() + +Revision 1.8 2002/11/18 14:21:07 ghillie +- moved global severity_t to ApplicationError::severity_t +- added throw() declarations to header files + +Revision 1.7 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.6 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.5 2002/11/12 15:48:07 ghillie +added data in handler + +Revision 1.4 2002/10/31 12:35:58 ghillie +added DTMF support + +Revision 1.3 2002/10/30 14:24:41 ghillie +added support for python call handling before call is connected + +Revision 1.2 2002/10/27 12:47:20 ghillie +- added multithread support for python +- changed callcontrol reference to stay in the python namespace +- changed ApplicationError to support differen severity + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.6 2002/10/23 15:40:15 ghillie +added python integration... + +Revision 1.5 2002/10/23 14:17:41 ghillie +added registerCallModule() + +Revision 1.4 2002/10/09 14:36:22 gernot +added CallModule base class for all call handling modules + +Revision 1.3 2002/10/05 20:43:32 gernot +quick'n'dirty, but WORKS + +Revision 1.2 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.1 2002/10/04 13:28:43 gernot +CallControll class added + +*/ diff --git a/src/application/pythonscript.cpp b/src/application/pythonscript.cpp new file mode 100644 index 0000000..a712cf2 --- /dev/null +++ b/src/application/pythonscript.cpp @@ -0,0 +1,138 @@ +/* @file pythonscript.cpp + @brief Contains PythonScript - Read a python script and call a function in own thread + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "pythonscript.h" +#include +#include + +PythonScript::PythonScript(ostream &debug, unsigned short debug_level, ostream &error, string filename, string functionname, PycStringIO_CAPI* cStringIO) +:debug(debug),debug_level(debug_level),error(error),filename(filename),functionname(functionname),args(NULL), cStringIO(cStringIO) +{ + if (debug_level>=3) + debug << prefix() << "PythonScript created." << endl; +} + +PythonScript::~PythonScript() +{ + if (debug_level>=3) + debug << prefix() << "PythonScript deleted." << endl; +} + +string +PythonScript::prefix() +{ + stringstream s; + time_t t=time(NULL); + char* ct=ctime(&t); + ct[24]='\0'; + s << ct << " Pythonscript " << filename << "," << functionname << "," << hex << this << ": "; + return (s.str()); +} + +void +PythonScript::run() throw (ApplicationError) +{ + PyObject *module=NULL, *module_dict=NULL, *function_ref=NULL, *result=NULL; + + FILE* scriptfile=NULL; + try { + if (!(scriptfile=fopen(filename.c_str(),"r") ) ) + throw ApplicationError("unable to open "+filename,"PythonScript::run()"); + + // get __main__ + if ( ! ( module=PyImport_AddModule("__main__"))) // module = borrowed ref + throw ApplicationError("unable to get __main__ namespace","PythonScript::run()"); + if ( ! ( module_dict=PyModule_GetDict(module) ) ) // module_dict = borrowed ref + throw ApplicationError("unable to get __main__ dictionary","PythonScript::run()"); + + // read control script. It must define a function callIncoming. For description see run() + if (PyRun_SimpleFile(scriptfile,const_cast(filename.c_str()))==-1) + throw ApplicationError("syntax error while executing python script","PythonScript::run()"); + + fclose(scriptfile); + scriptfile=NULL; + + // now let's get the user defined function + PyObject* function_ref=PyDict_GetItemString(module_dict,const_cast(functionname.c_str())); // borrowed ref + if (! function_ref || !PyCallable_Check(function_ref) ) + throw ApplicationError("control script does not define function "+functionname,"PythonScript::run()"); + + if (!args) + throw ApplicationError("no arguments given","PythonScript::run()"); + if (!PyTuple_Check(args)) + throw ApplicationError("args must be a tuple","PythonScript::run()"); + + result=PyObject_CallObject(function_ref,args); + if (!result) { + PyObject *catch_stderr; + // redirect sys.stderr and then print exception + if ( ! ( module=PyImport_AddModule("sys"))) // module = borrowed ref + throw ApplicationError("unable to get sys namespace","PythonScript::run()"); + if ( ! ( module_dict=PyModule_GetDict(module) ) ) // module_dict = borrowed ref + throw ApplicationError("unable to get sys dictionary","PythonScript::run()"); + + catch_stderr=cStringIO->NewOutput(128); // create StringIO object for collecting stderr messages + if ( PyDict_SetItemString(module_dict,"stderr",catch_stderr)!=0 ) { + Py_DECREF(catch_stderr); + throw ApplicationError("unable to redirect sys.stderr","PythonScript::run()"); + } + Py_DECREF(catch_stderr); + + PyErr_Print(); + PyObject *py_traceback; + if ( !(py_traceback=cStringIO->cgetvalue(catch_stderr)) ) + throw ApplicationError("unable to get traceback","PythonScript::run()"); + + int length; + char *traceback; + if (PyString_AsStringAndSize(py_traceback, &traceback, &length)) + throw ApplicationError("unable to convert traceback to char*","PythonScript::run()"); + + error << prefix() << "A python error occured. See traceback below." << endl; + error << prefix() << "Python traceback: "; + for (int i=0;i + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef PYTHONSCRIPT_H +#define PYTHONSCRIPT_H + +#include +#include +#include "applicationexception.h" +class PycStringIO_CAPI; + +using namespace std; + +/** @brief Read a python script and call a function + + This class reads a given python script which + must define one function with given name. This function is called + with arbitrary parameters. + + @author Gernot Hillier +*/ +class PythonScript +{ + public: + /** @brief Constructor. Create Object. + + @param debug stream for debugging info + @param debug_level verbosity level for debug messages + @param error stream for error messages + @param filename file name of the python script to read + @param functionname name of the function to call + @param cStringIO pointer to the Python cStringIO C API + */ + PythonScript(ostream &debug, unsigned short debug_level, ostream &error, string filename, string functionname, PycStringIO_CAPI* cStringIO); + + /** @brief Destructor. + */ + virtual ~PythonScript(); + + protected: + /** @brief Reads the given python script and calls the given function. + + The arguments for the function must be given in the constructor. + + @throw ApplicationError Thrown when script can't be executed for any reason. + */ + virtual void run() throw (ApplicationError); + + /** @brief Called by pscript_cleanup_handler(), will delete the current object. + */ + virtual void final(); + + /** @brief return a prefix containing this pointer and date for log messages + + @return constructed prefix as stringstream + */ + string prefix(); + + string filename, ///< name of the python script to read + functionname; ///< name of the function to call + PyObject *args; ///< python tuple containing the args for the called python function + ostream &debug, ///< debug stream + &error; ///< error stream + unsigned short debug_level; ///< debug level + PycStringIO_CAPI* cStringIO; ///< holds a pointer to the Python cStringIO C API +}; + +#endif + +/* History + +$Log: pythonscript.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.6 2003/02/10 14:17:09 ghillie +merged from NATIVE_PTHREADS to HEAD + +Revision 1.5.2.2 2003/02/10 14:04:57 ghillie +- made destructors virtual, otherwise wrong destructor is called! + +Revision 1.5.2.1 2003/02/09 15:03:42 ghillie +- rewritten to use native pthread_* calls instead of CommonC++ Thread + +Revision 1.5 2003/01/18 12:55:39 ghillie +- run handles python script errors now on its own and prints tracebacks + to error log file correctly (solves TODO) + +Revision 1.4 2003/01/04 16:00:53 ghillie +- log improvements: log_level, timestamp + +Revision 1.3 2002/12/14 14:04:20 ghillie +- run throws ApplicationError now so that derived classes can catch + and handle it on their behalf + +Revision 1.2 2002/12/10 15:05:45 ghillie +- finished pythonscript class definition + +Revision 1.1 2002/12/09 18:07:59 ghillie +- initial checkin, not finished! + +*/ diff --git a/src/backend/.cvsignore b/src/backend/.cvsignore new file mode 100644 index 0000000..e995588 --- /dev/null +++ b/src/backend/.cvsignore @@ -0,0 +1,3 @@ +.deps +Makefile +Makefile.in diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am new file mode 100644 index 0000000..6f19e65 --- /dev/null +++ b/src/backend/Makefile.am @@ -0,0 +1,3 @@ +noinst_LIBRARIES = libccbackend.a +libccbackend_a_SOURCES = capi.cpp capi.h applicationinterface.h connection.h \ + connection.cpp callinterface.h capiexception.h diff --git a/src/backend/applicationinterface.h b/src/backend/applicationinterface.h new file mode 100644 index 0000000..fe3cf5f --- /dev/null +++ b/src/backend/applicationinterface.h @@ -0,0 +1,104 @@ +/** @file applicationinterface.h + @brief Contains ApplicationInterface - Interface class which is implemented by the main application. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef APPLICATIONINTERFACE_H +#define APPLICATIONINTERFACE_H + +using namespace std; + +#include + +class Connection; + +/** @brief Interface class which is implemented by the main application. + + This interface exposes methods for general communication between the Capi abstraction layer and the application. These methods + are mainly used for telling the application the begin and end of an incoming call, so it can start and finish call handling + procedures + + For special events during call handling, see the CallInterface. + + @author Gernot Hillier +*/ +class ApplicationInterface +{ + public: + /** @brief Called by Capi if we get a CONNECT_IND (in case of an incoming call) + + @param conn pointer to the according connection object + */ + virtual void callWaiting (Connection *conn) = 0; +}; + +#endif + +/* History + +$Log: applicationinterface.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.7 2002/12/05 15:55:34 ghillie +- removed callCompleted(), application will self-determine when call is completed + +Revision 1.6 2002/12/05 14:54:52 ghillie +- thrown away debug() method, debug stream is given in constructor now + +Revision 1.5 2002/11/29 10:20:44 ghillie +- updated docs, use doxygen format now + +Revision 1.4 2002/11/25 21:00:53 ghillie +- improved documentation, now doxygen-readabl + +Revision 1.3 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.2 2002/10/29 14:10:42 ghillie +updated description of callCompleted: now the given reference is valid + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.9 2002/10/23 15:37:50 ghillie +typo... + +Revision 1.8 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.7 2002/10/04 13:27:15 gernot +some restructuring to get it to a working state ;-) + +does not do anything useful yet nor does it even compile... + +Revision 1.6 2002/10/02 14:10:07 gernot +first version + +Revision 1.5 2002/10/01 08:56:09 gernot +some cosmetic improvements, changes for gcc3.2 + +Revision 1.4 2002/09/22 14:22:53 gernot +some cosmetic comment improvements ;-) + +Revision 1.3 2002/09/19 12:08:19 gernot +added magic CVS strings + +* Sun Sep 15 2002 - gernot@hillier.de +- put under CVS, cvs changelog follows + +* Sun May 20 2002 - gernot@hillier.de +- first version + +*/ diff --git a/src/backend/callinterface.h b/src/backend/callinterface.h new file mode 100644 index 0000000..9c89fb9 --- /dev/null +++ b/src/backend/callinterface.h @@ -0,0 +1,142 @@ +/** @file callinterface.h + @brief Contains CallInterface - Interface class for all signals specific to a certain call. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CALLINTERFACE_H +#define CALLINTERFACE_H + +#include + +using namespace std; + +/** @brief Interface class for all signals specific to a certain call. + + While ApplicationInterface contains the methods to inform the application about general events, this + interface has all the signals which describe events of a certain connection like DTMF signal received, + call is disconnected logical, etc. + + The application is supposed to create objects for each call which implement this interface and register + them with Connection::registerCallInterface(). It's possible to use different modules for different tasks + during one connection and to dynamically register/unregister them. If no object is registered, the callbacks + are simply not called. However, there are certain events which need a registered CallInterface implementing + object - otherwise Connection will throw exceptions. + + @author Gernot Hillier +*/ +class CallInterface +{ + public: + /** @brief Called if the connection is completely established (physical + logical) + */ + virtual void callConnected (void) = 0; + + /** @brief called if logical connection is finished + */ + virtual void callDisconnectedLogical (void) = 0; + + /** @brief called if physical connection is finished. + + This is called when the connection has been cleared down completely. + + Attention: You must delete the Connection object yourself if you don't need + it any more! + */ + virtual void callDisconnectedPhysical (void) = 0; + + /** @brief called if the file requested for sending is sent completely + */ + virtual void transmissionComplete (void) = 0; + + /** @brief called by Connection object if DMTF characters were received. + + It is necessary to enable DTMF receiving with Connection::enableDTMF + before any DTMFs are signalled. DTMF chars can be read with Connection::getDTMF(). + */ + virtual void gotDTMF (void) = 0; + + /** @brief called by Connection object for each received data packet. + + You can either use this to save your data manually and/or tell connection + to save it to a file (with start_file_reception)() ) + + But please not that this is a performance issue: calling an application function + for each received function should only be done if really necessary. + + @param data pointer to data as received by CAPI + @param length length of data in bytes + */ + virtual void dataIn (unsigned char* data, unsigned length) = 0; +}; + +#endif + +/* History + +$Log: callinterface.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.9 2002/12/06 12:55:04 ghillie +- updated docs + +Revision 1.8 2002/11/29 10:20:44 ghillie +- updated docs, use doxygen format now + +Revision 1.7 2002/11/27 15:58:13 ghillie +updated comments for doxygen + +Revision 1.6 2002/11/15 13:49:10 ghillie +fix: callmodule wasn't aborted when call was only connected/disconnected physically + +Revision 1.5 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.4 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.3 2002/11/12 15:48:54 ghillie +added data in handler + +Revision 1.2 2002/10/31 12:37:34 ghillie +added DTMF support + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.5 2002/10/23 15:40:51 ghillie +typo... + +Revision 1.4 2002/10/09 14:36:22 gernot +added CallModule base class for all call handling modules + +Revision 1.3 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.2 2002/10/04 13:27:15 gernot +some restructuring to get it to a working state ;-) + +does not do anything useful yet nor does it even compile... + +Revision 1.1 2002/10/02 14:10:07 gernot +first version + +*/ diff --git a/src/backend/capi.cpp b/src/backend/capi.cpp new file mode 100644 index 0000000..18473b6 --- /dev/null +++ b/src/backend/capi.cpp @@ -0,0 +1,1031 @@ +/* @file capi.h + @brief Contains Capi - Main Class for communication with CAPI + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include "connection.h" +#include "applicationinterface.h" +#include "capi.h" + +short Capi::numControllers=0; + +void* capi_exec_handler(void* arg) +{ + if (!arg) { + cerr << "FATAL ERROR: no Capi reference given in capi_exec_handler" << endl; + exit(1); + } + + Capi *instance=static_cast(arg); + instance->run(); +} + +Capi::Capi (ostream& debug, unsigned short debug_level, ostream &error, unsigned maxLogicalConnection, unsigned maxBDataBlocks,unsigned maxBDataLen) throw (CapiMsgError, CapiError) +:debug(debug),debug_level(debug_level),error(error),messageNumber(0),usedInfoMask(0),usedCIPMask(0) +{ + if (debug_level >= 2) + debug << prefix() << "Capi object created" << endl; + getInfo(); // can throw CapiMsgError. Just propagate... + + if (Capi::numControllers==0) + throw (CapiError("No ISDN-Controller installed","Capi::Capi()")); + + unsigned info = capi20_register(maxLogicalConnection, maxBDataBlocks, maxBDataLen, &applId); + if (applId == 0 || info!=0) + throw (CapiMsgError(info,"Error while registering application: "+describeParamInfo(info),"Capi::Capi()")); + + int erg=pthread_create(&thread_handle, NULL, capi_exec_handler, this); // create a normal thread + if (erg!=0) + throw (CapiMsgError(erg,"Error while starting message thread","Capi::Capi()")); +} + +Capi::~Capi () +{ + int ret=pthread_cancel(thread_handle); // tell run() to end + if (ret) + throw (CapiMsgError(ret,"Error while cancelling Capi thread","Capi::~Capi()")); + ret=pthread_join(thread_handle,NULL); + if (ret) + throw (CapiMsgError(ret,"Error while joining Capi thread","Capi::~Capi()")); + + unsigned info = capi20_release(applId); // this will abort capi20_waitformessage + if (info != 0) + throw (CapiMsgError(info,"Error while unregistering application: "+describeParamInfo(info),"Capi::~Capi()")); + + if (debug_level >= 2) + debug << prefix() << "Capi object deleted. Let's go to bed..." << endl; +} + +void +Capi::registerApplicationInterface(ApplicationInterface* application_in) +{ + application=application_in; + if (debug_level >= 1) { + debug << prefix() << "Registered successful at CAPI with ApplId " << applId << endl; + } +} + +void +Capi::unregisterConnection(_cdword plci) +{ + connections.erase(plci); +} + +void +Capi::listen_req(_cdword Controller, _cdword InfoMask, _cdword CIPMask) throw (CapiMsgError) +{ + _cmsg CMSG; // Nachrichten-Struktur + usedInfoMask=InfoMask; + usedCIPMask=CIPMask; + + if (debug_level >= 2) { + debug << prefix() << ">LISTEN_REQ ApplID 0x" << hex << applId << " msgNum 0x" << messageNumber << " Controller 0x" << Controller << " InfoMask 0x" + << InfoMask << " CIPMask 0x" << CIPMask << " 0x0 NULL NULL" << endl; + } + unsigned info=LISTEN_REQ(&CMSG, applId, messageNumber++, Controller, InfoMask,CIPMask,0,NULL,NULL); + if (debug_level >= 2) { + debug << prefix() << "info: " << info << endl; + } + + if (info != 0) + throw(CapiMsgError(info,"Error while LISTEN_REQ: "+Capi::describeParamInfo(info),"Capi::listen_req()")); +} + +void +Capi::alert_req(_cdword plci) throw (CapiMsgError) +{ + _cmsg CMSG; // Nachrichten-Struktur + + if (debug_level >= 2) { + debug << prefix() << ">ALERT_REQ: ApplId 0x" << hex << applId << ", MsgNr 0x" << messageNumber << ", PLCI 0x" << plci << endl; + } + unsigned info=ALERT_REQ(&CMSG, applId, messageNumber++, plci, NULL, NULL, NULL, NULL); + if (debug_level >= 2) { + debug << prefix() << "info: " << info << endl; + } + + if (info != 0) + throw(CapiMsgError(info,"Error while ALERT_REQ: "+Capi::describeParamInfo(info),"Capi::alert_req()")); +} + + +void +Capi::connect_req(Connection *conn, _cdword controller, _cword CIPValue, _cstruct calledPartyNumber, _cstruct callingPartyNumber, _cword B1protocol, _cword B2protocol, _cword B3protocol, _cstruct B1configuration, _cstruct B2configuration, _cstruct B3configuration) throw (CapiMsgError) +{ + _cmsg CMSG; // Nachrichten-Struktur + + messageNumber++; + + connections[0xFACE0000 | messageNumber]=conn; // pseudo PLCI to see which CONNECT_CONF corresponds to which CONNECT_REQ + // this can't be a valid NCCI as a valid ncci & 0xFF000000 = 0 + + if (debug_level >= 2) { + debug << prefix() << ">CONNECT_REQ: ApplId 0x" << hex << applId << ", MsgNr 0x" << messageNumber << ", Controller 0x" << controller + << " CIPValue 0x" << CIPValue << ", B1proto 0x" << B1protocol << ", B2proto 0x" << B2protocol <<", B3proto 0x" << B3protocol << endl; + } + unsigned info=CONNECT_REQ(&CMSG, applId, messageNumber, controller, CIPValue, calledPartyNumber, callingPartyNumber, NULL, NULL, + B1protocol, B2protocol, B3protocol, B1configuration, B2configuration, B3configuration, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + if (debug_level >= 2) { + debug << prefix() << "info: " << info << endl; + } + + if (info != 0) + throw(CapiMsgError(info,"Error while CONNECT_REQ: "+Capi::describeParamInfo(info),"Capi::connect_req()")); +} + +void +Capi::connect_b3_req(_cdword plci) throw (CapiMsgError) +{ + _cmsg CMSG; // Nachrichten-Struktur + + if (debug_level >= 2) { + debug << prefix() << ">CONNECT_B3_REQ: ApplId 0x" << hex << applId << ", MsgNr 0x" << messageNumber << ", PLCI 0x" << plci << endl; + } + unsigned info=CONNECT_B3_REQ(&CMSG, applId, messageNumber++, plci, NULL); + if (debug_level >= 2) { + debug << prefix() << "info: " << info << endl; + } + + if (info != 0) + throw(CapiMsgError(info,"Error while CONNECT_B3_REQ: "+Capi::describeParamInfo(info),"Capi::connect_b3_req()")); +} + +void +Capi::select_b_protocol_req (_cdword plci, _cword B1protocol, _cword B2protocol, _cword B3protocol, _cstruct B1configuration, _cstruct B2configuration, _cstruct B3configuration) throw (CapiMsgError) +{ + _cmsg CMSG; // Nachrichten-Struktur + + if (debug_level >= 2) debug << prefix() << ">SELECT_B_PROTOCOL_REQ: ApplId 0x" << hex << applId << ", MsgNr 0x" << messageNumber << ", PLCI 0x" << plci + << ", B1protocol " << B1protocol << ", B2protocol " << B2protocol << ", B3protocol " << B3protocol << endl; + unsigned info=SELECT_B_PROTOCOL_REQ(&CMSG, applId, messageNumber++, plci, B1protocol, B2protocol, B3protocol, B1configuration, B2configuration, B3configuration); + if (debug_level >= 2) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while SELECT_B_PROTOCOL_REQ: "+Capi::describeParamInfo(info),"Capi::select_b_protocol_req()")); +} + +void +Capi::data_b3_req (_cdword ncci, void* Data, _cword DataLength,_cword DataHandle,_cword Flags) throw (CapiMsgError) +{ + _cmsg CMSG; // Nachrichten-Struktur + if (debug_level >= 3) + debug << prefix() << ">DATA_B3_REQ ApplId 0x" << hex << applId << ", msgNum 0x" << messageNumber << ", NCCI 0x" << ncci << dec + << ", DataLen " << DataLength << ", DataHandle " << DataHandle << hex << ", Flags 0x" << Flags << endl; + unsigned info=DATA_B3_REQ(&CMSG, applId, messageNumber++, ncci, Data, DataLength, DataHandle, Flags); + if (debug_level >= 3) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while DATA_B3_REQ: "+Capi::describeParamInfo(info),"Capi::data_b3_req()")); +} + +void +Capi::disconnect_b3_req (_cdword ncci, _cstruct ncpi) throw (CapiMsgError) +{ + _cmsg CMSG; // Nachrichten-Struktur + if (debug_level >= 2) + debug << prefix() << ">DISCONNECT_B3_REQ ApplId 0x" << hex << applId << " MsgNum 0x" << messageNumber << " NCCI 0x" << ncci << endl; + unsigned info=DISCONNECT_B3_REQ(&CMSG, applId, messageNumber++, ncci, ncpi); + if (debug_level >= 2) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while DISCONNECT_B3_REQ: "+Capi::describeParamInfo(info),"Capi::disconnect_b3_req()")); +} + +void +Capi::disconnect_req (_cdword plci, _cstruct Keypadfacility, _cstruct Useruserdata, _cstruct Facilitydataarray) throw (CapiMsgError) +{ + _cmsg CMSG; // Nachrichten-Struktur + if (debug_level >= 2) { + debug << prefix() << ">DISCONNECT_REQ ApplId 0x" << hex << applId << " MsgNum 0x" << messageNumber << " PLCI 0x" << plci << endl; + } + unsigned info=DISCONNECT_REQ(&CMSG, applId, messageNumber++, plci, NULL, Keypadfacility, Useruserdata, Facilitydataarray); + if (debug_level >= 2) { + debug << prefix() << "info: " << info << endl; + } + + if (info != 0) + throw(CapiMsgError(info,"Error while DISCONNECT_REQ: "+Capi::describeParamInfo(info),"Capi::disconnect_req()")); +} + +void +Capi::facility_req (_cdword address, _cword FacilitySelector, _cstruct FacilityRequestParameter) throw (CapiMsgError) +{ + _cmsg CMSG; // Nachrichten-Struktur + if (debug_level >= 2) { + debug << prefix() << ">FACILITY_REQ ApplId 0x" << hex << applId << ", MsgNr 0x" << messageNumber << ", Address 0x" << address << ", FacilitySelector 0x" << FacilitySelector << endl; + } + unsigned info=FACILITY_REQ(&CMSG, applId, messageNumber++, address, FacilitySelector, FacilityRequestParameter); + if (debug_level >= 2) { + debug << prefix() << "info: " << info << endl; + } + + if (info != 0) + throw(CapiMsgError(info,"Error while FACILITY_REQ: "+Capi::describeParamInfo(info),"Capi::facility_req()")); +} + + +void +Capi::setListenFaxG3 (_cdword Controller) throw (CapiMsgError) +{ + usedCIPMask|=0x00020010; + + if (!Controller) { + unsigned char buf[64]; + for (int i=1;i<=numControllers;i++) { + unsigned info = CAPI20_GET_PROFILE(i, buf); + if (info!=0) + throw (CapiMsgError(info,"Error in CAPI20_GET_PROFILE: "+Capi::describeParamInfo(info),"Capi::setListenFaxG3()")); + + if ((buf[8] & 0x10 && buf[12] & 0x10 && buf[16] & 0x10) // is controller able to handle faxG3? + || (buf[8] & 0x10 && buf[12] & 0x10 && buf[16] & 0x20)) //faxG3ext + listen_req(i, usedInfoMask, usedCIPMask); // can throw CapiMsgError + } + } + else + listen_req(Controller, usedInfoMask, usedCIPMask); // can throw CapiMsgError +} + +void +Capi::setListenTelephony (_cdword Controller) throw (CapiMsgError) +{ + usedCIPMask|=0x00010012; + if (!Controller) { + unsigned char buf[64]; + for (int i=1;i<=numControllers;i++) { + unsigned info = CAPI20_GET_PROFILE(i, buf); + if (info!=0) + throw (CapiMsgError(info,"Error in CAPI20_GET_PROFILE: "+Capi::describeParamInfo(info),"Capi::setListenTelephony()")); + + if (buf[8] & 0x02 && buf[12] & 0x02 && buf[16] & 0x01) // is controller able to handle transparent? + listen_req(i, usedInfoMask, usedCIPMask); // can throw CapiMsgError + } + } + else + listen_req(Controller, usedInfoMask, usedCIPMask); // can throw CapiMsgError +} + +void +Capi::connect_resp (_cword messageNumber, _cdword plci, _cword reject, _cword B1protocol, _cword B2protocol, _cword B3protocol, _cstruct B1configuration, _cstruct B2configuration, _cstruct B3configuration) throw (CapiMsgError) +{ + if (debug_level >= 2) + debug << prefix() << ">CONNECT_RESP ApplId 0x" << hex << applId << ", msgNum 0x" << messageNumber << ", PLCI 0x" << plci << ", Reject 0x" + << reject << ", B1proto 0x" << B1protocol << ", B2proto 0x" << B2protocol << ", B3proto 0x" << B3protocol << endl; + + _cmsg new_message; + unsigned info=CONNECT_RESP(&new_message, applId, messageNumber, plci, reject, B1protocol, B2protocol, B3protocol, B1configuration, B2configuration, B3configuration, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + if (debug_level >= 2) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while CONNECT_REQ: "+Capi::describeParamInfo(info),"Capi::connect_resp()")); + +} + +void +Capi::connect_active_resp (_cword messageNumber, _cdword plci) throw (CapiMsgError) +{ + if (debug_level >= 2) + debug << prefix() << ">CONNECT_ACTIVE_RESP ApplId 0x" << hex << applId << " MsgNum 0x" << messageNumber << " PLCI 0x" << plci << endl; + + _cmsg new_message; + unsigned info=CONNECT_ACTIVE_RESP(&new_message, applId, messageNumber, plci); + if (debug_level >= 2) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while CONNECT_ACTIVE_RESP: "+Capi::describeParamInfo(info),"Capi::connect_active_resp()")); +} + +void +Capi::connect_b3_resp (_cword messageNumber, _cdword ncci, _cword reject, _cstruct ncpi) throw (CapiMsgError) +{ + if (debug_level >= 2) + debug << prefix() << ">CONNECT_B3_RESP ApplId 0x" << hex << applId << " MsgNum 0x" << messageNumber << " NCCI 0x" << ncci << " Reject 0x" << reject << endl; + + _cmsg new_message; + unsigned info=CONNECT_B3_RESP(&new_message, applId, messageNumber, ncci, reject, ncpi); + if (debug_level >= 2) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while CONNECT_B3_RESP: "+Capi::describeParamInfo(info),"Capi::connect_b3_resp()")); +} + +void +Capi::connect_b3_active_resp (_cword messageNumber, _cdword ncci) throw (CapiMsgError) +{ + if (debug_level >= 2) + debug << prefix() << ">CONNECT_B3_ACTIVE_RESP ApplId 0x" << hex << applId << " MsgNum 0x" << messageNumber << " NCCI 0x" << ncci << endl; + + _cmsg new_message; + unsigned info=CONNECT_B3_ACTIVE_RESP(&new_message, applId, messageNumber, ncci); + if (debug_level >= 2) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while CONNECT_B3_ACTIVE_RESP: "+Capi::describeParamInfo(info),"Capi::connect_b3_active_resp()")); +} + + +void +Capi::data_b3_resp (_cword messageNumber, _cdword ncci, _cword dataHandle) throw (CapiMsgError) +{ + if (debug_level >= 3) + debug << prefix() << ">DATA_B3_RESP, ApplId 0x" << hex << applId << ", msgNum 0x" << messageNumber << ", NCCI 0x" << ncci << ", DataHandle 0x" << dataHandle << endl; + + _cmsg new_message; + unsigned info=DATA_B3_RESP(&new_message, applId, messageNumber, ncci, dataHandle); + if (debug_level >= 3) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while DATA_B3_RESP: "+Capi::describeParamInfo(info),"Capi::data_b3_resp()")); +} + +void +Capi::facility_resp (_cword messageNumber, _cdword address, _cword facilitySelector, _cstruct facilityResponseParameter) throw (CapiMsgError) +{ + if (debug_level >= 2) + debug << prefix() << ">FACILITY_RESP ApplId 0x" << hex << applId << ", MsgNr 0x" << messageNumber << ", Address 0x" << address + << ", FacilitySelector 0x" << facilitySelector << endl; + + _cmsg new_message; + unsigned info=FACILITY_RESP(&new_message, applId, messageNumber, address, facilitySelector, facilityResponseParameter); + if (debug_level >= 2) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while FACILITY_RESP: "+Capi::describeParamInfo(info),"Capi::facility_resp()")); +} + + +void +Capi::disconnect_b3_resp (_cword messageNumber, _cdword ncci) throw (CapiMsgError) +{ + if (debug_level >= 2) + debug << prefix() << ">DISCONNECT_B3_RESP ApplId 0x" << hex << applId << " MsgNum 0x" << messageNumber << " NCCI 0x" << ncci << endl; + + _cmsg new_message; + unsigned info=DISCONNECT_B3_RESP(&new_message, applId, messageNumber, ncci); + if (debug_level >= 2) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while DISCONNECT_B3_RESP: "+Capi::describeParamInfo(info),"Capi::disconnect_b3_resp()")); +} + + +void +Capi::disconnect_resp (_cword messageNumber, _cdword plci) throw (CapiMsgError) +{ + if (debug_level >= 2) + debug << prefix() << ">DISCONNECT_RESP ApplId 0x" << hex << applId << " MsgNum 0x" << messageNumber << " PLCI 0x" << plci << endl; + + _cmsg new_message; + unsigned info=DISCONNECT_RESP(&new_message, applId, messageNumber, plci); + if (debug_level >= 2) + debug << prefix() << "info: " << info << endl; + + if (info != 0) + throw(CapiMsgError(info,"Error while DISCONNECT_RESP: "+Capi::describeParamInfo(info),"Capi::disconnect_resp()")); +} + + + +void +Capi::readMessage (void) throw (CapiMsgError, CapiError, CapiWrongState, CapiExternalError) +{ + _cmsg nachricht; + unsigned info=CAPI_GET_CMSG(&nachricht, applId); // don't use capi20_get_message here as CAPI_GET_CMSG does disassembling of message parameters for us + switch (info) { + case CapiNoError: //----- a message has been read ----- + switch (nachricht.Subcommand) { + case CAPI_CONF: // confirmation + switch (nachricht.Command) { + case CAPI_ALERT: { + _cdword plci=ALERT_CONF_PLCI(&nachricht); + if (debug_level >= 2) + debug << prefix() << "alert_conf(nachricht); + } break; + + case CAPI_CONNECT: { + _cdword pseudoPLCI=0xFACE0000 | nachricht.Messagenumber; // pseudo PLCI as set by connect_req + _cdword plci=CONNECT_CONF_PLCI(&nachricht); + if (debug_level >= 2) + debug << prefix() << "connect_conf(nachricht); + } + } break; + + case CAPI_CONNECT_B3: { + _cdword plci=CONNECT_B3_CONF_NCCI(&nachricht) & 0xFFFF; // PLCI is coded in the least 2 octets of NCCI + if (debug_level >= 2) + debug << prefix() << "connect_b3_conf(nachricht); + } break; + + case CAPI_SELECT_B_PROTOCOL: { + _cdword plci=SELECT_B_PROTOCOL_CONF_PLCI(&nachricht); + if (debug_level >= 2) + debug << prefix() << "select_b_protocol_conf(nachricht); + } break; + + case CAPI_LISTEN: + if (debug_level >= 2) + debug << prefix() << "= 3) + debug << prefix() << "data_b3_conf(nachricht); + } break; + + + case CAPI_FACILITY: + switch (FACILITY_CONF_FACILITYSELECTOR(&nachricht)) { + case 1: { // DTMF + _cdword plci=FACILITY_CONF_PLCI(&nachricht) & 0xFFFF; // this *should* be PLCI but who knows, so let's mask it to be sure + if (debug_level >= 2) + debug << prefix() << "facility_conf_DTMF(nachricht); + } break; + + default: + error << prefix() << "WARNING: PLCI " << hex << FACILITY_CONF_PLCI(&nachricht) << ": unsupported facility selector " << FACILITY_CONF_FACILITYSELECTOR(&nachricht) << " in FACILITY_CONF" << endl; + break; + } + break; + + case CAPI_DISCONNECT_B3: { + _cdword plci=DISCONNECT_B3_CONF_NCCI(&nachricht) & 0xFFFF; // PLCI is coded in the least 2 octets of NCCI + if (debug_level >= 2) + debug << prefix() << "disconnect_b3_conf(nachricht); + } break; + + case CAPI_DISCONNECT: { // TODO: perhaps we should handle NCPI telling us fax infos here?? + _cdword plci=DISCONNECT_CONF_PLCI(&nachricht); + if (debug_level >= 2) + debug << prefix() << "disconnect_conf(nachricht); + } break; + } + break; + + case CAPI_IND: // indication + switch (nachricht.Command) { + case CAPI_CONNECT: { // call for us + _cdword plci=CONNECT_IND_PLCI(&nachricht); + if (debug_level >= 2) + debug << prefix() << "0) + throw(CapiError("PLCI used twice from CAPI in CONNECT_IND","Capi::readMessage()")); + else { + Connection *c=new Connection(nachricht,this); + connections[plci]=c; + application->callWaiting(c); + } + } break; + + case CAPI_CONNECT_ACTIVE: { + _cdword plci=CONNECT_IND_PLCI(&nachricht); + if (debug_level >= 2) + debug << prefix() << "connect_active_ind(nachricht); + } break; + + case CAPI_CONNECT_B3: { + _cdword plci=CONNECT_B3_IND_NCCI(&nachricht) & 0xFFFF; // PLCI is coded in the least 2 octets of NCCI + if (debug_level >= 2) + debug << prefix() << "connect_b3_ind(nachricht); + } break; + + case CAPI_CONNECT_B3_ACTIVE: { + _cdword plci=CONNECT_B3_ACTIVE_IND_NCCI(&nachricht) & 0xFFFF; // PLCI is coded in the least 2 octets of NCCI + if (debug_level >= 2) + debug << prefix() << "connect_b3_active_ind(nachricht); + } break; + + case CAPI_DISCONNECT: { // call gone, we'll confirm to CAPI + _cdword plci=DISCONNECT_IND_PLCI(&nachricht); + if (debug_level >= 2) + debug << prefix() << "disconnect_ind(nachricht); + } + } break; + + case CAPI_DISCONNECT_B3: { + _cdword plci=DISCONNECT_B3_IND_NCCI(&nachricht) & 0xFFFF; // PLCI is coded in the least 2 octets of NCCI + if (debug_level >= 2) + debug << prefix() << "disconnect_b3_ind(nachricht); + } break; + + case CAPI_DATA_B3: { + _cdword plci=DATA_B3_IND_NCCI(&nachricht) & 0xFFFF; // PLCI is coded in the least 2 octets of NCCI + if (debug_level >= 3) + debug << prefix() << "data_b3_ind(nachricht); + } break; + + case CAPI_FACILITY: + switch (FACILITY_IND_FACILITYSELECTOR(&nachricht)) { + case 1: { // DTMF + _cdword plci=FACILITY_IND_PLCI(&nachricht) & 0xFFFF; // we *should* get PLCI but just to be sure we mask the NCCI-part out... + if (debug_level >= 2) + debug << prefix() << "facility_ind_DTMF(nachricht); + } break; + + default: + error << prefix() << "WARNING: PLCI " << hex << (FACILITY_IND_PLCI(&nachricht) & 0xFFFF) << ": Unsupported facility selector " << FACILITY_IND_FACILITYSELECTOR(&nachricht) << " in FACILITY_IND" << endl; + break; + } + break; + + + default: + stringstream err; + err << "Indication 0x" << hex << nachricht.Command << " not handled" << ends; + throw (CapiError(err.str(),"Capi::readMessage()")); + break; + } + break; + + default: //----- neither indication nor confirmation ???? ----- + throw(CapiError("Unknown subcommand in function Handle_CAPI_Msg","Capi::readMessage()")); + break; + } + break; + case CapiReceiveQueueEmpty: + throw (CapiError("readMessage called but no message available?","Capi::readMessage()")); + break; + + default: + throw (CapiMsgError(info,"Error while CAPI_GET_CMSG: "+Capi::describeParamInfo(info),"Capi::readMessage()")); + break; + } +} + +void +Capi::run() +{ + while (1) { + pthread_testcancel(); + unsigned info=capi20_waitformessage (applId, NULL); // will block until message is available or capi20_release called + try { + if (info==CapiNoError) { + if (debug_level >= 3) + debug << prefix() << "*" << endl; + readMessage(); // trigger message reading + if (debug_level >= 3) + debug << prefix() << "**" << endl; + } + } + catch (CapiError e) { + error << prefix() << "ERROR: Connection " << this << ": Error in readMessage(), messagge: " << e << endl; + } + } +} + +string +Capi::describeParamInfo (unsigned int info) +{ + switch (info) + { + // class 0x00xx: Informative values, no error (see CAPI 2.0 4th ed. PartI, chapter 6.1.26) + case 0x0000: + return "request accepted."; + case 0x0001: + return "warning: NCPI not supported by current protocol, NCPI ignored."; + case 0x0002: + return "warnung: Flags not supported by current protocol, flags ignored."; + case 0x0003: + return "warnung: Alert already sent by another application."; + // class 0x10xx: Error information concerning CAPI_REGISTER + case 0x1001: + return "Too many applications."; + case 0x1002: + return "Logical Block size too small; must be at least 128 bytes."; + case 0x1003: + return "Buffer exceeds 64 kbytes."; + case 0x1004: + return "Message buffer size too small, must be at least 1024 bytes."; + case 0x1005: + return "Max. number of logical connections not supported."; + case 0x1006: + return "reserved (unknown error)."; + case 0x1007: + return "The message could not be accepted because of an internal busy condition."; + case 0x1008: + return "OS Resource error (out of memory?)."; + case 0x1009: + return "CAPI not installed."; + case 0x100A: + return "Controller does not support external equipment."; + case 0x100B: + return "Controller does only support external equipment."; + // class 0x11xx: Error information concerning message exchange functions + case 0x1101: + return "Illegal application number."; + case 0x1102: + return "Illegal command or subcommand, or message length less than 12 octets."; + case 0x1103: + return "The message could not be accepted because of a queue full condition."; + case 0x1104: + return "Queue is empty."; + case 0x1105: + return "Queue overflow: a message was lost!!"; + case 0x1106: + return "Unknown notification parameter."; + case 0x1107: + return "The message could not be accepted because on an internal busy condition."; + case 0x1108: + return "OS resource error (out of memory?)."; + case 0x1109: + return "CAPI not installed."; + case 0x110A: + return "Controller does not support external equipment."; + case 0x110B: + return "Controller does only support external equipment."; + // class 0x20xx: Error information concerning resource/coding problems + case 0x2001: + return "Message not supported in current state."; + case 0x2002: + return "Illegal Controller/PLCI/NCCI."; + case 0x2003: + return "No PLCI available."; + case 0x2004: + return "No NCCI available."; + case 0x2005: + return "No Listen resources available."; + case 0x2006: + return "No fax resources available (protocol T.30 not supported)."; + case 0x2007: + return "Illegal message parameter coding."; + case 0x2008: + return "No interconnection resources available."; + // class 0x30xx: Error information concerning requested services + case 0x3001: + return "B1 protocol not supported."; + case 0x3002: + return "B2 protocol not supported."; + case 0x3003: + return "B3 protocol not supported."; + case 0x3004: + return "B1 protocol parameter not supported."; + case 0x3005: + return "B2 protocol parameter not supported."; + case 0x3006: + return "B3 protocol parameter not supported."; + case 0x3007: + return "B protocol combination not supported."; + case 0x3008: + return "NCPI not supported."; + case 0x3009: + return "CIP Value unknown."; + case 0x300A: + return "Flags nor supported (reserved bits used!)."; + case 0x300B: + return "Facility not supported."; + case 0x300C: + return "Data length not supported by current protocol."; + case 0x300D: + return "Reset procedure not supported by current protocol."; + case 0x300E: + return "TEI assignment failed / overlapping channel masks."; + case 0x300F: + return "Unsupported interoperability (see CAPI 2.0 specification, Part IV)."; + case 0x3010: + return "Request not allowed in this state."; + case 0x3011: + return "Facility specific function not supported."; + + default: + stringstream message; + message << "CAPI returned an unknown error! Please ask your manufacturer for assistance (error code=0x" << hex << info << ")"; + return message.str(); + } +} + +string +Capi::prefix() +{ + stringstream s; + time_t t=time(NULL); + char* ct=ctime(&t); + ct[24]='\0'; + s << ct << " Capi " << hex << this << ": "; + return (s.str()); +} + +string +Capi::getInfo(bool verbose) throw (CapiMsgError) +{ + unsigned char buf[64]; + _cdword buf2[4]; + stringstream tmp; + + // is CAPI correctly installed? + unsigned info=CAPI20_ISINSTALLED(); + if (info!=0) + throw (CapiMsgError(info,"Error in CAPI20_ISINSTALLED: "+describeParamInfo(info),"Capi::getCapiInfo()")); + + // retrieve number of installed controllers and create array for storing the descriptions + info=CAPI20_GET_PROFILE (0, buf); + if (info!=0) + throw (CapiMsgError(info,"Error in CAPI20_GET_PROFILE: "+describeParamInfo(info),"Capi::getCapiInfo()")); + + numControllers=buf[0]+(buf[1] << 8); + + tmp << numControllers << " controllers found" << endl; + + if (verbose) + { + // retrieve general information (kernel driver manufacturer, version of kernel driver) + if (capi20_get_manufacturer(0,buf)) + tmp << reinterpret_cast (buf); + else + tmp << "unknown"; + + if (capi20_get_version(0, reinterpret_cast(buf2))) { + tmp << ", version " << buf2[0] << "." << buf2[1] << "/" << buf2[2] << "." << buf2[3] << endl; + } else + tmp << ", version unknown" << endl; + + // retrieve controller specific information (manufacturer, version) + for (unsigned i=1;i<=numControllers;i++) { + tmp << "Controller " << i << ": "; + + if (capi20_get_manufacturer(i,buf)) + tmp << reinterpret_cast (buf); + else + tmp << "unknown"; + + info = CAPI20_GET_PROFILE(i, buf); + if (info!=0) + throw (CapiMsgError(info,"Error in CAPI20_GET_PROFILE/2: "+Capi::describeParamInfo(info),"Capi::getCapiInfo()")); + + tmp << " (" << buf[2] + (buf[3]<<8) << " B channels"; + + if (buf[4] & 0x08) + tmp << ", DTMF"; + if (buf[4] & 0x10) + tmp << ", SuppServ"; + if (buf[8] & 0x02 && buf[12] & 0x02 && buf[16] & 0x01) + tmp << ", transparent"; + if (buf[8] & 0x10 && buf[12] & 0x10 && buf[16] & 0x10) + tmp << ", FaxG3"; + if (buf[8] & 0x10 && buf[12] & 0x10 && buf[16] & 0x20) + tmp << ", FaxG3ext"; + + if (capi20_get_version(i,reinterpret_cast(buf2))) + tmp << "), driver version " << buf2[0] << "." << buf2[1] << "/" << buf2[2] << "." << buf2[3]; + else + tmp << "), unknown driver version"; + + tmp << endl; + } + } + return tmp.str(); +} + +/* History + +$Log: capi.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.28 2003/02/10 14:20:52 ghillie +merged from NATIVE_PTHREADS to HEAD + +Revision 1.27.2.1 2003/02/09 15:05:36 ghillie +- rewritten to use native pthread_* calls instead of CommonC++ Thread + +Revision 1.27 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.26 2003/01/06 16:29:25 ghillie +- added debug output for constructor/destructor +- added call to terminate() in destructor +- missing break added in readMessage (oops... :-|) +- consider CapiReceiveQueueEmpty as error in readMessage() +- support finishing in run() +- only call readMessage() when waitformessage returned CapiNoError + +Revision 1.25 2003/01/04 16:07:42 ghillie +- log improvements: log_level, timestamp + +Revision 1.24 2002/12/18 14:40:23 ghillie +- removed this nasty listen_state. It had no sense than making problems. +- small change in getInfo() +- added check for abilities in setListen* if you say 0 for all controllers. + Do nothing if this ability isn't provided for this controller + +Revision 1.23 2002/12/13 09:57:10 ghillie +- error message formatting done by exception classes now + +Revision 1.22 2002/12/09 15:32:58 ghillie +- debug stream now given in constructor +- debug output improvements +- exception cleanup (removed WARNING exceptions, output messages, ...) + +Revision 1.21 2002/12/06 12:55:28 ghillie +- don't delete Connection objects any more + +Revision 1.20 2002/12/05 15:00:06 ghillie +- new methods registerApplicationInterface(), unregisterConnection() +- use debug stream given in constructor, not from ApplicationInterface any more +- connect_req gets Connection* now, enters it with pseudoPLCI in connections map +- readMessage(): when receiving CONNECT_CONF: searches for pseudoPLCI in connections and moves this entry to real plci +- readMessage(): when receiving DISCONNECT_INF: don't erase Connection object from connections map, this will be done in Connection::~Connection + +Revision 1.19 2002/12/02 16:53:51 ghillie +- minor debug output change + +Revision 1.18 2002/11/29 11:11:12 ghillie +- moved communication thread from own class (CapiCommThread) to Capi class + +Revision 1.17 2002/11/29 10:22:19 ghillie +- updated comments, use doxygen format now +- fixed small typo in setListen* methods + +Revision 1.16 2002/11/27 15:58:52 ghillie +caught exceptions from disconnect_ind() + +Revision 1.15 2002/11/25 20:57:21 ghillie +setListen* methods can now set listen state on all found controllers + +Revision 1.14 2002/11/22 15:08:22 ghillie +- new method select_b_protocol_req() +- added SELECT_B_PROTOCOL_CONF case in readMessage() + +Revision 1.13 2002/11/19 15:57:18 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.12 2002/11/18 14:24:09 ghillie +- moved global severity_t to CapiError::severity_t +- added throw() declarations + +Revision 1.11 2002/11/17 14:38:34 ghillie +- improved exception throwing: using different exception types with severity parameter (FATAL,ERROR,WARNING) now +- changed getInfo() to use macros from /usr/include/capiutils.h now + +Revision 1.10 2002/11/15 15:25:53 ghillie +added ALERT_REQ so we don't loose a call when we wait before connection establishment + +Revision 1.9 2002/11/15 13:49:10 ghillie +fix: callmodule wasn't aborted when call was only connected/disconnected physically + +Revision 1.8 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.7 2002/11/10 17:04:21 ghillie +improved some debug output + +Revision 1.6 2002/11/08 07:57:07 ghillie +added functions to initiate a call +corrected FACILITY calls to use PLCI instead of NCCI in DTMF processing as told by Mr. Ortmann on comp.dcom.isdn.capi + +Revision 1.5 2002/10/31 15:39:04 ghillie +added missing FACILITY_RESP message (oops...) + +Revision 1.4 2002/10/31 12:37:34 ghillie +added DTMF support + +Revision 1.3 2002/10/29 15:42:29 ghillie +typos... + +Revision 1.2 2002/10/29 14:09:12 ghillie +fixed order in DISCONNECT_ROUTINE: first call callCompleted, then delete connection object! +cosmetic fixes + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.12 2002/10/09 11:18:59 gernot +cosmetic changes (again...) and changed info function of CAPI class + +Revision 1.11 2002/10/05 20:43:32 gernot +quick'n'dirty, but WORKS + +Revision 1.10 2002/10/05 13:53:00 gernot +changed to use thread class of CommonC++ instead of the threads-package +some cosmetic improvements (indentation...) + +Revision 1.9 2002/10/04 15:56:34 gernot +reactivated error handling for Capi::getInfo() + +Revision 1.8 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.7 2002/10/04 13:27:15 gernot +some restructuring to get it to a working state ;-) + +does not do anything useful yet nor does it even compile... + +Revision 1.6 2002/10/02 14:10:07 gernot +first version + +Revision 1.5 2002/10/01 09:02:04 gernot +changes for compilation with gcc3.2 + +Revision 1.4 2002/09/22 14:22:53 gernot +some cosmetic comment improvements ;-) + +Revision 1.3 2002/09/19 12:08:19 gernot +added magic CVS strings + +* Sun Sep 15 2002 - gernot@hillier.de +- put under CVS, cvs changelog follows + +* Sun May 19 2002 - gernot@hillier.de +- changed to not using QT libs any more + +* Sun Apr 1 2002 - gernot@hillier.de +- first version + +*/ diff --git a/src/backend/capi.h b/src/backend/capi.h new file mode 100644 index 0000000..ed08e64 --- /dev/null +++ b/src/backend/capi.h @@ -0,0 +1,500 @@ +/** @file capi.h + @brief Contains Capi - Main Class for communication with CAPI + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +***************************************************************************/ + +#ifndef CAPI_H +#define CAPI_H + +#include +#include +#include +#include "capiexception.h" + +using namespace std; + +class Connection; +class ApplicationInterface; + +/** @brief Thread exec handler for Capi class + + This is a handler which will call this->run() for the use in pthread_create(). +*/ +void* capi_exec_handler(void* args); + +/** @brief Main Class for communication with CAPI + + This class is the main encapsulation to use the CAPI ISDN interface. + + There are only a small subset of methods which are of use for the application layer. These are for general purposes like enabling + listening to calls and getting some nice formatted info strings. + + The biggest amount are shadow methods for nearly all CAPI messages, which do the dumb stuff like increasing message numbers, + testing for errors, building message structures and so on. Not each parameter of these is described in every detail here. + For more details please have a look in the CAPI 2.0 specification, available from http://www.capi.org + + There's also a big message handling routine (readMessage()) which calls special handlers for incoming messages of the CAPI. + + A Capi object creates a new thread (with body run()) which waits for incoming messages in an endless loop and hands them to readMessage(). + + This class only does the general things - for handling single connections see Connection. Connection objects will be automatically created + by this class for incoming connections and can be created manually to initiate an outgoing connection. + + The application is supposed to create one single object of this class. + + To communicate with the application via callback functions, the application must provide an implementation of the ApplicationInterface. + The methods of this interface are called when some special events are received. + + @author Gernot Hillier +*/ +class Capi { + friend class Connection; + friend void* capi_exec_handler(void*); + + public: + /** @brief Constructor. Registers our App at CAPI and start the communication thread. + + @param debug reference to a ostream object where debug info should be written to + @param debug_level verbosity level for debug messages + @param error reference to a ostream object where errors should be written to + @param maxLogicalConnection max. number of logical connections we will handle + @param maxBDataBlocks max. number of unconfirmed B3-datablocks, 7 is the maximum supported by CAPI + @param maxBDataLen max. B3-Datablocksize, 2048 is the maximum supported by CAPI + @throw CapiError Thrown if no ISDN controller is reported by CAPI + @throw CapiMsgError Thrown if registration at CAPI wasn't successful. + */ + Capi (ostream &debug, unsigned short debug_level, ostream &error, unsigned maxLogicalConnection=2, unsigned maxBDataBlocks=7,unsigned maxBDataLen=2048) throw (CapiError, CapiMsgError); + + /** @brief Destructor. Unregister App at CAPI + + @throw CapiMsgError Thrown if deregistration at CAPI failed. + */ + ~Capi(); + + /** @brief Register the instance implementing the ApplicationInterface + + @param application_in pointer to a class implementing (derived from) ApplicationInterface + */ + void registerApplicationInterface(ApplicationInterface* application_in); + + /** @brief Tell capi that we want to _additionally_ listen to Fax G3 calls + + This method enables listening to fax calls from the analog network (coded as Bearer Capability 3.1kHz audio and + from ISDN coded as fax group 3. The previously set listen mask is not cleared, so that the application + can call this method after another listen request w/o loosing the other services. + + @param Controller Nr. of Controller (0 = all available controllers) + @throw CapiMsgError Thrown by listen_req, see there for details + */ + void setListenFaxG3 (_cdword Controller=0) throw (CapiMsgError); + + /** @brief Tell capi that we want to _additionally_ listen to Telephony calls + + This method enables listening to speech calls from the analog network (coded as Bearer Capability 3.1kHz audio) and + from ISDN (coded as Bearer Capability Speech or High Layer Compatibility telephony). The previously set listen mask + is not cleared, so that the application can call this method after another listen request w/o loosing the other services. + + @param Controller Nr. of Controller (0 = all available controllers) + @throw CapiMsgError Thrown by listen_req, see there for details + */ + void setListenTelephony (_cdword Controller=0) throw (CapiMsgError); + + /** @brief Static function which returns some info about the installed Controllers + + The returned string has the following format (UPPERCASE words replaced): + + "NUM controllers found. + CAPI_MANUFACTURER, version VERSION_OF_CAPI/SUBVERSION_OF_CAPI + Controller X: CONTROLLER_MANUFACTURER (NUM B channels, SERVICE1, SERVICE2, ..., driver version DRIVER_VERSION/DRIVER_SUBVERSION + Controller X+1: CONTROLLER_MANUFACTURER (NUM B channels, SERVICE1, SERVICE2, ..., driver version DRIVER_VERSION/DRIVER_SUBVERSION + ..." + + If verbose is set to false, only the first line is returned. + + The following services are checked currently and printed as SERVICEX if found: DTMF, SuppServ, transparent, FaxG3, FaxG3ext + + @param verbose controls verbosity of output (see above). + @return string containing details (see above). + */ + static string getInfo(bool verbose=false) throw (CapiMsgError); + + private: + + /** @brief erase Connection object in connections map + + This method is used by Connection::~Connection() + */ + void unregisterConnection (_cdword plci); + + /********************************************************************************/ + /* methods to send CAPI messages - called by the Connection class */ + /********************************************************************************/ + + /*************************** REQUESTS *******************************************/ + + /** @brief Send LISTEN_REQ to CAPI + + @param Controller Nr. of Controller + @param InfoMask see CAPI 2.0 spec, ch 5.37, default = 0x03FF -> all available info elements + @param CIPMask see CAPI 2.0 spec, ch 5.37, default = 0x1FFF03FF -> all available services + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void listen_req (_cdword Controller, _cdword InfoMask=0x03FF, _cdword CIPMask=0x1FFF03FF) throw (CapiMsgError); + + /** @brief Send ALERT_REQ to CAPI + + @param plci reference to physical connection + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void alert_req (_cdword plci) throw (CapiMsgError); + + /** @brief Send CONNECT_REQ to CAPI + + To be able to see which CONNECT_CONF corresponds to this CONNECT_REQ, the Connection object + will be saved in the connections map under a pseudo PLCI (0xFACE & messageNumber). This will + be corrected at the moment the CONNECT_CONF is received. + + @param conn reference to the Connection object which calls connect_req() + @param Controller Nr. of controller to use for connection establishment + @param CIPvalue CIP (service indicator) to use for the connection + @param calledPartyNumber The number of the party which is called (i.e. the destination of the call) + @param callingPartyNumber The number of the party which calls (i.e. the source of the call) + @param B1protocol see CAPI spec for details + @param B2protocol see CAPI spec for details + @param B3protocol see CAPI spec for details + @param B1configuration see CAPI spec for details + @param B2configuration see CAPI spec for details + @param B3configuration see CAPI spec for details + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void connect_req (Connection *conn, _cdword Controller, _cword CIPvalue, _cstruct calledPartyNumber, _cstruct callingPartyNumber, _cword B1protocol, _cword B2protocol, _cword B3protocol, _cstruct B1configuration, _cstruct B2configuration, _cstruct B3configuration) throw (CapiMsgError); + + /** @brief send SELECT_B_PROTOCOL_REQ to CAPI + + @param plci reference to physical connection + @param B1protocol see CAPI spec for details + @param B2protocol see CAPI spec for details + @param B3protocol see CAPI spec for details + @param B1configuration see CAPI spec for details + @param B2configuration see CAPI spec for details + @param B3configuration see CAPI spec for details + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void select_b_protocol_req (_cdword plci, _cword B1protocol, _cword B2protocol, _cword B3protocol, _cstruct B1configuration, _cstruct B2configuration, _cstruct B3configuration) throw (CapiMsgError); + + /** @brief send CONNECT_B3_REQ to CAPI + + @param plci reference to physical connection + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void connect_b3_req (_cdword plci) throw (CapiMsgError); + + /** @brief send DATA_B3_REQ to CAPI + + @param ncci reference to physical connection + @param Data pointer to transmission data + @param DataLength length of transmission data + @param DataHandle some word value which will be referred to in DATA_B3_CONF (to see which data packet was sent successful) + @param Flags see CAPI 2.0 spec + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void data_b3_req (_cdword ncci, void* Data, _cword DataLength,_cword DataHandle,_cword Flags) throw (CapiMsgError); + + /** @brief send DISCONNECT_B3_REQ to CAPI + + @param ncci reference to physical connection + @param ncpi protocol specific info + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void disconnect_b3_req (_cdword ncci, _cstruct ncpi=NULL) throw (CapiMsgError); + + /** @brief send DISCONNECT_REQ to CAPI + + @param plci reference to physical connection + @param Keypadfacility see CAPI spec + @param Useruserdata see CAPI spec + @param Facilitydataarray see CAPI spec + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void disconnect_req (_cdword plci, _cstruct Keypadfacility=NULL, _cstruct Useruserdata=NULL, _cstruct Facilitydataarray=NULL) throw (CapiMsgError); + + /** @brief send FACILITY_REQ to CAPI + + @param address Nr. of connection (Controller/PLCI/NCCI) + @param FacilitySelector see CAPI spec (1=DTMF) + @param FacilityRequestParameter see CAPI spec (too long to describe it here...) + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void facility_req (_cdword address, _cword FacilitySelector, _cstruct FacilityRequestParameter) throw (CapiMsgError); + + /*************************** RESPONSES *******************************************/ + + /** @brief send CONNECT_RESP to CAPI + + @param messageNumber number of the referred INDICATION message + @param plci reference to physical connection + @param reject tell CAPI if we want to accept (0) or reject (!=0, for details see CAPI spec) the incoming call + @param B1protocol see CAPI spec for details + @param B2protocol see CAPI spec for details + @param B3protocol see CAPI spec for details + @param B1configuration see CAPI spec for details + @param B2configuration see CAPI spec for details + @param B3configuration see CAPI spec for details + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void connect_resp (_cword messageNumber, _cdword plci, _cword reject, _cword B1protocol, _cword B2protocol, _cword B3protocol, _cstruct B1configuration, _cstruct B2configuration, _cstruct B3configuration) throw (CapiMsgError); + + /** @brief send CONNECT_ACTIVE_RESP to CAPI + + @param messageNumber number of the referred INDICATION message + @param plci reference to physical connection + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void connect_active_resp (_cword messageNumber, _cdword plci) throw (CapiMsgError); + + /** @brief send CONNECT_B3_RESP to CAPI + + @param messageNumber number of the referred INDICATION message + @param ncci reference to physical connection + @param reject tell CAPI if we want to accept (0) or reject (2) the incoming call + @param ncpi protocol specific info + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void connect_b3_resp (_cword messageNumber, _cdword ncci, _cword reject, _cstruct ncpi) throw (CapiMsgError); + + /** @brief send CONNECT_B3_ACTIVE_RESP to CAPI + + @param messageNumber number of the referred INDICATION message + @param ncci reference to physical connection + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void connect_b3_active_resp (_cword messageNumber, _cdword ncci) throw (CapiMsgError); + + /** @brief send DATA_B3_RESP to CAPI + + @param messageNumber number of the referred INDICATION message + @param ncci reference to physical connection + @param dataHandle Data Handle given by the referred DATA_B3_IND + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void data_b3_resp (_cword messageNumber, _cdword ncci, _cword dataHandle) throw (CapiMsgError); + + /** @brief send FACILITY_RESP to CAPI + + @param messageNumber number of the referred INDICATION message + @param address Nr. of connection (Controller/PLCI/NCCI) + @param facilitySelector see CAPI spec (1=DTMF) + @param facilityResponseParameter see CAPI spec + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void facility_resp (_cword messageNumber, _cdword address, _cword facilitySelector, _cstruct facilityResponseParameter=NULL) throw (CapiMsgError); + + /** @brief send DISCONNECT_RESP to CAPI + + @param messageNumber number of the referred INDICATION message + @param plci reference to physical connection + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void disconnect_resp (_cword messageNumber, _cdword plci) throw (CapiMsgError); + + /** @brief send DISCONNECT_B3_RESP to CAPI + + @param messageNumber number of the referred INDICATION message + @param ncci reference to physical connection + @throw CapiMsgError Thrown when CAPI_PUT_MESSAGE returned an error. + */ + void disconnect_b3_resp (_cword messageNumber, _cdword ncci) throw (CapiMsgError); + + /********************************************************************************/ + /* main message handling method for incoming msgs */ + /********************************************************************************/ + + /** @brief read Message from CAPI and process it accordingly + + This method handles all incoming messages. It is called by run() and will call + special handler methods of Connection mainly. Prints messages for debug purposes. + + @throw CapiMsgError directly raised when CAPI_GET_MESSAGE or LISTEN_REQ fails, may also be raised by all called *_ind, *_conf handlers + @throw CapiError directly raised when general error occurs (unknown call references, unknown message, ... received) + @throw CapiWrongState may be raised by all called *_ind(), *_conf() handlers + @throw CapiExternalError may be raised by some called *_ind(), *_conf() handlers + */ + void readMessage (void) throw (CapiMsgError, CapiError, CapiWrongState, CapiExternalError); + + /********************************************************************************/ + /* methods for internal use */ + /********************************************************************************/ + + /** @brief textual description for Parameter Info + + This method returns an error string for the given info parameter. The strings were + taken out of the CAPI 2.0 spec + + @param info errorcode as given by CAPI + @return textual description of error + */ + static string describeParamInfo (unsigned int info); + + /** @brief getApplId returns the application id we got from CAPI + + @return Application ID from CAPI + */ + unsigned short getApplId(void) {return applId;} + + /** @brief Thread body - endless loop, will be blocked until message is received and then call readMessage() + */ + virtual void run(void); + + /** @brief return a prefix containing this pointer and date for log messages + + @return constructed prefix as string + */ + string prefix(); + + /********************************************************************************/ + /* attributes */ + /********************************************************************************/ + + map <_cdword,Connection*> connections; ///< @brief containing pointers to the currently active Connection + ///< objects, referenced by PLCI (or 0xFACE & messageNum when Connection is in plci_state Connection::P01 + + static short numControllers; ///< number of installed controllers, set by getInfo() method + + _cword messageNumber; ///< sequencial message number, must be increased for every sent message + _cdword usedInfoMask; ///< InfoMask currently used (in last listen_req) + _cdword usedCIPMask; ///< CIPMask currently used (in last listen_req) + + unsigned applId; ///< containing the ID we got from CAPI after successful registration + + ApplicationInterface *application; ///< pointer to the application object implementing ApplicationInterface + ostream &debug, ///< stream to write debug info to + &error; ///< stream for error messages + unsigned short debug_level; ///< debug level + + pthread_t thread_handle; ///< handle for the created message reading thread +}; + +#endif + +/* History + +$Log: capi.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.22 2003/02/10 14:20:52 ghillie +merged from NATIVE_PTHREADS to HEAD + +Revision 1.21.2.1 2003/02/09 15:05:36 ghillie +- rewritten to use native pthread_* calls instead of CommonC++ Thread + +Revision 1.21 2003/01/06 16:29:52 ghillie +- destructor doesn't throw any exceptions any more + +Revision 1.20 2003/01/04 16:07:42 ghillie +- log improvements: log_level, timestamp + +Revision 1.19 2002/12/18 14:40:44 ghillie +- removed this nasty listen_state. Made nothing than problems + +Revision 1.18 2002/12/11 13:05:34 ghillie +- minor comment improvements + +Revision 1.17 2002/12/09 15:33:23 ghillie +- debug and error stream now given in constructor + +Revision 1.16 2002/12/05 15:02:36 ghillie +- constructor: removed param application (pointer to ApplicationInterface, now given by registerApplInterface()), added param debug giving debug stream +- new methods registerApplicationInterface(), unregisterConnection() +- connect_req gets COnnection* now + +Revision 1.15 2002/11/29 11:38:13 ghillie +- missed some changes because CapiCommThread was deleted + +Revision 1.14 2002/11/29 11:11:12 ghillie +- moved communication thread from own class (CapiCommThread) to Capi class + +Revision 1.13 2002/11/29 10:23:07 ghillie +- updated comments, use doxygen format now + +Revision 1.12 2002/11/27 15:58:13 ghillie +updated comments for doxygen + +Revision 1.11 2002/11/25 20:58:47 ghillie +- improved documentation, is now readable by doxygen +- setListen* can now set listen state for all available controllers + +Revision 1.10 2002/11/22 15:08:22 ghillie +- new method select_b_protocol_req() +- added SELECT_B_PROTOCOL_CONF case in readMessage() + +Revision 1.9 2002/11/19 15:57:18 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.8 2002/11/18 14:24:09 ghillie +- moved global severity_t to CapiError::severity_t +- added throw() declarations + +Revision 1.7 2002/11/17 14:39:23 ghillie +removed CapiError from this header -> exceptions are now defined in capiexception.h + +Revision 1.6 2002/11/15 15:25:53 ghillie +added ALERT_REQ so we don't loose a call when we wait before connection establishment + +Revision 1.5 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.4 2002/11/08 07:57:07 ghillie +added functions to initiate a call +corrected FACILITY calls to use PLCI instead of NCCI in DTMF processing as told by Mr. Ortmann on comp.dcom.isdn.capi + +Revision 1.3 2002/10/31 15:39:04 ghillie +added missing FACILITY_RESP message (oops...) + +Revision 1.2 2002/10/31 12:37:35 ghillie +added DTMF support + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.8 2002/10/09 14:36:22 gernot +added CallModule base class for all call handling modules + +Revision 1.7 2002/10/09 11:18:59 gernot +cosmetic changes (again...) and changed info function of CAPI class + +Revision 1.6 2002/10/08 12:01:26 gernot +cosmetic... (indentation) + +Revision 1.5 2002/10/01 09:02:04 gernot +changes for compilation with gcc3.2 + +Revision 1.4 2002/09/22 14:22:53 gernot +some cosmetic comment improvements ;-) + +Revision 1.3 2002/09/19 12:08:19 gernot +added magic CVS strings + +* Sun Sep 15 2002 - gernot@hillier.de +- put under CVS, cvs changelog follows above + +* Sun May 19 2002 - gernot@hillier.de +- changed to not using QT libs any more +- modified to conform to CAPI20-Spec, 4th edition (parameter names, ...) + +* Sun Apr 1 2002 - gernot@hillier.de +- first version + +*/ diff --git a/src/backend/capiexception.h b/src/backend/capiexception.h new file mode 100644 index 0000000..b48266b --- /dev/null +++ b/src/backend/capiexception.h @@ -0,0 +1,209 @@ +/** @file capiexception.h + @brief Contains exception classes for errors in the CAPI abstraction layer + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CAPIEXCEPTION_H +#define CAPIEXCEPTION_H + +#include +#include +#include + +using namespace std; + +/** @brief General and base class for errors in the Capi abstraction layer. + + This is the general class for all Capi errors. It serves as base class for the more specific exceptions + and also as one-size-fits-all throwable object if the other errors doesn't fit. ;-) + + Each exception gets a severity (Warning, Error or Fatal), a message and the name of the function where it occurred. + If you need further data, please derive a sub-class or format it into the errormsg. + + @author Gernot Hillier +*/ +class CapiError +{ + public: + /** @brief Constructor. Create an object, print error message and abort if severity FATAL was chosen. + + @param errormsg some informal message describing the error + @param function_name name of the function which throws this exception + */ + CapiError(string errormsg,string function_name): + errormsg(errormsg),function_name(function_name) + {} + + /** @brief Return nice formatted error message + + Returns the string "Classname: error message occured in function()" + @return error message + */ + virtual string message() + { + return ("CapiError: "+errormsg+" occured in "+function_name); + } + + protected: + string errormsg; ///< textual error message + string function_name; ///< function/method where this error occured +}; + +/** @brief Capi Abstraction Layer exception class thrown if something should be done in a wrong state. + + This exception is thrown if the Connection is in a wrong state (see Connection::ncci_state and Connection::plci_state) + when something connection related should be done. This usually means the call was disconnected by the other party + and should be handled by clearing the ressources controlling the call. + + @author Gernot Hillier +*/ +class CapiWrongState : public CapiError +{ + public: + /** @brief Constructor. Create an object, print error message and abort if severity FATAL was chosen. + + @param errormsg some informal message describing the error + @param function_name name of the function which throws this exception + */ + CapiWrongState(string errormsg,string function_name): + CapiError("CapiWrongstate: "+errormsg,function_name) + {} + + /** @brief Return nice formatted error message + + Returns the string "Classname: error message occured in function()" + @return error message + */ + virtual string message() + { + return ("CapiWrongState: "+errormsg+" occured in "+function_name); + } +}; + +/** @brief Capi Abstraction Layer exception class thrown if an error is indicated by Capi + + This exception serves as a way to communicate errors indicated by CAPI to the application. These + are mostly errors indicated from CAPI_PUT_MESSAGE or *_CONF messages. + + It includes also the error code given by CAPI in the info attribute. + + @author Gernot Hillier +*/ +class CapiMsgError : public CapiError +{ + public: + /** @brief Constructor. Create an object, print error message and abort if severity FATAL was chosen. + + @param info error code given by CAPI + @param errormsg informal message describing the error + @param function_name name of the function which throws this exception + */ + CapiMsgError(unsigned info, string errormsg ,string function_name): + CapiError(errormsg,function_name),info(info) + {} + + /** @brief Return nice formatted error message + + Returns the string "Classname: error message occured in function()" + @return error message + */ + virtual string message() + { + stringstream m; + m << "CapiMsgError: " << errormsg << " (error code 0x" << hex << info << ") occured in " << function_name; + return (m.str()); + } + + protected: + unsigned info; ///< error code given by CAPI +}; + +/** @brief Capi Abstraction Layer exception class thrown if an error was caused by the application. + + This ecxeption should be raised by methods in the CAPI abstraction layer when an error was detected + that was clearly caused by the application (e.g. giving an invalid file to send, ...). + + @author Gernot Hillier +*/ +class CapiExternalError : public CapiError +{ + public: + /** @brief Constructor. Create an object, print error message and abort if severity FATAL was chosen. + + @param errormsg informal message describing the error + @param function_name name of the function which throws this exception + */ + CapiExternalError(string errormsg,string function_name): + CapiError("CapiExternalError: "+errormsg,function_name) + {} + + /** @brief Return nice formatted error message + + Returns the string "Classname: error message occured in function()" + @return error message + */ + virtual string message() + { + return ("CapiExternalError: "+errormsg+" occured in "+function_name); + } +}; + +/** @brief Overloaded operator for output of error classes +*/ +inline ostream& operator<<(ostream &s, CapiError &e) +{ + s << e.message(); + return s; +} + +#endif + +/* History + +$Log: capiexception.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.9 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.8 2002/12/13 09:57:10 ghillie +- error message formatting done by exception classes now + +Revision 1.7 2002/12/11 13:05:34 ghillie +- minor comment improvements + +Revision 1.6 2002/12/09 15:39:01 ghillie +- removed severity WARNING +- exception class doesn't print error message any more + +Revision 1.5 2002/11/29 10:24:09 ghillie +- updated comments, use doxygen format now +- changed some parameter names in constructor of CapiMsgError + +Revision 1.4 2002/11/27 16:00:02 ghillie +updated comments for doxygen + +Revision 1.3 2002/11/19 15:57:18 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.2 2002/11/18 14:24:09 ghillie +- moved global severity_t to CapiError::severity_t +- added throw() declarations + +Revision 1.1 2002/11/17 14:42:22 ghillie +initial checkin + +*/ diff --git a/src/backend/connection.cpp b/src/backend/connection.cpp new file mode 100644 index 0000000..6da5c27 --- /dev/null +++ b/src/backend/connection.cpp @@ -0,0 +1,1204 @@ +/* @file connection.cpp + @brief Contains Connection - Encapsulates a CAPI connection with all its states and methods. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include "capi.h" +#include "callinterface.h" +#include "connection.h" + +#define conf_send_buffers 4 + +// TODO NCPI handling fr Fax +// TODO Bconfiguration fr Fax berprfen + +using namespace std; + +Connection::Connection (_cmsg& message, Capi* capi_in): + call_if(NULL),capi(capi_in),plci_state(P2),ncci_state(N0), buffer_start(0), buffers_used(0), + file_for_reception(NULL), file_to_send(NULL), received_dtmf(""), keepPhysicalConnection(false), + disconnect_cause(0),debug(capi->debug), debug_level(capi->debug_level), error(capi->error), + our_call(false), disconnect_cause_b3(0) +{ + pthread_mutex_init(&send_mutex, NULL); + pthread_mutex_init(&receive_mutex, NULL); + + plci=CONNECT_IND_PLCI(&message); // Physical Link Connection Identifier + call_from = getNumber(CONNECT_IND_CALLINGPARTYNUMBER(&message),true); + call_to = getNumber(CONNECT_IND_CALLEDPARTYNUMBER(&message),false); + if (debug_level >= 1) { + debug << prefix() << "Connection object created for incoming call PLCI " << plci << endl; + debug << prefix() << "from " << call_from << " to " << call_to << " CIP 0x" << hex << CONNECT_IND_CIPVALUE(&message) << endl; + } + switch (CONNECT_IND_CIPVALUE(&message)) { + case 1: + case 4: + case 16: + service=VOICE; + break; + case 17: + service=FAXG3; + break; + default: + service=OTHER; + break; + } + connect_ind_msg_nr=message.Messagenumber; // this is needed as connect_resp is given later +} + +Connection::Connection (Capi* capi, _cdword controller, string call_from_in, bool clir, string call_to_in, service_t service, string faxStationID, string faxHeadline) throw (CapiExternalError, CapiMsgError) + :call_if(NULL),capi(capi),plci_state(P01),ncci_state(N0),plci(0),service(service), buffer_start(0), buffers_used(0), + file_for_reception(NULL), file_to_send(NULL), call_from(call_from_in), call_to(call_to_in), connect_ind_msg_nr(0), + disconnect_cause(0), debug(capi->debug), debug_level(capi->debug_level), error(capi->error), keepPhysicalConnection(false), + our_call(true), disconnect_cause_b3(0) +{ + pthread_mutex_init(&send_mutex, NULL); + pthread_mutex_init(&receive_mutex, NULL); + + if (debug_level >= 1) { + debug << prefix() << "Connection object created for outgoing call from " << call_from << " to " << call_to + << " service " << dec << service << endl; + } + if (debug_level >= 2) { + debug << prefix() << "using faxStationID " << faxStationID << " faxHeadline " << faxHeadline << " CLIR " << clir << endl; + } + _cstruct B1config=NULL, B2config=NULL, B3config=NULL, calledPartyNumber=NULL, callingPartyNumber=NULL; + _cword B1proto,B2proto,B3proto; + + try { + _cword CIPvalue; + switch (service) { + case VOICE: + CIPvalue=16; + break; + case FAXG3: + CIPvalue=17; + break; + default: + throw CapiExternalError("unsupported service given","Connection::Connection()"); + break; + } + + buildBconfiguration(service, faxStationID, faxHeadline, B1proto, B2proto, B3proto, B1config, B2config, B3config); + + if (!call_to.size()) + throw CapiExternalError("calledPartyNumber is required","Connection::Connection()"); + + calledPartyNumber=new unsigned char [1+1+call_to.size()]; //struct length, number type/number plan, number + calledPartyNumber[0]=1+call_to.size(); // length + calledPartyNumber[1]=0x80; // as suggested by CAPI spec (unknown number type, unknown number plan, see ETS 300 102-1) + for (unsigned j=0;jconnect_req(this,controller,CIPvalue, calledPartyNumber, callingPartyNumber,B1proto,B2proto,B3proto,B1config, B2config, B3config); + } catch (...) { + if (B1config) + delete[] B1config; + if (B2config) + delete[] B2config; + if (B3config) + delete[] B3config; + if (calledPartyNumber) + delete[] calledPartyNumber; + if (callingPartyNumber) + delete[] callingPartyNumber; + throw; + } + if (B1config) + delete[] B1config; + if (B2config) + delete[] B2config; + if (B3config) + delete[] B3config; + if (calledPartyNumber) + delete[] calledPartyNumber; + if (callingPartyNumber) + delete[] callingPartyNumber; +} + +Connection::~Connection() +{ + stop_file_transmission(); + stop_file_reception(); + + if (getState()!=DOWN) { + error << prefix() << "WARNING: please disconnect yourself before deleting connection object!!" << endl; + disconnectCall(PHYSICAL_ONLY); + while (getState()!=DOWN) + ; + } + plci_state=P0; + + pthread_mutex_lock(&send_mutex); // assure the lock is free before destroying it + pthread_mutex_unlock(&send_mutex); + pthread_mutex_destroy(&send_mutex); + + pthread_mutex_lock(&receive_mutex); // assure the lock is free before destroying it + pthread_mutex_unlock(&receive_mutex); + pthread_mutex_destroy(&receive_mutex); + + if (debug_level >= 1) { + debug << prefix() << "Connection object deleted" << endl; + } +} + +void +Connection::registerCallInterface(CallInterface *call_if_in) +{ + call_if=call_if_in; +} + +void +Connection::changeProtocol(service_t desired_service, string faxStationID, string faxHeadline) throw (CapiMsgError, CapiExternalError, CapiWrongState) +{ + if (debug_level >= 2) { + debug << prefix() << "Protocol change to " << desired_service << " requested" << endl; + } + + if (ncci_state!=N0 || plci_state!=PACT) + throw CapiWrongState("wrong state for changeProtocol","Connection::changeProtocol()"); + + if (desired_service!=service) { + _cstruct B1config=NULL, B2config=NULL, B3config=NULL; + _cword B1proto,B2proto,B3proto; + + try { + buildBconfiguration(desired_service, faxStationID, faxHeadline, B1proto, B2proto, B3proto, B1config, B2config, B3config); + + capi->select_b_protocol_req(plci,B1proto,B2proto,B3proto,B1config, B2config, B3config); + } catch (...) { + if (B1config) + delete[] B1config; + if (B2config) + delete[] B2config; + if (B3config) + delete[] B3config; + throw; + } + if (B1config) + delete[] B1config; + if (B2config) + delete[] B2config; + if (B3config) + delete[] B3config; + + service=desired_service; + } +} + +void +Connection::connectWaiting(service_t desired_service, string faxStationID, string faxHeadline) throw (CapiWrongState, CapiExternalError, CapiMsgError) +{ + if (debug_level >= 1) { + debug << prefix() << "accepting with service " << desired_service << endl; + } + if (debug_level >= 2) { + debug << prefix() << "using faxStationID " << faxStationID << " faxHeadline " << faxHeadline << endl; + } + if (plci_state!=P2) + throw CapiWrongState("wrong state for connectWaiting","Connection::connectWaiting()"); + + if (our_call) + throw (CapiError("can't accept an outgoing call","Connection::connectWaiting()")); + + _cstruct B1config=NULL, B2config=NULL, B3config=NULL; + _cword B1proto,B2proto,B3proto; + + try { + buildBconfiguration(desired_service, faxStationID, faxHeadline, B1proto, B2proto, B3proto, B1config, B2config, B3config); + + plci_state=P4; + capi->connect_resp(connect_ind_msg_nr,plci,0,B1proto,B2proto,B3proto,B1config, B2config, B3config); + } catch (...) { + if (B1config) + delete[] B1config; + if (B2config) + delete[] B2config; + if (B3config) + delete[] B3config; + throw; + } + if (B1config) + delete[] B1config; + if (B2config) + delete[] B2config; + if (B3config) + delete[] B3config; + service=desired_service; +} + +void +Connection::rejectWaiting(_cword reject) throw (CapiWrongState, CapiMsgError, CapiExternalError) +{ + if (debug_level >= 1) { + debug << prefix() << "rejecting with cause " << reject << endl; + } + if (plci_state!=P2) + throw CapiWrongState("wrong state for reject","Connection::reject()"); + if (our_call) + throw (CapiError("can't accept an outgoing call","Connection::connectWaiting()")); + if (!reject) + throw CapiExternalError("reject cause must not be zero","Connection::reject()"); + + plci_state=P5; + capi->connect_resp(connect_ind_msg_nr,plci,reject,0,0,0,NULL,NULL,NULL); // can throw CapiMsgError. Propagate +} + +void +Connection::acceptWaiting() throw (CapiMsgError, CapiWrongState) +{ + if (plci_state!=P2) + throw CapiWrongState("wrong state for acceptWaiting","Connection::acceptWaiting()"); + capi->alert_req(plci); +} + +string +Connection::getCalledPartyNumber() +{ + return call_to; +} + +string +Connection::getCallingPartyNumber() +{ + return call_from; +} + +string +Connection::prefix() +{ + stringstream s; + time_t t=time(NULL); + char* ct=ctime(&t); + ct[24]='\0'; + s << ct << " Connection " << hex << this << ": "; + return (s.str()); +} + +void +Connection::debugMessage(string message, unsigned short level) +{ + if (debug_level >= level) + debug << prefix() << message << endl; +} + +void +Connection::errorMessage(string message) +{ + error << prefix() << message << endl; +} + +Connection::service_t +Connection::getService() +{ + return service; +} + +Connection::connection_state_t +Connection::getState() +{ + if (plci_state==PACT && ncci_state==NACT) + return UP; + else if (plci_state==P2 && ncci_state==N0) + return WAITING; + else if (plci_state==P0 && ncci_state==N0) + return DOWN; + else + return OTHER_STATE; +} + +_cword +Connection::getCause() +{ + return disconnect_cause; +} + +_cword +Connection::getCauseB3() +{ + return disconnect_cause_b3; +} + +void +Connection::connect_active_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (plci_state!=P4 && plci_state!=P1) { + throw CapiWrongState("CONNECT_ACTIVE_IND received in wrong state","Connection::connect_active_ind()"); + } else { + try { + capi->connect_active_resp(message.Messagenumber,plci); + } + catch (CapiMsgError e) { + error << prefix() << "WARNING: error detected when trying to send connect_active_resp. Message was:" << e << endl; + } + + if (plci_state==P1) { // this is an outgoing call, so we have to initiate B3 connection + ncci_state=N01; + try { + capi->connect_b3_req(plci); + } + catch (CapiMsgError) { + plci_state=PACT; + ncci_state=N0; + throw; // this is critical, so propagate + } + } + plci_state=PACT; + } +} + +void +Connection::connect_b3_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (ncci_state!=N0) { + throw CapiWrongState("CONNECT_B3_IND received in wrong state","Connection::connect_b3_ind()"); + } else { + ncci=CONNECT_B3_IND_NCCI(&message); + + // 0 = we'll accept any call, NULL=no NCPI necessary + // this can throw CapiMsgError. Propagate. + ncci_state=N2; + capi->connect_b3_resp(message.Messagenumber,ncci,0,NULL); + } +} + +void +Connection::connect_b3_active_ind(_cmsg& message) throw (CapiWrongState, CapiExternalError) +{ + if (ncci_state!=N2) { + throw CapiWrongState("CONNECT_B3_ACTIVE_IND received in wrong state","Connection::connect_active_b3_ind()"); + } else { + if (ncci!=CONNECT_B3_IND_NCCI(&message)) + throw CapiError("CONNECT_B3_ACTIVE_IND received with wrong NCCI","Connection::connect_active_b3_ind()"); + try { + capi->connect_b3_active_resp(message.Messagenumber,ncci); + } + catch (CapiMsgError e) { + error << prefix() << "WARNING: Error deteced when sending connect_b3_active_resp. Message was: " << e << endl; + } + ncci_state=NACT; + if (call_if) + call_if->callConnected(); + else + throw CapiExternalError("no call control interface registered!","Connection::connect_b3_active_ind()"); + } +} + +void +Connection::disconnect_b3_ind(_cmsg& message) throw (CapiWrongState) +{ + if (ncci_state!=NACT && ncci_state!=N1 && ncci_state!=N2 && ncci_state!=N3 && ncci_state!=N4) { + throw CapiWrongState("DISCONNECT_B3_IND received in wrong state","Connection::disconnect_b3_ind()"); + } else { + if (ncci!=DISCONNECT_B3_IND_NCCI(&message)) + throw CapiError("DISCONNECT_B3_IND received with wrong NCCI","Connection::disconnect_b3_ind()"); + + disconnect_cause_b3=DISCONNECT_B3_IND_REASON_B3(&message); + + pthread_mutex_lock(&send_mutex); + buffers_used=0; // we'll get no DATA_B3_CONF's after DISCONNECT_B3_IND, see Capi 2.0 spec, 5.18, note for DATA_B3_CONF + pthread_mutex_unlock(&send_mutex); + + stop_file_transmission(); + stop_file_reception(); + + bool our_disconnect_req= (ncci_state==N4) ? true : false; + + ncci_state=N5; + + if (call_if) + call_if->callDisconnectedLogical(); + + try { + ncci_state=N0; + capi->disconnect_b3_resp(message.Messagenumber,ncci); + } + catch (CapiMsgError e) { + error << prefix() << "WARNING: Can't send disconnect_b3_resp. Message was: " << e << endl; + } + + if (our_disconnect_req && !keepPhysicalConnection) { // this means *we* initiated disconnect of logical connection with DISCONNECT_B3_REQ before + try { + plci_state=P5; + capi->disconnect_req(plci); // so we'll continue with the disconnect of physical connection + } + catch (CapiMsgError e) { + // in this application this is fatal. Panic please. + throw CapiError("Can't disconnect. Please file a bug report. Error message: "+e.message(),"Connection::disconnect_b3_ind()"); + } + } else + keepPhysicalConnection=false; + } +} + +void +Connection::disconnect_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (ncci_state!=N0 || (plci_state!=P1 && plci_state!=P2 && plci_state!=P3 && plci_state!=P4 && plci_state!=P5 && plci_state!=PACT)) { + throw CapiWrongState("DISCONNECT_IND received in wrong state","Connection::disconnect_ind()"); + } else { + if (plci!=DISCONNECT_IND_PLCI(&message)) + throw CapiError("DISCONNECT_IND received with wrong PLCI","Connection::disconnect_ind()"); + + disconnect_cause=DISCONNECT_IND_REASON(&message); + + plci_state=P0; + capi->disconnect_resp(message.Messagenumber,plci); + capi->unregisterConnection(plci); + + if (call_if) + call_if->callDisconnectedPhysical(); + } +} + +void +Connection::data_b3_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (ncci_state!=NACT && ncci_state!=N4) + throw CapiWrongState("DATA_B3_IND received in wrong state","Connection::data_b3_ind()"); + + if (ncci!=CONNECT_B3_IND_NCCI(&message)) + throw CapiError("DATA_B3_IND received with wrong NCCI","Connection::data_b3_ind()"); + + pthread_mutex_lock(&receive_mutex); + if (file_for_reception) { + for (int i=0;idataIn(DATA_B3_IND_DATA(&message),DATA_B3_IND_DATALENGTH(&message)); + + capi->data_b3_resp(message.Messagenumber,ncci,DATA_B3_IND_DATAHANDLE(&message)); +} + +void +Connection::facility_ind_DTMF(_cmsg &message) throw (CapiWrongState) +{ + if (plci_state!=PACT) + throw CapiWrongState("FACILITY_IND received in wrong state","Connection::facility_ind_DTMF()"); + + if (plci!=(FACILITY_IND_PLCI(&message) & 0xFFFF) ) // this *should* be PLCI, but who knows - so mask NCCI part if it's there... + throw CapiError("FACILITY_IND received with wrong PLCI","Connection::facility_ind_DTMF()"); + + try { + capi->facility_resp(message.Messagenumber,plci,1); + } + catch (CapiMsgError e) { + error << prefix() << "WARNING: Can't send facility_resp. Message was: " << e << endl; + } + + _cstruct facilityIndParam=FACILITY_IND_FACILITYINDICATIONPARAMETER(&message); + received_dtmf.append(reinterpret_cast(facilityIndParam+1),static_cast(facilityIndParam[0])); //string, length + if (debug_level >= 2) { + debug << prefix() << "received DTMF buffer " << received_dtmf << endl; + } + + if (call_if) + call_if->gotDTMF(); +} + +void +Connection::connect_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (plci_state!=P01) + throw CapiWrongState("CONNECT_CONF received in wrong state","Connection::connect_conf()"); + + if (CONNECT_CONF_INFO(&message)) + throw CapiMsgError(CONNECT_CONF_INFO(&message),"CONNECT_CONF received with Error (Info)","Connection::connect_conf()"); + // TODO: do we have to delete Connection here if Info!=0 or is a DISCONNECT_IND initiated then (think not ...) + + plci=CONNECT_CONF_PLCI(&message); + if (debug_level >= 2) { + debug << prefix() << "got PLCI " << plci << endl; + } + + plci_state=P1; +} + +void +Connection::connect_b3_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (ncci_state!=N01) + throw CapiWrongState("CONNECT_B3_CONF received in wrong state","Connection::connect_b3_conf()"); + + if (CONNECT_B3_CONF_INFO(&message)) { + ncci_state=N0; + throw CapiMsgError(CONNECT_B3_CONF_INFO(&message),"CONNECT_B3_CONF received with Error (Info)","Connection::connect_b3_conf()"); + } + + ncci=CONNECT_B3_CONF_NCCI(&message); + + ncci_state=N2; +} + +void +Connection::select_b_protocol_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (plci_state!=PACT || ncci_state!=N0) + throw CapiWrongState("SELECT_B_PROTOCOL_CONF received in wrong state","Connection::select_b_protocol_conf()"); + + if (plci!=SELECT_B_PROTOCOL_CONF_PLCI(&message)) + throw CapiError("SELECT_B_PROTOCOL_CONF received with wrong PLCI","Connection::select_b_protocol_conf()"); + + if (SELECT_B_PROTOCOL_CONF_INFO(&message)) + throw CapiMsgError(SELECT_B_PROTOCOL_CONF_INFO(&message),"SELECT_B_PROTOCOL_CONF received with Error (Info)","Connection::select_b_protocol_conf()"); + + if (our_call) { + try { + ncci_state=N01; + capi->connect_b3_req(plci); + } + catch (CapiMsgError) { + ncci_state=N0; + throw; // this is critical, so propagate + } + } +} + +void +Connection::alert_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (plci_state!=P2 && plci_state!=P5) + throw CapiWrongState("ALERT_CONF received in wrong state","Connection::alert_conf()"); + + if (plci!=ALERT_CONF_PLCI(&message)) + throw CapiError("ALERT_CONF received with wrong PLCI","Connection::alert_conf()"); + + if (ALERT_CONF_INFO(&message) && ALERT_CONF_INFO(&message)!=0x0003) // 0x0003 = another application sent ALERT_REQ earlier -> no problem for us + throw CapiMsgError(ALERT_CONF_INFO(&message),"ALERT_CONF received with Error (Info)","Connection::alert_conf()"); +} + +void +Connection::data_b3_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError, CapiExternalError) +{ + if (ncci_state!=NACT) + throw CapiWrongState("DATA_B3_CONF received in wrong state","Connection::data_b3_conf()"); + + if (ncci!=DATA_B3_CONF_NCCI(&message)) + throw CapiError("DATA_B3_CONF received with wrong NCCI","Connection::data_b3_conf()"); + + if (DATA_B3_CONF_INFO(&message)) + throw CapiMsgError(DATA_B3_CONF_INFO(&message),"DATA_B3_CONF received with Error (Info)","Connection::data_b3_conf()"); + + if ( (!buffers_used) || (DATA_B3_CONF_DATAHANDLE(&message)!=buffer_start) ) + throw CapiError("DATA_B3_CONF received with invalid data handle","Connection::data_b3_conf()"); + + pthread_mutex_lock(&send_mutex); + // free one buffer + buffers_used--; + buffer_start=(buffer_start+1)%7; + + try { + while (file_to_send && (buffers_used < conf_send_buffers) ) + send_block(); + } + catch (...) { + pthread_mutex_unlock(&send_mutex); + throw; + } + pthread_mutex_unlock(&send_mutex); +} + +void +Connection::facility_conf_DTMF(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (plci_state!=PACT) + throw CapiWrongState("FACILITY_CONF for DTMF received in wrong state","Connection::facility_conf_DTMF()"); + + if (plci!=(FACILITY_CONF_PLCI(&message) & 0xFFFF)) // this *should* be the PLCI but to be sure we mask out NCCI part + throw CapiError("FACILITY_CONF received with wrong PLCI","Connection::facility_conf_DTMF()"); + + if (FACILITY_CONF_INFO(&message)) + throw CapiMsgError(FACILITY_CONF_INFO(&message),"FACILITY_CONF received with Error (Info)","Connection::facility_conf_DTMF()"); + + _cstruct facilityConfParameter=FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&message); + if ((facilityConfParameter[0]==2) && facilityConfParameter[1]) + throw CapiMsgError(FACILITY_CONF_INFO(&message),"FACILITY_CONF received with DTMF Error (DTMF information)","Connection::facility_conf_DTMF()"); +} + +void +Connection::disconnect_b3_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (ncci_state!=N4) + throw CapiWrongState("DISCONNECT_B3_CONF received in wrong state","Connection::disconnect_b3_conf()"); + + if (ncci!=DISCONNECT_B3_CONF_NCCI(&message)) + throw CapiError("DISCONNECT_B3_CONF received with wrong NCCI","Connection::disconnect_b3_conf()"); + + if (DISCONNECT_B3_CONF_INFO(&message)) + throw CapiMsgError(DISCONNECT_B3_CONF_INFO(&message),"DISCONNECT_B3_CONF received with Error (Info)","Connection::disconnect_b3_conf()"); +} + +void +Connection::disconnect_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError) +{ + if (plci_state!=P5) + throw CapiWrongState("DISCONNECT_CONF received in wrong state","Connection::disconnect_conf()"); + + if (plci!=DISCONNECT_CONF_PLCI(&message)) + throw CapiError("DISCONNECT_CONF received with wrong PLCI","Connection::disconnect_conf()"); + + if (DISCONNECT_CONF_INFO(&message)) + throw CapiMsgError(DISCONNECT_CONF_INFO(&message),"DISCONNECT_CONF received with Error (Info)","Connection::disconnect_conf()"); +} + +void +Connection::disconnectCall(disconnect_mode_t disconnect_mode) throw (CapiMsgError) +{ + if (debug_level >= 1) { + debug << prefix() << "disconnect initiated" << endl; + } + if ((ncci_state==N1 || ncci_state==N2 || ncci_state==N3 || ncci_state==NACT) && (disconnect_mode==ALL || disconnect_mode==LOGICAL_ONLY) ) { // logical connection up + ncci_state=N4; + capi->disconnect_b3_req(ncci); // can throw CapiMsgError. Fatal here. Propagate + if (disconnect_mode==LOGICAL_ONLY) + keepPhysicalConnection=true; + } else if ((plci_state==PACT || plci_state==P1 || plci_state==P2 || plci_state==P3 || plci_state==P4) && (disconnect_mode==ALL || disconnect_mode==PHYSICAL_ONLY) ) { // physical connection up + plci_state=P5; + capi->disconnect_req(plci); // can throw CapiMsgError. Fatal here. Propagate + } + // otherwise do nothing +} + +void +Connection::send_block() throw (CapiWrongState, CapiExternalError, CapiMsgError) +{ + if (ncci_state!=NACT) + throw CapiWrongState("unable to send file because connection is not established","Connection::send_block()"); + + if (!file_to_send) + throw CapiError("unable to play file because no input file is open","Connection::send_block()"); + + if (buffers_used>=7) + throw CapiError("unable to send file snippet because buffers are full","Connection::send_block()"); + + bool file_completed=false; + + unsigned short buff_num=(buffer_start+buffers_used)%7; // buffer to store the next item + + int i=0; + while (i<2048 && !file_completed) { + if (!file_to_send->get(send_buffer[buff_num][i])) + file_completed=true; + else + i++; + } + + try { + if (i>0) { + capi->data_b3_req(ncci,send_buffer[buff_num],i,buff_num,0); // can throw CapiMsgError. Propagate. + buffers_used++; + } + } + catch (CapiMsgError e) { + error << prefix() << "WARNING: Can't send data_b3_req. Message was: " << e << endl; + if (file_completed) { + file_to_send->close(); + delete file_to_send; + file_to_send=NULL; + } + } + + if (file_completed) { + file_to_send->close(); + delete file_to_send; + file_to_send=NULL; + if (call_if) + call_if->transmissionComplete(); + else + throw CapiExternalError("no call control interface registered!","Connection::send_block()"); + } +} + +void +Connection::start_file_transmission(string filename) throw (CapiWrongState, CapiExternalError, CapiMsgError) +{ + if (debug_level >= 2) { + debug << prefix() << "start_file_transmission " << filename << endl; + } + if (ncci_state!=NACT) + throw CapiWrongState("unable to send file because connection is not established","Connection::start_file_transmission()"); + + if (file_to_send) + throw CapiExternalError("unable to send file because transmission is already in progress","Connection::start_file_transmission()"); + + file_to_send=new ifstream(filename.c_str()); + + if (! (*file_to_send)) { // we can't open the file + delete file_to_send; + file_to_send=NULL; + throw CapiExternalError("unable to open file to send ("+filename+")","Connection::start_file_transmission()"); + } else { + pthread_mutex_lock(&send_mutex); + try { + while (file_to_send && buffers_used= 2) { + debug << prefix() << "stop_file_transmission initiated" << endl; + } + pthread_mutex_lock(&send_mutex); + if (file_to_send) { + file_to_send->close(); + delete file_to_send; + file_to_send=NULL; + } + pthread_mutex_unlock(&send_mutex); + + timespec delay_time; + delay_time.tv_sec=0; delay_time.tv_nsec=100000000; // 100 msec + while (buffers_used) // wait until all packages are transmitted + nanosleep(&delay_time,NULL); + if (debug_level >= 2) { + debug << prefix() << "stop_file_transmission finished" << endl; + } +} + +void +Connection::start_file_reception(string filename) throw (CapiWrongState, CapiExternalError) +{ + if (debug_level >= 2) { + debug << prefix() << "start_file_reception " << filename << endl; + } + if (ncci_state!=NACT) + throw CapiWrongState("unable to receive file because connection is not established","Connection::start_file_reception()"); + + if (file_for_reception) + throw CapiExternalError("file reception is already active","Connection::start_file_reception()"); + + file_for_reception=new ofstream(filename.c_str()); + if (! (*file_for_reception)) { // we can't open the file + delete file_for_reception; + file_for_reception=NULL; + throw CapiExternalError("unable to open file for reception ("+filename+")","Connection::start_file_reception()"); + } +} + +void +Connection::stop_file_reception() +{ + pthread_mutex_lock(&receive_mutex); + + if (file_for_reception) { + file_for_reception->close(); + delete file_for_reception; + file_for_reception=NULL; + } + + pthread_mutex_unlock(&receive_mutex); + if (debug_level >= 2) { + debug << prefix() << "stop_file_reception finished" << endl; + } +} + +void +Connection::enableDTMF() throw (CapiWrongState, CapiMsgError) +{ + if (plci_state!=PACT) + throw CapiWrongState("unable to enable DTMF because connection is not established","Connection::enableDTMF()"); + + _cstruct facilityRequestParameter=new unsigned char[1+2+2+2+1+3]; + int i=0; + facilityRequestParameter[i++]=2+2+2+1+3; // total length + facilityRequestParameter[i++]=1; facilityRequestParameter[i++]=0; // start DTMF listen + facilityRequestParameter[i++]=40; facilityRequestParameter[i++]=0; // default value for tone-duration + facilityRequestParameter[i++]=40; facilityRequestParameter[i++]=0; // default value for gap-duration + facilityRequestParameter[i++]=0; // we don't want to send DTMF now (=empty struct) + facilityRequestParameter[i++]=2; // now let's start substruct DTMF Characteristics (length) + facilityRequestParameter[i++]=0; facilityRequestParameter[i++]=0; // default value for DTMF Selectivity + + try { + capi->facility_req(plci,1,facilityRequestParameter); + } + catch (CapiMsgError) { + delete[] facilityRequestParameter; + throw; + } + delete[] facilityRequestParameter; +} + +void +Connection::disableDTMF() throw (CapiWrongState, CapiMsgError) +{ + if (plci_state!=PACT) + throw CapiWrongState("unable to disable DTMF because connection is not established","Connection::disableDTMF()"); + + _cstruct facilityRequestParameter=new unsigned char[1+2+2+2+1+1]; + int i=0; + facilityRequestParameter[i++]=2+2+2+1+1; // total length + facilityRequestParameter[i++]=2; facilityRequestParameter[i++]=0; // stop DTMF listen + facilityRequestParameter[i++]=40; facilityRequestParameter[i++]=0; // default value for tone-duration + facilityRequestParameter[i++]=40; facilityRequestParameter[i++]=0; // default value for gap-duration + facilityRequestParameter[i++]=0; // we don't want to send DTMF now (=empty struct) + facilityRequestParameter[i++]=0; // no DTMF Characteristics + + try { + capi->facility_req(plci,1,facilityRequestParameter); + } + catch (CapiMsgError) { + delete[] facilityRequestParameter; + throw; + } + delete[] facilityRequestParameter; +} + +string +Connection::getDTMF() +{ + return received_dtmf; +} + +void +Connection::clearDTMF() +{ + received_dtmf.clear(); +} + +string +Connection::getNumber(_cstruct capi_input, bool isCallingNr) +{ +// CallingNr: byte 0: length (w/o byte 0), Byte 1+2 see ETS 300 102-1, Chapter 4.5, byte 3-end: number (w/o leading "0" or "00") +// CalledNr: byte 0: length (w/o byte 0), Byte 1 see ETS 300 102-1, Chapter 4, byte 2-end: number w/o leading "0" or "00" + int length=capi_input[0]; + + if (!length) // no info element given + return "??"; + + char *nr=new char[length]; + memcpy (nr,&capi_input[2],length-1); // copy only number + nr[length-1]='\0'; // add \0 + string a(nr); + if (isCallingNr) + a=a.substr(1); + + // if we are looking at a CallingPartyNumber and it is an international number or a national number + // (see ETS 300 102-1, chapter 4.5), we'll add the prefix "0" or "+" + + if (a.empty()) { + a="??"; + } else if (isCallingNr && ((capi_input[1] & 0x70) == 0x20)) { // national number + a='0'+a; + } else if (isCallingNr && ((capi_input[1] & 0x70) == 0x10)) { // international number + a='+'+a; + } + return a; +} + +void +Connection::buildBconfiguration(service_t service, string faxStationID, string faxHeadline, _cword& B1proto, _cword& B2proto, _cword& B3proto, _cstruct& B1config, _cstruct& B2config, _cstruct& B3config) throw (CapiExternalError) +{ + switch (service) { + case VOICE: + B1proto=1; // bit-transparent + B2proto=1; // Transparent + B3proto=0; // Transparent + B1config=NULL; // no configuration for bit-transparent available + B2config=NULL; // no configuration for transparent available + B3config=NULL; // no configuration for transparent available + break; + + case FAXG3: { + B1proto=4; // T.30 modem for Fax G3 + B2proto=4; // T.30 for Fax G3 + B3proto=4; // T.30 for Fax G3 TODO: should be changed to 5 if necessary!! + B1config=NULL; // default configuration (adaptive maximum baud rate, default transmit level) + B2config=NULL; // no configuration available + + if (faxStationID.size()>254) // if the string would be longer the struct must be coded different, but I think a ID > 254 bytes has no sence anyway + faxStationID=faxStationID.substr(0,254); + if (faxHeadline.size()>254) // if the string would be longer the struct must be coded different, but I think a header > 254 bytes has no sence + faxHeadline=faxHeadline.substr(0,254); + _cstruct B3config=new unsigned char [1+2+2+1+faxStationID.size()+1+faxHeadline.size()]; // length + 1 byte for the length itself + int i=0; + B3config[i++]=2+2+1+faxStationID.size()+1+faxHeadline.size(); // length + B3config[i++]=0; B3config[i++]=0; // resolution = standard + B3config[i++]=0; B3config[i++]=0; // format: SFF + B3config[i++]=faxStationID.size(); + for (unsigned j=0;junregisterConnection(plci) in destructor +- connect_conf() sets plci attribute +- connect_b3_conf() sets ncci attribute + +Revision 1.25 2002/12/04 10:43:43 ghillie +- small FIX in getNumber(): added missing parantheses in if condition -> national number & international number work now + +Revision 1.24 2002/12/02 12:31:10 ghillie +- renamed Connection::SPEECH to Connection::VOICE + +Revision 1.23 2002/11/29 10:25:01 ghillie +- updated comments, use doxygen format now + +Revision 1.22 2002/11/27 16:02:54 ghillie +- added missing throw() declaration in changeProtocol() +- added missing state check in acceptWaiting() +- data_b3_ind and disconnect_ind propagate CapiMsgError now +- DTMF handling routines and select_b_protocol_conf test for state of physical connection instead of logical connection now + +Revision 1.21 2002/11/25 11:51:45 ghillie +- removed the unhandy CIP parameters from the interface to the application layer, use service type instead +- rejectWaiting() tests against cause!=0 now +- removed isUp() method + +Revision 1.20 2002/11/22 15:13:44 ghillie +- new attribute keepPhysicalConnection which prevents disconnect_b3_ind() from sending disconnect_req() +- moved the ugly B*configuration, B*protocol settings from some methods to private method buildBconfiguration +- new methods changeProtocol(), select_b_protocol_conf(), clearDTMF() +- disconnect_b3_ind sets ncci_state to N0 before calling the callbacks +- added parameter disconnect_mode to disconnectCall() +- getDTMF() does non-destructive read now + +Revision 1.19 2002/11/21 15:28:12 ghillie +- removed ALERT_REQ sending from constructor - this is now done by the python functions connect_*() +- new method Connection::acceptWaiting() - sends ALERT_REQ for use by the above mentioned python functions +- connectWaiting changes cipValue now + +Revision 1.18 2002/11/20 17:24:58 ghillie +- added check if call_if is set in data_b3_ind before it's called (ouch!) +- changed impossible error to ::FATAL in send_block() + +Revision 1.17 2002/11/19 15:57:18 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.16 2002/11/18 14:24:09 ghillie +- moved global severity_t to CapiError::severity_t +- added throw() declarations + +Revision 1.15 2002/11/18 12:23:17 ghillie +- fix: set buffers_used to 0 in critical section in Connection::disconnect_b3_ind() +- disconnectCall() doesn't throw exception any more (does nothing if we have wrong state), + so we can call it w/o knowledge if connection is still up + +Revision 1.14 2002/11/17 14:40:47 ghillie +- improved exception throwing, different exception kinds are used now +- added isUp() + +Revision 1.13 2002/11/15 15:25:53 ghillie +added ALERT_REQ so we don't loose a call when we wait before connection establishment + +Revision 1.12 2002/11/15 13:49:10 ghillie +fix: callmodule wasn't aborted when call was only connected/disconnected physically + +Revision 1.11 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.10 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.9 2002/11/12 15:51:12 ghillie +minor fixes (avoid deadlock, don't wait for DATA_B3_CONF after DISCONNECT_B3_IND) in file_transmission code +added dataIn handler +minor fixes (and reformatting) in getNumber() + +Revision 1.8 2002/11/10 17:05:18 ghillie +changed to support multiple buffers -> deadlock in stop_file_transmission!! + +Revision 1.7 2002/11/08 07:57:06 ghillie +added functions to initiate a call +corrected FACILITY calls to use PLCI instead of NCCI in DTMF processing as told by Mr. Ortmann on comp.dcom.isdn.capi + +Revision 1.6 2002/10/31 15:39:04 ghillie +added missing FACILITY_RESP message (oops...) + +Revision 1.5 2002/10/31 12:40:06 ghillie +added DTMF support +small fixes like making some unnecessary global variables local, removed some unnecessary else cases + +Revision 1.4 2002/10/30 14:29:25 ghillie +added getCIPvalue + +Revision 1.3 2002/10/30 10:47:13 ghillie +added debug output + +Revision 1.2 2002/10/29 14:27:09 ghillie +added stop_file_*, added semaphore calls to guarantee right order of execution (I hope ;-) ) + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.15 2002/10/24 09:55:52 ghillie +many fixes. Works for one call now + +Revision 1.14 2002/10/23 09:43:05 ghillie +small variable name change (stationID->faxStationID) + +Revision 1.13 2002/10/10 12:45:40 gernot +added AudioReceive module, some small details changed + +Revision 1.12 2002/10/09 14:36:22 gernot +added CallModule base class for all call handling modules + +Revision 1.11 2002/10/09 11:18:59 gernot +cosmetic changes (again...) and changed info function of CAPI class + +Revision 1.10 2002/10/05 13:53:00 gernot +changed to use thread class of CommonC++ instead of the threads-package +some cosmetic improvements (indentation...) + +Revision 1.9 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.8 2002/10/04 13:27:15 gernot +some restructuring to get it to a working state ;-) +does not do anything useful yet nor does it even compile... + +Revision 1.7 2002/10/01 09:02:04 gernot +changes for compilation with gcc3.2 + +Revision 1.6 2002/09/22 14:22:53 gernot +some cosmetic comment improvements ;-) + +Revision 1.5 2002/09/19 12:08:19 gernot +added magic CVS strings + +Revision 1.4 2002/09/18 16:59:48 gernot +added version info + +* Sun Sep 15 2002 - gernot@hillier.de +- put under CVS, cvs log follows above + +* Sun May 20 2002 - gernot@hillier.de +- first version + +*/ diff --git a/src/backend/connection.h b/src/backend/connection.h new file mode 100644 index 0000000..89d73ab --- /dev/null +++ b/src/backend/connection.h @@ -0,0 +1,774 @@ +/** @file connection.h + @brief Contains Connection - Encapsulates a CAPI connection with all its states and methods. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CONNECTION_H +#define CONNECTION_H + +#include +#include +#include +#include "capiexception.h" + +class CallInterface; +class Capi; + +using namespace std; + +/** @brief Encapsulates a CAPI connection with all its states and methods. + + This class encapsulates one ISDN connection (physical and logical). It has two groups of methods: + methods which are supposed to be called from the application (declared public) which serve to + access the connection, send data, clear it etc. Also there are many message handlers implemented + (declared as private) which are called by Capi::readMessage() and do the most message handling stuff. + + Only one logical connection per physical connection is supported. + + To communicate with the application by callback functions, the application must implement the CallInterface. + The methods of this class are called if different events occur like call completion or a finished data transfer. + + There are two ways how Connection objects are created: + - automatically for each incoming connection + - manually to initiate a outgoing connection + + @author Gernot Hillier +*/ +class Connection +{ + friend class Capi; + + public: + /** @brief Type for describing the service of incoming and outgoing calls. + + Several similar services are grouped together so the application doesn't have to distinct between speech calls + originating from the analog or digital network, between devices providing a High Layer compatibility and devices + which only provide bearer capabilities and so on. + + For outgoing calls, the most specific value will be used. + + For exact details concerning the different services see the parameter CIP in the CAPI spec. + */ + enum service_t { + VOICE, ///< connections for speech services originated from the analog or digital network (CIP values 1,4,16) + FAXG3, ///< connections for fax Group2/3 services originating from the digital network (as analog networks don't provide service indicator) (CIP value 17) + OTHER ///< all other service types not included in the above values + }; + + /** @brief Constructor. Create an object to initiate an outgoing call + + This constructor is used when the application creates a Connection object manually to initiate an outgoing connection. + + It constructs the necessary information elements (B channel protocol settings, Number elements) and calls Capi::connect_req + to initiate a call. + + @param capi pointer to the Capi Object + @param controller number of the controller which should initiate the call + @param call_from our number + @param clir set to TRUE to hide our number (so that the called party can't see it) + @param call_to the number which we want to call + @param service service to use (currently only VOICE and FAXG3 are supported), see service_t + @param faxStationID fax station ID, only used with service faxG3 + @param faxHeadline fax headline (written on each fax page), only used with service faxG3 + @throw CapiExternalError thrown when parameters are missing or wrong + @throw CapiMsgError thrown by Capi::connect_req, see there + */ + Connection (Capi* capi, _cdword controller, string call_from, bool clir, string call_to, service_t service, string faxStationID, string faxHeadline) throw (CapiExternalError, CapiMsgError); + + /** @brief. Destructor. Deletes the connection object. + + Can block if file transmission is still in progress and/or if connection is not cleared already. + + Please call as soon as you don't need the object any more as this will also free some + CAPI resources associated to the call. + + Please disconnect before deleting a Connection object! The auto-disconnect here is only supported + to prevent fatal errors and won't work very nicely!! + */ + ~Connection(); + + /** @brief Register the instance implementing the CallInterface + + @param call_if - pointer to the instance to use + */ + void registerCallInterface(CallInterface *call_if); + + /** @brief Change the used B protcols (e.g. switch from speech to faxG3) + + You have to disconnect the logical connection before calling this method. So to change from speech to fax do: + - disconnect(LOGICAL_ONLY); + - wait for CallInterface::CallDisconnectedLogical() to be called + - changeProtocol(FAXG3,"stationID","headline") + - wait for CallInterface::callConnected() to be called + + Does nothing if the requested service is already active. Otherwise the necessary information elements are built + and Capi::select_b_protocol_req is called. + + @param desired_service service to switch to + @param faxStationID Only needed when switching to FaxG3. The fax station ID to use. + @param faxHeadline Only needed when switching to FaxG3. The fax headline to use. + @throw CapiExternalError Thrown by Connection::buildBconfiguration. See there. + @throw CapiMsgError Thrown by Connection::select_b_protocol_req. See there. + @throw CapiWrongState Connection is in wrong state. Either it was finished by the partner or you didn't disconnect the logical connection before calling changeProtocol + */ + void changeProtocol (service_t desired_service, string faxStationID, string faxHeadline) throw (CapiMsgError, CapiExternalError, CapiWrongState); + + /** @brief called to start sending of a file + + The transmission will be controlled automatically by Connection - no further user interaction is necessary + if the whole file should be sent. + + The file has to be in the correct format expected by CAPI, i.e. bit-reversed A-Law, 8 khz, mono (".la" for sox) for speech, SFF for faxG3 + + @param filename the name of the file which should be sent + @throw CapiWrongState Thrown if Connection isn't up completely (physical & logical) + @throw CapiExternalError Thrown if file transmission is already in progress or the file couldn't be opened + @throw CapiMsgError Thrown by send_block(). See there. + */ + void start_file_transmission(string filename) throw (CapiWrongState, CapiExternalError, CapiMsgError); + + /** @brief called to stop sending of the current file, will block until file is really finished + + If you stop the file transmission manually, CallInterface::transferCompleted won't be called. + */ + void stop_file_transmission(); + + /** @brief called to activate receive mode + + This method doen't do anything active, it will only set the receive mode for this connection + If you don't call this, all received data is discarded. If called, the data B3 stream received + is written to this file w/o changes. So it's in the native format given by CAPI (i.e. inserved A-Law + for speech, SFF for FaxG3). + + @param filename name of the file to which to save the incoming data + @throw CapiWrongState Thrown if Connection isn't up completely (physical & logical) + @throw CapiExternalError Thrown if file reception is already in progress or the file couldn't be opened + */ + void start_file_reception(string filename) throw (CapiWrongState, CapiExternalError); + + /** @brief called to stop receive mode + + This closes the reception file and tells us to ignore further incoming B3 data. + */ + void stop_file_reception(); + + /** @brief Tells disconnectCall() method how to disconnect. + */ + enum disconnect_mode_t { + ALL, ///< do the necessary steps to finish the call nicely (first logical, then physical) + PHYSICAL_ONLY, ///< quick'n'dirty disconnect (physical only, logical will auto-disconnect with protocol error) + LOGICAL_ONLY ///< only disconnect logical connection, physical will stay up + }; + + /** @brief Terminate this connection + + According to the current state and the given mode, the needed steps to disconnect will be executed. + + If you don't specify a mode, the connection will be completely cleared (first logical, then physical). + + It doesn't throw any error if you call it for an already cleared connection, so it's safe to call it more + than one time. + + To reject a waiting call you haven't accepted earlier, use rejectWaiting(), not disconnectCall(). + + Attention: Connection objects will not be deleted after the disconnection. You must delete the Connection + object explicitly. The reason for this behavior is to allow you to get connection related information + (like disconnect cause, ...) after the connection has cleared. See also CallInterface::callDisconnectedPhysical(). + + @param disconnect_mode see description of disconnect_mode_t + @throw CapiMsgError Thrown by Capi::disconnect_b3_req() or Capi::disconnect_req(). See there. + */ + void disconnectCall(disconnect_mode_t disconnect_mode=ALL) throw (CapiMsgError); + + /** @brief Accept a waiting incoming call. + + Will update the saved service to the desired_service. Calls Capi::connect_resp(). + + @param desired_service to determine with which service we should connect (e.g. VOICE or FAXG3) + @param faxStationID my fax station ID - only needed in FAXG3 mode + @param faxHeadline my fax headline - only needed in FAXG3 mode + @throw CapiWrongState Thrown if call is not in necessary state (waiting for acception or rejection) + @throw CapiExternalError Thrown by buildBconfiguration. See there. + @throw CapiMsgError Thrown by Capi::connect_resp(). See there. + */ + void connectWaiting(service_t desired_service, string faxStationID="", string faxHeadline="") throw (CapiWrongState, CapiExternalError, CapiMsgError); + + /** @brief Reject a waiting incoming call + + Only use for waiting calls - to disconnect an already connected call use disconnectCall(). + + @param reject reject cause, see CAPI spec ch 5.6 for details + @throw CapiWrongState Thrown if call is not in necessary state (waiting for acception or rejection) + @throw CapiExternalError Thrown if reject cause is not set (cause 0 not allowed). + @throw CapiMsgError Thrown by Capi::connect_resp(). See there. + */ + void rejectWaiting(_cword reject) throw (CapiWrongState, CapiMsgError, CapiExternalError); + + /** @brief disable timeout for a pending call (i.e. send ALERT) + + Normally, a call is indicated from ISDN and timeouts after 4 (or 8) seconds if no answer is received. + If you want to accept the call, not immediately, but some secods later, you must call acceptWaiting(). + This tells ISDN that we will accept it some times later (i.e. send ALERT_REQ) + + @throw CapiWrongState Thrown if call is not in necessary state (waiting for acception or rejection) + @throw CapiMsgError Thrown by Capi::calert_req(). See there. + */ + void acceptWaiting() throw (CapiMsgError, CapiWrongState); + + /** @brief Enable indication for DTMF signals + + Normally you get no indication when the CAPI recognizes a DTMF signal. With enableDTMF() + you enable these indications. DTMF signals will be saved locally and signalled by CallInterface::gotDTMF(). + You can read the saved DTMF signales with getDTMF(). + + @throw CapiWrongState Thrown if Connection isn't up completely (physical & logical) + @throw CapiMsgError Thrown by Capi::facility_req(). See there. + */ + void enableDTMF() throw (CapiWrongState, CapiMsgError); + + /** @brief Disable indication for DTMF signals + + @throw CapiWrongState Thrown if Connection isn't up completely (physical & logical) + @throw CapiMsgError Thrown by Capi::facility_req(). See there. + */ + void disableDTMF() throw (CapiWrongState, CapiMsgError); + + /** @brief read the saved DTMF characters + + DTMF characters will only be saved if you called enableDTMF() before. + + @return string containing received DTMF coded as characters ('0'..'9','A'..'D','*','#','X'=fax CNG,'Y'=fax CED) + */ + string getDTMF(); + + /** @brief Delete the saved DTMF characters + */ + void clearDTMF(); + + /** @brief Return number of the called party (the source of the call) + + @return CalledPartyNumber + */ + string getCalledPartyNumber(); + + /** @brief Return number of calling party (the destination of the call) + + @return callingPartyNumber + */ + string getCallingPartyNumber(); + + /** @brief Return currently used service mode + + @return service as described in Connection::service_t + */ + service_t getService(); + + /** @brief Return disconnection cause given by the CAPI + + 0x33xx=see CAPI spec + 0x34xx=ISDN cause, for coding of xx see ETS 300102-1. + + @return Returns the disconnection cause, 0=no cause available + */ + _cword getCause(); + + /** @brief Return disconnection cause given by the CAPI + + 0x33xx=see CAPI spec + 0x34xx=ISDN cause, for coding of xx see ETS 300102-1. + + @return Returns the disconnection cause of Layer3, 0=no cause available or normal clearing + */ + _cword getCauseB3(); + + + /** @brief Represents the current connection state in an easy way + */ + enum connection_state_t { + DOWN, ///< this means the connection is completely down, i.e. physical and logical down + WAITING, ///< this means an incoming connection is waiting for our decision (accept or reject) + UP, ///< this means the connection is completely up, i.e. physical and logical up + OTHER_STATE ///< connection is in some other state (e.g. physical up, logical down, ...) + }; + + /** @brief Return the connection state + + @return current connection state as specified in Connection::connection_state_t + */ + connection_state_t getState(); + + /** @brief Output error message + + This is intended for external use if some other part of the application wants to make a error-log entry. + For internal use in this class just output to the stream "error". + + @param message the message to print + */ + void errorMessage(string message); + + + /** @brief Output debug message + + This is intended for external use if some other part of the application wants to make a log entry. + For internal use in this class just output to the stream "debug". + + @param message the message to print + @param level the debug level of the given message (see capisuite.conf for explanation) + */ + void debugMessage(string message, unsigned short level); + + protected: + + /** @brief Constructor. Create an object for an incoming call + + This one is used by Capi when an incoming connection triggers the automatic creation of a Connection object, i.e. + when we receive a CONNECT_IND message. + + It only extracts some data (numbers, services, etc.) from the message and saves it in private attributes. + + @param message the received CONNECT_IND message + @param capi pointer to the Capi Object + */ + Connection (_cmsg& message, Capi *capi); + + /********************************************************************************/ + /* methods handling CAPI messages - called by the Capi class */ + /********************************************************************************/ + + /*************************** INDICATIONS ****************************************/ + + /** @brief Called when we get CONNECT_ACTIVE_IND from CAPI + + This method will also send a response to Capi and initiate + a B3 connection if necessary. + + @param message the received CONNECT_ACTIVE_IND message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong plci_state) + @throw CapiMsgError Thrown by Capi::connect_b3_req + */ + void connect_active_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError); + + /** @brief called when we get CONNECT_B3_IND from CAPI + + This method will also send a response to Capi. + + @param message the received CONNECT_B3_IND message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong ncci_state) + @throw CapiMsgError Thrown by Capi::connect_b3_resp() + */ + void connect_b3_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError); + + /** @brief called when we get CONNECT_B3_ACTIVE_IND from CAPI + + This method will also send a response to Capi and call CallInterface::callConnected(). + + @param message the received CONNECT_B3_ACTIVE_IND message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong ncci_state) + @throw CapiExternalError Thrown if no CallInterface is registered + */ + void connect_b3_active_ind(_cmsg& message) throw (CapiWrongState, CapiExternalError); + + /** @brief called when we get DATA_B3_IND from CAPI + + This method will also save the received data, send a response to Capi and call CallInterface::dataIn(). + + @param message the received DATA_B3_IND message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong ncci_state) + @throw CapiMsgError Thrown by Capi::data_b3_resp() + */ + void data_b3_ind(_cmsg& message) throw (CapiWrongState,CapiMsgError); + + /** @brief called when we get FACILITY_IND from CAPI with facility selector saying it's DTMF + + This method will save the received DTMF to received_dtmf, send a response to Capi and call + CallInterface::gotDTMF(). + + @param message the received FACILITY_IND message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong ncci_state) + */ + void facility_ind_DTMF(_cmsg& message) throw (CapiWrongState); + + /** @brief called when we get DISCONNECT_B3_IND from CAPI + + This method will also send a response to Capi and stop_file_transmission and stop_file_reception(). + It will call CallInterface::callDisconnectedLogical() and initiate termination of physical connection. + + @param message the received DISCONNECT_B3_IND message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong ncci_state) + */ + void disconnect_b3_ind(_cmsg& message) throw (CapiWrongState); + + /** @brief called when we get DISCONNECT_IND from CAPI + + This method will also send a response to Capi and call CallInterface::callDisconnectedPhysical(). + + @param message the received DISCONNECT_IND message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong ncci_state or plci_state) + @throw CapiMsgError Thrown by Capi::disconnect_resp() + */ + void disconnect_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError); + + /*************************** CONFIRMATIONS **************************************/ + + /** @brief called when we get CONNECT_CONF from CAPI + + @param message the received CONNECT_CONF message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong plci_state) + @throw CapiMsgError Thrown if the info InfoElement indicates an error + */ + void connect_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError); + + /** @brief called when we get CONNECT_B3_CONF from CAPI + + @param message the received CONNECT_B3_CONF message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong ncci_state) + @throw CapiMsgError Thrown if the info InfoElement indicates an error + */ + void connect_b3_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError); + + /** @brief called when we get SELECT_B_PROTOCOL_CONF from CAPI + + @param message the received SELECT_B_PROTOCOL_CONF message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong plci_state) + @throw CapiMsgError Thrown if the info InfoElement indicates an error + */ + void select_b_protocol_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError); + + /** @brief called when we get ALERT_CONF from CAPI + + @param message the received ALERT_CONF message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong plci_state) + @throw CapiMsgError Thrown if the info InfoElement indicates an error + */ + void alert_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError); + + /** @brief called when we get DATA_B3_CONF from CAPI + + This will trigger new send_block(). + + @param message the received DATA_B3_CONF message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong plci_state) + @throw CapiMsgError Thrown if the info InfoElement indicates an error + @throw CapiExternalError Thrown by Connection::send_block() + */ + void data_b3_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError, CapiExternalError); + + /** @brief called when we get FACILITY_CONF from CAPI with facility selector saying it's DTMF + + @param message the received FACILITY_CONF message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong plci_state) + @throw CapiMsgError Thrown if the info InfoElement indicates an error + */ + void facility_conf_DTMF(_cmsg& message) throw (CapiWrongState, CapiMsgError); + + /** @brief called when we get DISCONNECT_B3_CONF from CAPI + + @param message the received DISCONNECT_B3_CONF message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong plci_state) + @throw CapiMsgError Thrown if the info InfoElement indicates an error + */ + void disconnect_b3_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError); + + /** @brief called when we get DISCONNECT_CONF from CAPI + + @param message the received DISCONNECT_CONF message + @throw CapiWrongState Thrown when the message is received unexpected (i.e. in a wrong plci_state) + @throw CapiMsgError Thrown if the info InfoElement indicates an error + */ + void disconnect_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError); + + /********************************************************************************/ + /* internal methods */ + /********************************************************************************/ + + /** @brief return a prefix containing this pointer and date for log messages + + @return constructed prefix as stringstream + */ + string prefix(); + + /** @brief format the CallingPartyNr or CalledPartyNr to readable string + + @param capi_input the CallingPartyNumber resp. CalledPartyNumber struct + @param isCallingNr true = CallingPartyNr / false = CalledPartyNr + @return the number formatted as string + */ + string getNumber (_cstruct capi_input, bool isCallingNr); + + /** @brief called to send next block (2048 bytes) of file + + The transmission will be controlled automatically by Connection, so you don't + need to call this method directly. send_block() will automatically send as much + packets as the configured window size (conf_send_buffers) permits. + + Will call CallInterface::transmissionComplete() if the file was transferred completely. + + @throw CapiWrongState Thrown when the the connection is not up completely (physical & logical) + @throw CapiExternalError Thrown when no CallInterface is registered + @throw CapiMsgError Thrown by Capi::data_b3_req(). + */ + void send_block() throw (CapiWrongState, CapiExternalError, CapiMsgError); + + /** @brief called to build the B Configuration info elements out of given service + + This is a convenience functin to do the quite annoying enconding stuff for the 6 B configuration parameters. + + @param service value indicating service to be used as described in service_t + @param faxStationID my fax station ID + @param faxHeadline the fax headline + @param B1proto return value: B1protocol value for CAPI, see CAPI spec + @param B2proto return value: B2protocol value for CAPI, see CAPI spec + @param B3proto return value: B3protocol value for CAPI, see CAPI spec + @param B1config return value: B1configuration element for CAPI, see CAPI spec + @param B2config return value: B2configuration element for CAPI, see CAPI spec + @param B3config return value: B3configuration element for CAPI, see CAPI spec + */ + void buildBconfiguration(service_t service, string faxStationID, string faxHeadline, _cword& B1proto, _cword& B2proto, _cword& B3proto, _cstruct& B1config, _cstruct& B2config, _cstruct& B3config) throw (CapiExternalError); + + /********************************************************************************/ + /* attributes */ + /********************************************************************************/ + + /** @brief State for the physical connection as defined in the CAPI spec. + + For complete diagrams and state definition see CAPI 2.0 spec, chapter 7.2 + + */ + enum plci_state_t { + P0, ///< default state. no connection. + P01, ///< CONNECT_REQ sent, waiting for CONNECT_CONF + P1, ///< CONNECT_CONF received, waiting for CONNECT_ACTIVE_IND + P2, ///< CONNECT_IND received, no CONNECT_RESP given yet + P3, ///< only needed for handsets + P4, ///< CONNECT_RESP sent, waiting for CONNECT_ACTIVE_IND + P5, ///< DISCONNECT_REQ sent, waiting for DISCONNECT_IND + P6, ///< DISCONNECT_IND received, DISCONNECT_RESP not sent yet + PACT ///< active physical connection + } plci_state; + + /** @brief State for logical connection as defined in the CAPI spec + + For complete diagrams and state definition see CAPI 2.0 spec, chapter 7.2 + */ + enum ncci_state_t { + N0, ///< default state, no connection. + N01, ///< CONNECT_B3_REQ sent, waiting for CONNECT_B3_CONF + N1, ///< CONNECT_B3_IND received, CONNECT_B3_RESP not sent yet + N2, ///< CONNECT_B3_CONF received / CONNECT_B3_RESP sent, waiting for CONNECT_B3_ACTIVE_IND + N3, ///< RESET_B3_REQ sent, waiting for RESET_B3_IND + N4, ///< DISCONNECT_B3_REQ sent, waiting for DISCONNECT_B3_IND + N5, ///< DISCONNECT_B3_IND received + NACT ///< active logical connection + } ncci_state; + + _cdword plci; ///< CAPI id for call + _cdword ncci; ///< id for logical connection + + service_t service; ///< as described in Connection::service_t, set to the last known service (either got from ISDN or set explicitly) + + _cword connect_ind_msg_nr; ///< this is needed as connect_resp is given in another function as connect_ind + + _cword disconnect_cause, ///< the disconnect cause as given by the CAPI in DISCONNECT_IND + disconnect_cause_b3; ///< the disconnect cause as given by the CAPI in DISCONNECT_B3_IND + + string call_from; ///< CallingPartyNumber, formatted as string with leading '0' or '+' prefix + string call_to; ///< CalledPartyNumber, formatted as string + + string received_dtmf; ///< accumulates the received DTMF data, see readDTMF() + + bool keepPhysicalConnection, ///< set to true to disable auto-physical disconnect after logical disconnect for one time + our_call; ///< set to true if we initiated the call (needed to know as some messages must be sent if we initiated the call) + + CallInterface *call_if; ///< pointer to the object implementing CallInterface + Capi *capi; ///< pointer to the Capi object + + pthread_mutex_t send_mutex, ///< to realize critical sections in transmission code + receive_mutex; ///< to realize critical sections in reception code + + ofstream *file_for_reception; ///< NULL if no file is received, pointer to the file otherwise + ifstream *file_to_send; ///< NULL if no file is sent, pointer to the file otherwise + + ostream &debug, ///< debug stream + &error; ///< stream for error messages + unsigned short debug_level; ///< debug level + + /** @brief ring buffer for sending + + 7 buffers a 2048 byte mark the maximal window size handled by CAPI + + is empty: buffer_used==0 / is full: buffers_used==7 + to forget item: buffers_used--; buffer_start++; + to remember item: send_buffer[ (buffer_start+buffers_used)%8 ]=item; buffers_used++ + */ + char send_buffer[7][2048]; + + unsigned short buffer_start, ///< holds the index for the first buffer currently used + buffers_used; ///< holds the number of currently used buffers +}; + +#endif + +/* History + +$Log: connection.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.30 2003/02/10 14:20:52 ghillie +merged from NATIVE_PTHREADS to HEAD + +Revision 1.29.2.1 2003/02/10 14:07:54 ghillie +- use pthread_mutex_* instead of CommonC++ Semaphore + +Revision 1.29 2003/01/04 16:08:22 ghillie +- log improvements: log_level, timestamp +- added methods debugMessage(), errorMessage(), removed get*Stream() +- added some additional debug output for connection setup / finish + +Revision 1.28 2002/12/16 13:13:47 ghillie +- added getCauseB3 to return B3 cause + +Revision 1.27 2002/12/13 11:46:59 ghillie +- added attribute our_call to differ outgoing and incoming calls + +Revision 1.26 2002/12/11 13:39:05 ghillie +- added support for PHYSICAL_ONLY disconnect in disconnectCall() + +Revision 1.25 2002/12/10 15:06:15 ghillie +- new methods get*Stream() for use in capisuitemodule + +Revision 1.24 2002/12/09 15:42:24 ghillie +- save debug and error stream in own attributes + +Revision 1.23 2002/12/06 15:25:47 ghillie +- new return value for getState(): WAITING + +Revision 1.22 2002/12/06 13:07:36 ghillie +- update docs because application is now responsible to delete + Connection object +- new methods getCause() and getState() + +Revision 1.21 2002/12/05 15:05:12 ghillie +- moved constructor for incoming calls to "private:" + +Revision 1.20 2002/12/02 12:31:36 ghillie +renamed Connection::SPEECH to Connection::VOICE + +Revision 1.19 2002/11/29 10:25:01 ghillie +- updated comments, use doxygen format now + +Revision 1.18 2002/11/27 16:03:20 ghillie +updated comments for doxygen + +Revision 1.17 2002/11/25 11:51:54 ghillie +- removed the unhandy CIP parameters from the interface to the application layer, use service type instead +- rejectWaiting() tests against cause!=0 now +- removed isUp() method + +Revision 1.16 2002/11/22 15:13:44 ghillie +- new attribute keepPhysicalConnection which prevents disconnect_b3_ind() from sending disconnect_req() +- moved the ugly B*configuration, B*protocol settings from some methods to private method buildBconfiguration +- new methods changeProtocol(), select_b_protocol_conf(), clearDTMF() +- disconnect_b3_ind sets ncci_state to N0 before calling the callbacks +- added parameter disconnect_mode to disconnectCall() +- getDTMF() does non-destructive read now + +Revision 1.15 2002/11/21 15:30:28 ghillie +- added new method Connection::acceptWaiting() - sends ALERT_REQ +- updated description of Connection::connectWaiting() + +Revision 1.14 2002/11/19 15:57:19 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.13 2002/11/18 14:24:09 ghillie +- moved global severity_t to CapiError::severity_t +- added throw() declarations + +Revision 1.12 2002/11/18 12:24:33 ghillie +- changed disconnectCall() so that it doesn't throw exceptions any more, + so that we can call it in any state + +Revision 1.11 2002/11/17 14:40:55 ghillie +added isUp() + +Revision 1.10 2002/11/15 15:25:53 ghillie +added ALERT_REQ so we don't loose a call when we wait before connection establishment + +Revision 1.9 2002/11/15 13:49:10 ghillie +fix: callmodule wasn't aborted when call was only connected/disconnected physically + +Revision 1.8 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.7 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.6 2002/11/10 17:05:18 ghillie +changed to support multiple buffers -> deadlock in stop_file_transmission!! + +Revision 1.5 2002/11/08 07:57:07 ghillie +added functions to initiate a call +corrected FACILITY calls to use PLCI instead of NCCI in DTMF processing as told by Mr. Ortmann on comp.dcom.isdn.capi + +Revision 1.4 2002/10/31 12:40:06 ghillie +added DTMF support +small fixes like making some unnecessary global variables local, removed some unnecessary else cases + +Revision 1.3 2002/10/30 14:29:25 ghillie +added getCIPvalue + +Revision 1.2 2002/10/29 14:27:42 ghillie +added stop_file_*, added semaphores + +Revision 1.1 2002/10/25 13:29:38 ghillie +grouped files into subdirectories + +Revision 1.10 2002/10/10 12:45:40 gernot +added AudioReceive module, some small details changed + +Revision 1.9 2002/10/09 11:18:59 gernot +cosmetic changes (again...) and changed info function of CAPI class + +Revision 1.8 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.7 2002/10/04 13:27:15 gernot +some restructuring to get it to a working state ;-) + +does not do anything useful yet nor does it even compile... + +Revision 1.6 2002/10/01 09:02:04 gernot +changes for compilation with gcc3.2 + +Revision 1.5 2002/09/22 14:22:53 gernot +some cosmetic comment improvements ;-) + +Revision 1.4 2002/09/19 12:08:19 gernot +added magic CVS strings + +* Sun Sep 15 2002 - gernot@hillier.de +- put under CVS, cvs changelog follows above + +* Sun May 20 2002 - gernot@hillier.de +- first version + +*/ diff --git a/src/capisuite.conf.in b/src/capisuite.conf.in new file mode 100644 index 0000000..f454d6d --- /dev/null +++ b/src/capisuite.conf.in @@ -0,0 +1,70 @@ +# $Id: capisuite.conf.in,v 1.1 2003/02/19 08:19:52 gernot Exp $ +# +# This is the global configuration file for CapiSuite +# +# Some general options will be set here, which define which scripts +# to use, when to start them and what to log and where. +# +# All other options are defined in the python scripts given here +# or (preferably) are read from them from some other configuration +# files. +# +# As usual, lines starting with # or empty lines will be ignored +# +# The rest must be key value pairs written as key=value. +# +# Additional whitespaces and quotation marks (") surrounding +# the values will be ignored. + +# incoming_script +# +# This python script will be called when an incoming call occurs. +# +# It must define a python function named "callIncoming". +# +incoming_script="@pkglibdir@/incoming.py" + +# idle_script +# +# This python script will be called in regular intervals giving +# you the ability to search for needed actions (like send fax) +# in a queue (or somewhere else) and initiate the calls. +# +# It must define a python function named "idle". It's allowed +# to use the same script for incoming_script and idle_script +# +idle_script="@pkglibdir@/idle.py" + +# idle_script_interval +# +# The given idle_script will be called in regular intervals. +# +# The length of the intervals in seconds is given here. If you +# don't want to use an idle script, set it to "0" +# +idle_script_interval="60" + +# log_file +# +# The file given here is used for writing normal log messages to. +# +log_file="@localstatedir@/log/capisuite.log" + +# log_level +# +# Selects how chatty CapiSuite will be: +# +# 0 = only very few messages (e.g. system start/stop) +# 1 = some important messages (e.g. connection start/end) +# 2 = many debug messages (nearly every CAPI message logged) +# 3 = all debug messages (all CAPI messages - include every transmitted data package, every script execution) +log_level="1" + +# log_error +# +# The file given here is used for writing error messages to. +# +# Error messages are separated so you can easily find them. +# +log_error="@localstatedir@/log/capisuite.error" + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..9741359 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,126 @@ +/** @file main.cpp + @brief Contains main(). + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "application/capisuite.h" +#include "application/applicationexception.h" +#include "backend/capiexception.h" + +/** @brief main application function + + This is the main() of CapiSuite. +*/ +int main(int argc, char** argv) +{ + CapiSuite cs(argc,argv); + cs.mainLoop(); +} + +/* History + +$Log: main.cpp,v $ +Revision 1.1 2003/02/19 08:19:52 gernot +Initial revision + +Revision 1.13 2003/02/05 15:59:38 ghillie +- moved error handling to capisuite.cpp, so it's logged + +Revision 1.12 2003/01/19 16:36:07 ghillie +- added catch blocks + +Revision 1.11 2003/01/07 14:48:44 ghillie +- added support for commandline parsing (only --help and --config yet) + +Revision 1.10 2003/01/05 12:26:19 ghillie +- renamed fro capisuite.cpp to main.cpp +- moved all code to the CapiSuite class (application/capisuite.*) + +Revision 1.9 2003/01/04 15:54:18 ghillie +- append log files, don't recreate them on every new start +- support for log_level +- added timestamp to log messages + +Revision 1.8 2002/12/18 14:35:39 ghillie +- added setListenFaxG3(). Oops ;) + +Revision 1.7 2002/12/16 13:10:41 ghillie +- added support for using log files + +Revision 1.6 2002/12/14 14:00:04 ghillie +- added code for configfile parsing + +Revision 1.5 2002/12/13 09:22:19 ghillie +- use config.h from automake/autoconf +- error classes support direct output now using operator<< +- began implementing support for config file + +Revision 1.4 2002/12/09 15:19:08 ghillie +- given debug and error streams to FlowControl and Capi objects + +Revision 1.3 2002/12/05 14:47:42 ghillie +- small changes in the constructor parameters for Capi and FlowControl, added call to capi->registerApplicationInterface() + +Revision 1.2 2002/12/02 12:12:56 ghillie +added script names to FlowControl contructor parameters + +Revision 1.1 2002/11/29 11:06:22 ghillie +renamed CapiCom to CapiSuite (name conflict with MS crypto API :-( ) + +Revision 1.5 2002/11/29 10:20:44 ghillie +- updated docs, use doxygen format now + +Revision 1.4 2002/11/25 20:55:27 ghillie +-changed to set Listen on all controllers + +Revision 1.3 2002/11/20 17:14:03 ghillie +fixed small scope problem + +Revision 1.2 2002/11/19 15:57:18 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.1 2002/10/25 13:23:38 ghillie +renamed main.cpp + +Revision 1.8 2002/10/09 11:18:59 gernot +cosmetic changes (again...) and changed info function of CAPI class + +Revision 1.7 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.6 2002/10/04 13:27:15 gernot +some restructuring to get it to a working state ;-) + +does not do anything useful yet nor does it even compile... + +Revision 1.5 2002/09/22 20:06:36 gernot +deleted autocreated file + +Revision 1.4 2002/09/22 14:55:21 gernot +adding audio send module + +Revision 1.3 2002/09/22 14:22:53 gernot +some cosmetic comment improvements ;-) + +Revision 1.2 2002/09/19 12:08:19 gernot +added magic CVS strings + +* Sun Sep 15 2002 - gernot@hillier.de +- put under CVS, cvs log follows above + +* Sun May 20 2002 - gernot@hillier.de +- first version + +*/ diff --git a/src/modules/.cvsignore b/src/modules/.cvsignore new file mode 100644 index 0000000..e995588 --- /dev/null +++ b/src/modules/.cvsignore @@ -0,0 +1,3 @@ +.deps +Makefile +Makefile.in diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am new file mode 100644 index 0000000..b67329c --- /dev/null +++ b/src/modules/Makefile.am @@ -0,0 +1,7 @@ +noinst_LIBRARIES = libccmodules.a +libccmodules_a_SOURCES = audiosend.cpp audiosend.h callmodule.h callmodule.cpp\ + audioreceive.h audioreceive.cpp faxreceive.h faxreceive.cpp connectmodule.cpp\ + connectmodule.h switch2faxG3.cpp switch2faxG3.h readDTMF.cpp readDTMF.h \ + calloutgoing.cpp calloutgoing.h disconnectmodule.cpp disconnectmodule.h \ + faxsend.cpp faxsend.h + diff --git a/src/modules/audioreceive.cpp b/src/modules/audioreceive.cpp new file mode 100644 index 0000000..b1b663a --- /dev/null +++ b/src/modules/audioreceive.cpp @@ -0,0 +1,182 @@ +/* @file audioreceive.cpp + @brief Contains AudioReceive - Call Module for receiving audio. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#define conf_silence_limit 10 + +#include "../backend/connection.h" +#include "audioreceive.h" +#include +#include +#include + +/* Lookup table to reverse the bit order of a byte. ie MSB become LSB */ +/* taken from Sox 12.17.3 */ +unsigned char cswap[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, + 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, + 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, + 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, + 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, + 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, + 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, + 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, + 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, + 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, + 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, + 0x3F, 0xBF, 0x7F, 0xFF +}; + + +AudioReceive::AudioReceive(Connection *conn, string file, int timeout, int silence_timeout, bool DTMF_exit) throw (CapiExternalError) + :CallModule(conn, timeout, DTMF_exit),silence_count(0),file(file),start_time(0),end_time(0), + silence_timeout(silence_timeout*8000) // ISDN audio sample rate = 8000Hz +{ + if (conn->getService()!=Connection::VOICE) + throw CapiExternalError("Connection not in speech mode","AudioReceive::AudioReceive()"); +} + +void +AudioReceive::mainLoop() throw (CapiWrongState, CapiExternalError) +{ + start_time=getTime(); + if (!(DTMF_exit && (!conn->getDTMF().empty()) ) ) { + conn->start_file_reception(file); + CallModule::mainLoop(); + conn->stop_file_reception(); + // truncate the silence away if it's more than one second + if (silence_timeout>8000 && silence_count > silence_timeout) { + struct stat filestat; + if (stat(file.c_str(),&filestat)==-1) + throw CapiExternalError("can't stat output file","AudioReceive::mainLoop"); + if (truncate(file.c_str(),filestat.st_size-silence_timeout+8000)==-1) + throw CapiExternalError("can't truncate output file","AudioReceive::mainLoop"); + } + } + end_time=getTime(); +} + +void +AudioReceive::dataIn(unsigned char* data, unsigned length) +{ + if (silence_timeout) { + unsigned int sum=0; + for (int i=0;idebugMessage("silence",3); + silence_count+=length; + if (silence_count > silence_timeout) + finish=true; + } else + silence_count=0; + } +} + +long +AudioReceive::duration() +{ + return end_time-start_time; +} + +/* History + +$Log: audioreceive.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.18 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.17 2003/01/16 13:02:30 ghillie +- truncate silence away if silence_timeout enabled +- improve duration() to only include real duration of recording + +Revision 1.16 2003/01/04 16:08:48 ghillie +- use new debugMessage method of Connection + +Revision 1.15 2002/12/16 15:06:14 ghillie +use right debug stream now + +Revision 1.14 2002/12/04 11:38:50 ghillie +- added time measurement: save time in start_time at the begin of mainLoop() and return difference to getTime() in duration() + +Revision 1.13 2002/12/02 12:32:21 ghillie +renamed Connection::SPEECH to Connection::VOICE + +Revision 1.12 2002/11/29 10:26:10 ghillie +- updated comments, use doxygen format now +- removed transmissionComplete() method as this makes no sense in receiving! + +Revision 1.11 2002/11/25 11:54:21 ghillie +- tests for speech mode before receiving now +- small performance improvement (use string::empty() instead of comparison to "") + +Revision 1.10 2002/11/22 15:16:20 ghillie +added support for finishing when DTMF is received + +Revision 1.9 2002/11/21 15:32:40 ghillie +- moved code from constructor/destructor to overwritten mainLoop() method + +Revision 1.8 2002/11/21 11:37:09 ghillie +make sure that we don't use Connection object after call was finished + +Revision 1.7 2002/11/19 15:57:19 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.6 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.5 2002/11/13 15:24:25 ghillie +finished silence detection code + +Revision 1.4 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.3 2002/11/12 15:52:08 ghillie +added data in handler + +Revision 1.2 2002/10/29 14:28:22 ghillie +added stop_file_* calls to make sure transmission is cancelled when it's time... + +Revision 1.1 2002/10/25 13:29:39 ghillie +grouped files into subdirectories + +Revision 1.2 2002/10/24 09:55:52 ghillie +many fixes. Works for one call now + +Revision 1.1 2002/10/23 09:47:30 ghillie +added audioreceive module + +*/ diff --git a/src/modules/audioreceive.h b/src/modules/audioreceive.h new file mode 100644 index 0000000..76c6124 --- /dev/null +++ b/src/modules/audioreceive.h @@ -0,0 +1,158 @@ +/** @file audioreceive.h + @brief Contains AudioReceive - Call Module for receiving audio. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef AUDIORECEIVE_H +#define AUDIORECEIVE_H + +#include +#include "callmodule.h" + +class Connection; + +using namespace std; + +/** @brief Call Module for receiving audio. + + This module handles the reception of an audio wave file. It can recognize silence in the signal and timeout after + a given period of silence, after a general timeout or after the reception of a DTMF signal. + + If DTMF abort is enabled, the module will abort immediately if the DTMF receiving buffer (see Connection::getDTMF) + isn't empty when it is created. That allows the user to abort subsequent audio receive and send commands with one + DTMF signal w/o needing to check for received DTMF after each command. + + The call must be in audio mode (by connecting with service VOICE), otherwise an exception will be caused. + + The created file will be saved in the format given by Capi, that is bit-reversed A-Law (or u-Law), 8 kHz mono. + + @author Gernot Hillier +*/ +class AudioReceive: public CallModule +{ + public: + /** @brief Constructor. Create an object and test for audio mode. + + The constructor also converts the given silence_timeout from seconds to number of samples (bytes). + + @param conn reference to Connection object + @param file name of file to save received audio stream to. + @param timeout timeout in seconds after which record is finished, 0=record forever (until call is finished) + @param silence_timeout duration of silence in seconds after which record is finished, 0=no silence detection + @param DTMF_exit true: abort if we receive DTMF during mainLoop() or if DTMF was received before + @throw CapiExternalError Thrown if connection is not in speech mode + */ + AudioReceive(Connection *conn, string file, int timeout, int silence_timeout, bool DTMF_exit) throw (CapiExternalError); + + /** @brief Start file reception, wait for one of the timeouts or disconnection and stop the reception. + + If the recording was finished because of silence, the silence is truncated away from the recorded file + + @throw CapiWrongState Thrown if disconnect is recognized + @throw CapiExternalError Thrown by Connection::start_file_reception(). + */ + void mainLoop() throw (CapiWrongState, CapiExternalError); + + /** @brief Test all received audio packets for silence and count silent packets + + All bytes of a received packages (i.e. 2048 bytes) are partly A-Law decoded, added and + compared to a threshhold. If silence is found, silence_count is increased, otherwise the + counter is reset to 0. + + If the silence_timeout value is reached, the mainLoop is signalled to finish. + */ + void dataIn(unsigned char* data, unsigned length); + + /** @brief Return the time in seconds since start of mainLoop() + + @return time in seconds since start of mainLoop() + */ + long duration(); + + private: + unsigned int silence_count; ///< counter how many consecutive samples (bytes) have been silent + unsigned int silence_timeout; ///< amount of silence samples after which record is finished + string file; ///< file name to save audio data to + long start_time, ///< time in seconds since the epoch when the recording was started + end_time; ///< time in seconds since the epoch when the recording was finished +}; + +#endif + +/* History + +$Log: audioreceive.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.14 2003/01/16 13:03:07 ghillie +- added attribute end_time +- updated comment to reflect new truncation of silence + +Revision 1.13 2002/12/04 11:38:50 ghillie +- added time measurement: save time in start_time at the begin of mainLoop() and return difference to getTime() in duration() + +Revision 1.12 2002/12/02 12:32:21 ghillie +renamed Connection::SPEECH to Connection::VOICE + +Revision 1.11 2002/11/29 10:26:10 ghillie +- updated comments, use doxygen format now +- removed transmissionComplete() method as this makes no sense in receiving! + +Revision 1.10 2002/11/25 11:54:21 ghillie +- tests for speech mode before receiving now +- small performance improvement (use string::empty() instead of comparison to "") + +Revision 1.9 2002/11/22 15:16:20 ghillie +added support for finishing when DTMF is received + +Revision 1.8 2002/11/21 15:32:40 ghillie +- moved code from constructor/destructor to overwritten mainLoop() method + +Revision 1.7 2002/11/19 15:57:19 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.6 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.5 2002/11/13 15:24:25 ghillie +finished silence detection code + +Revision 1.4 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.3 2002/11/12 15:52:08 ghillie +added data in handler + +Revision 1.2 2002/10/29 14:28:22 ghillie +added stop_file_* calls to make sure transmission is cancelled when it's time... + +Revision 1.1 2002/10/25 13:29:39 ghillie +grouped files into subdirectories + +Revision 1.2 2002/10/24 09:55:52 ghillie +many fixes. Works for one call now + +Revision 1.1 2002/10/23 09:47:30 ghillie +added audioreceive module + +*/ diff --git a/src/modules/audiosend.cpp b/src/modules/audiosend.cpp new file mode 100644 index 0000000..3509c41 --- /dev/null +++ b/src/modules/audiosend.cpp @@ -0,0 +1,136 @@ +/* @file audiosend.cpp + @brief Contains AudioSend - Call Module for sending an A-Law file + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "../backend/connection.h" +#include "audiosend.h" + +AudioSend::AudioSend(Connection *conn, string file, bool DTMF_exit) throw (CapiExternalError) +:CallModule(conn,-1,DTMF_exit),file(file) +{ + if (conn->getService()!=Connection::VOICE) + throw CapiExternalError("Connection not in speech mode","AudioSend::AudioSend()"); +} + +void +AudioSend::mainLoop() throw (CapiWrongState, CapiExternalError, CapiMsgError) +{ + start_time=getTime(); + if (!(DTMF_exit && (!conn->getDTMF().empty()) ) ) { + conn->start_file_transmission(file); + CallModule::mainLoop(); + conn->stop_file_transmission(); + } +} + +void +AudioSend::transmissionComplete() +{ + finish=true; +} + +long +AudioSend::duration() +{ + return getTime()-start_time; +} + + +/* History + +$Log: audiosend.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.14 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.13 2002/12/04 11:38:50 ghillie +- added time measurement: save time in start_time at the begin of mainLoop() and return difference to getTime() in duration() + +Revision 1.12 2002/12/02 12:32:21 ghillie +renamed Connection::SPEECH to Connection::VOICE + +Revision 1.11 2002/11/29 10:27:44 ghillie +- updated comments, use doxygen format now + +Revision 1.10 2002/11/25 11:54:35 ghillie +- tests for speech mode before receiving now +- small performance improvement (use string::empty() instead of comparison to "") + +Revision 1.9 2002/11/22 15:16:20 ghillie +added support for finishing when DTMF is received + +Revision 1.8 2002/11/21 15:32:40 ghillie +- moved code from constructor/destructor to overwritten mainLoop() method + +Revision 1.7 2002/11/21 11:37:09 ghillie +make sure that we don't use Connection object after call was finished + +Revision 1.6 2002/11/19 15:57:19 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.5 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.4 2002/11/13 15:26:28 ghillie +removed unnecessary member attribute filename + +Revision 1.3 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.2 2002/10/29 14:28:22 ghillie +added stop_file_* calls to make sure transmission is cancelled when it's time... + +Revision 1.1 2002/10/25 13:29:39 ghillie +grouped files into subdirectories + +Revision 1.8 2002/10/23 15:37:50 ghillie +typo... + +Revision 1.7 2002/10/23 14:10:27 ghillie +callmodules must register itself at connection class now + +Revision 1.6 2002/10/09 14:36:22 gernot +added CallModule base class for all call handling modules + +Revision 1.5 2002/10/05 20:43:32 gernot +quick'n'dirty, but WORKS + +Revision 1.4 2002/10/05 13:53:00 gernot +changed to use thread class of CommonC++ instead of the threads-package +some cosmetic improvements (indentation...) + +Revision 1.3 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.2 2002/10/04 13:27:15 gernot +some restructuring to get it to a working state ;-) + +does not do anything useful yet nor does it even compile... + +Revision 1.1 2002/09/22 14:55:21 gernot +adding audio send module + +*/ diff --git a/src/modules/audiosend.h b/src/modules/audiosend.h new file mode 100644 index 0000000..379925b --- /dev/null +++ b/src/modules/audiosend.h @@ -0,0 +1,164 @@ +/** @file audiosend.h + @brief Contains AudioSend - Call Module for sending an A-Law file + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef AUDIOSEND_H +#define AUDIOSEND_H + +#include +#include "callmodule.h" + +class Connection; + +using namespace std; + +/** @brief Call Module for sending an A-Law file. + + This module handles the sending of an audio file. The audio file must be in bit-inversed A-Law format. It can be created for example + with sox using the suffix ".la". It supports abortion if DTMF signal is received. + + If DTMF abort is enabled, the module will abort immediately if the DTMF receiving buffer (see Connection::getDTMF) + isn't empty when it is created. That allows the user to abort subsequent audio receive and send commands with one + DTMF signal w/o needing to check for received DTMF after each command. + + The connction must be in audio mode (by connecting with service VOICE), otherwise an exception will be caused. + + @author Gernot Hillier +*/ +class AudioSend: public CallModule +{ + public: + /** @brief Constructor. Test if we are in speech mode and create an object. + + @param conn reference to Connection object + @param file name of file to send + @param DTMF_exit set to true, if you want to finish when DTMF signal is received + @throw CapiExternalError Thrown if speech mode isn't established before. + */ + AudioSend(Connection *conn, string file, bool DTMF_exit) throw (CapiExternalError); + + /** @brief Start file transmission, wait for the end of the file or the connection, stop file transmission + + @throw CapiWrongState Thrown when disconnection takes place. + @throw CapiExternalError Thrown by Connection::start_file_transmission, see there for explanation. + @throw CapiMsgError Thrown by Connection::start_file_transmission, see there for explanation. + */ + void mainLoop() throw (CapiWrongState, CapiExternalError, CapiMsgError); + + /** @brief finish main loop if file is completely received + + */ + void transmissionComplete(); + + /** @brief Return the time in seconds since start of mainLoop() + + @return time in seconds since start of mainLoop() + */ + long duration(); + + private: + string file; ///< name of the file to send + long start_time; ///< time in seconds since the epoch when the module was started +}; + +#endif + +/* History + +$Log: audiosend.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.13 2002/12/04 11:38:50 ghillie +- added time measurement: save time in start_time at the begin of mainLoop() and return difference to getTime() in duration() + +Revision 1.12 2002/12/02 12:32:21 ghillie +renamed Connection::SPEECH to Connection::VOICE + +Revision 1.11 2002/11/29 10:27:44 ghillie +- updated comments, use doxygen format now + +Revision 1.10 2002/11/25 21:00:53 ghillie +- improved documentation, now doxygen-readabl + +Revision 1.9 2002/11/25 11:54:35 ghillie +- tests for speech mode before receiving now +- small performance improvement (use string::empty() instead of comparison to "") + +Revision 1.8 2002/11/22 15:16:20 ghillie +added support for finishing when DTMF is received + +Revision 1.7 2002/11/21 15:32:40 ghillie +- moved code from constructor/destructor to overwritten mainLoop() method + +Revision 1.6 2002/11/19 15:57:19 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.5 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.4 2002/11/13 15:26:28 ghillie +removed unnecessary member attribute filename + +Revision 1.3 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.2 2002/10/29 14:28:22 ghillie +added stop_file_* calls to make sure transmission is cancelled when it's time... + +Revision 1.1 2002/10/25 13:29:39 ghillie +grouped files into subdirectories + +Revision 1.10 2002/10/23 15:37:50 ghillie +typo... + +Revision 1.9 2002/10/23 14:10:27 ghillie +callmodules must register itself at connection class now + +Revision 1.8 2002/10/10 12:45:40 gernot +added AudioReceive module, some small details changed + +Revision 1.7 2002/10/09 14:36:22 gernot +added CallModule base class for all call handling modules + +Revision 1.6 2002/10/05 20:43:32 gernot +quick'n'dirty, but WORKS + +Revision 1.5 2002/10/04 15:48:03 gernot +structure changes completed & compiles now! + +Revision 1.4 2002/10/04 13:27:15 gernot +some restructuring to get it to a working state ;-) + +does not do anything useful yet nor does it even compile... + +Revision 1.3 2002/10/02 14:10:07 gernot +first version + +Revision 1.2 2002/10/01 09:02:04 gernot +changes for compilation with gcc3.2 + +Revision 1.1 2002/09/22 14:55:21 gernot +adding audio send module + +*/ diff --git a/src/modules/callmodule.cpp b/src/modules/callmodule.cpp new file mode 100644 index 0000000..395f623 --- /dev/null +++ b/src/modules/callmodule.cpp @@ -0,0 +1,160 @@ +/* @file callmodule.cpp + @brief Contains CallModule - Base class for all call handling modules + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include "../backend/connection.h" +#include "callmodule.h" + +CallModule::CallModule(Connection *connection, int timeout, bool DTMF_exit) +:finish(false),abort(false),timeout(timeout),conn(connection),DTMF_exit(DTMF_exit) +{ + if (conn) + conn->registerCallInterface(this); // Connection needs to know who we are... +} + +CallModule::~CallModule() +{ + if (conn) + conn->registerCallInterface(NULL); // tell Connection that we've finished... +} + +void +CallModule::callDisconnectedPhysical() +{ + abort=true; +} + +void +CallModule::callDisconnectedLogical() +{ + abort=true; +} + +void +CallModule::mainLoop() throw (CapiWrongState, CapiMsgError, CapiExternalError) +{ + if (! (DTMF_exit && (conn->getDTMF()!="") ) ) { + exit_time=getTime()+timeout; + timespec delay_time; + delay_time.tv_sec=0; delay_time.tv_nsec=100000000; // 100 msec + while(!finish && !abort && ( (timeout==-1) || (getTime() <= exit_time) ) ) + nanosleep(&delay_time,NULL); + } + if (abort) + throw CapiWrongState("call abort detected","CallModule::mainLoop()"); +} + +long +CallModule::getTime() +{ + struct timeval curr_time; + gettimeofday(&curr_time,NULL); + return curr_time.tv_sec; +} + +// will be overloaded by subclasses if necessary + +void +CallModule::transmissionComplete() +{ +} + +void +CallModule::callConnected() +{ +} + +void +CallModule::dataIn(unsigned char* data, unsigned length) +{ +} + +void +CallModule::gotDTMF() +{ + if (DTMF_exit) + finish=true; +} + +/* History + +$Log: callmodule.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.14 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.13 2002/12/09 15:43:10 ghillie +- changed WARNING to ERROR severity in exception + +Revision 1.12 2002/12/05 15:06:21 ghillie +- do registering and unregistering only if conn pointer is set (used for CallOutgoing) + +Revision 1.11 2002/11/29 10:27:44 ghillie +- updated comments, use doxygen format now + +Revision 1.10 2002/11/25 11:56:21 ghillie +- changed semantics of timeout parameter: -1 = infinite now, 0 = 0 seconds (i.e. abort immediately) + +Revision 1.9 2002/11/22 15:18:06 ghillie +- added support for DTMF_exit +- de-register Connection object uncondionally in destructor (checking for abort removed) + +Revision 1.8 2002/11/21 15:34:50 ghillie +- mainLoop() doesn't return any value any more, but throws CapiWrongState when connection is lost + +Revision 1.7 2002/11/21 11:37:09 ghillie +make sure that we don't use Connection object after call was finished + +Revision 1.6 2002/11/15 13:51:49 ghillie +fix: call module wasn't finished when call was only connected/disconnected physically + +Revision 1.5 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.4 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.3 2002/11/12 15:52:08 ghillie +added data in handler + +Revision 1.2 2002/10/31 12:40:57 ghillie +changed sleep to nanosleep to make module destruction more quick + +Revision 1.1 2002/10/25 13:29:39 ghillie +grouped files into subdirectories + +Revision 1.3 2002/10/23 14:25:29 ghillie +- a callmodule has to register itself at CallControl so it can be aborted if call is gone +- added distinction between "exited normally" and "aborted because call is gone" -> different results of mainLoop() + +Revision 1.2 2002/10/10 12:45:40 gernot +added AudioReceive module, some small details changed + +Revision 1.1 2002/10/09 14:34:22 gernot +added CallModule class as base class for all call handling modules + +*/ diff --git a/src/modules/callmodule.h b/src/modules/callmodule.h new file mode 100644 index 0000000..e2737fe --- /dev/null +++ b/src/modules/callmodule.h @@ -0,0 +1,180 @@ +/** @file callmodule.h + @brief Contains CallModule - Base class for all call handling modules + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CALLMODULE_H +#define CALLMODULE_H + +#include "../backend/callinterface.h" +#include "../backend/capiexception.h" + +class Connection; + +/** @brief Base class for all call handling modules + + This class implements the CallInterface. It is the base class for all special call handling modules like FaxReceive/FaxSend, AudioReceive/AudioSend, etc. + It contains basic code for registering with the according Connection object, realizing exits because of timeouts, received DTMF and call clearing from the other party. + + The general usage is: create a CallModule object, then run mainLoop(). mainLoop() will exit if necessary. + + To be able to recognize call clearing of the other party, the CapiWrongState exception is used. It is triggered by mainLoop(). + + Sub classes will mainly overwrite mainLoop() and the other signals they need for their tasks. + + If you don't change the semantics in the sub classes, the module will terminate when at least one of the following events occurs: + - logical connection is ended + - physical connection is ended + - DTMF code is received (depending on the value of DTMF_exit given in the constructor) + + @author Gernot Hillier +*/ +class CallModule: public CallInterface +{ + public: + /** @brief Constructor. Register this module at the according Connection object + + @param connection reference to Connection object + @param timeout timeout for this module in seconds (only considered in mainLoop!), -1=infinite (default) + @param DTMF_exit if this is set to true, then the current module is exited if we receive a DTMF tone + */ + CallModule(Connection* connection, int timeout=-1, bool DTMF_exit=false); + + /** @brief Destructor. Deregister this module at the according Connection object. + */ + ~CallModule(); + + /** @brief Waits in busy loop until the module is completed. + + Waits in a busy loop (sleeping 100 msecs between each iteration) until a DTMF signal is reached (if enabled) or the timeout is reached (if enabled). + + This method will likely be overwritten in each sub class. You can call CallModule::mainLoop() there to implement busy loops. + @throw CapiWrongState Something is tried in a wrong connection state. This usually means our call was finished (raised directly). + @throw CapiMsgError A CAPI function hasn't succeeded for some reason (not thrown by CallModule, but may be thrown in subclasses). + @throw CapiExternalError A given command didn't succeed for a reason not caused by the CAPI (not thrown by CallModule, but may be thrown in subclasses) + */ + virtual void mainLoop() throw (CapiWrongState, CapiMsgError, CapiExternalError); + + /** @brief empty here. + + empty function to overwrite if necessary + */ + virtual void transmissionComplete(void); + + /** @brief empty here. + + empty function to overwrite if necessary + */ + virtual void callConnected (void); + + /** @brief abort current modul if logical connection is lost. + + will abort currently running module. May be overwritten with complete new behaviour if needed. + */ + virtual void callDisconnectedLogical (void); + + /** @brief abort current module if physical connection is lost. + + will abort currently running module. Only overwrite if you know what you do! + */ + virtual void callDisconnectedPhysical (void); + + /** @brief finish current module if DTMF is received. + + finish current module if DTMF_exit was enabled + */ + virtual void gotDTMF (void); + + /** @brief empty here. + + empty function to overwrite if necessary + */ + virtual void dataIn (unsigned char* data, unsigned lentgh); + + protected: + /** @brief get the current time in # of seconds sinc 1/1/1970 + */ + virtual long getTime(); + + bool DTMF_exit; ///< if set to true, we will finish when we receive a DTMF signal + bool finish; ///< set this if the module should exit nicely for any reason + bool abort; ///< set this for hard exit because connection is lost, causes CapiWrongState to be throwed in mainLoop + Connection* conn; ///< reference to the according Connection object + long exit_time; ///< time when the timeout should occur + int timeout; ///< timeout period in seconds +}; + +#endif + +/* History + +$Log: callmodule.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.12 2002/12/06 13:08:30 ghillie +minor doc change + +Revision 1.11 2002/11/29 10:27:44 ghillie +- updated comments, use doxygen format now + +Revision 1.10 2002/11/25 21:00:53 ghillie +- improved documentation, now doxygen-readabl + +Revision 1.9 2002/11/25 11:56:21 ghillie +- changed semantics of timeout parameter: -1 = infinite now, 0 = 0 seconds (i.e. abort immediately) + +Revision 1.8 2002/11/22 15:18:06 ghillie +- added support for DTMF_exit +- de-register Connection object uncondionally in destructor (checking for abort removed) + +Revision 1.7 2002/11/21 15:34:50 ghillie +- mainLoop() doesn't return any value any more, but throws CapiWrongState when connection is lost + +Revision 1.6 2002/11/15 13:51:49 ghillie +fix: call module wasn't finished when call was only connected/disconnected physically + +Revision 1.5 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.4 2002/11/13 15:25:08 ghillie +fixed small typo + +Revision 1.3 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.2 2002/11/12 15:52:08 ghillie +added data in handler + +Revision 1.1 2002/10/25 13:29:39 ghillie +grouped files into subdirectories + +Revision 1.3 2002/10/23 14:25:29 ghillie +- a callmodule has to register itself at CallControl so it can be aborted if call is gone +- added distinction between "exited normally" and "aborted because call is gone" -> different results of mainLoop() + +Revision 1.2 2002/10/10 12:45:40 gernot +added AudioReceive module, some small details changed + +Revision 1.1 2002/10/09 14:34:22 gernot +added CallModule class as base class for all call handling modules + +*/ diff --git a/src/modules/calloutgoing.cpp b/src/modules/calloutgoing.cpp new file mode 100644 index 0000000..4ba8601 --- /dev/null +++ b/src/modules/calloutgoing.cpp @@ -0,0 +1,86 @@ +/* @file calloutgoing.cpp + @brief Contains CallOutgoingModule - Call Module for establishment of an outgoing connection and wait for successful connect + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "calloutgoing.h" + +CallOutgoing::CallOutgoing(Capi *capi, _cdword controller, string call_from, string call_to, Connection::service_t service, int timeout, string faxStationID, string faxHeadline, bool clir) +:CallModule(NULL,timeout,false),capi(capi),controller(controller),call_from(call_from),call_to(call_to),service(service),faxStationID(faxStationID),faxHeadline(faxHeadline),clir(clir) +{} + +void +CallOutgoing::mainLoop() throw (CapiExternalError, CapiMsgError) +{ + conn=new Connection(capi,controller,call_from,clir,call_to,service,faxStationID,faxHeadline); + conn->registerCallInterface(this); + + try { + CallModule::mainLoop(); + } + catch (CapiWrongState) {} // filter abort exception + + if (finish) // connection up + result=0; + else if (abort) { // error during connection setup + result=conn->getCause(); + if (!result) + result=2; // no reason available + } else { // timeout exceeded + result=1; + conn->disconnectCall(); + timespec delay_time; + delay_time.tv_sec=0; delay_time.tv_nsec=100000000; // 100 msec + while(conn->getState()!=Connection::DOWN) + nanosleep(&delay_time,NULL); + } +} + +void +CallOutgoing::callConnected() +{ + finish=true; +} + +Connection* +CallOutgoing::getConnection() +{ + return conn; +} + +int +CallOutgoing::getResult() +{ + return result; +} + +/* History + +$Log: calloutgoing.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.3 2002/12/06 13:10:34 ghillie +- corrected some wrong semantics, don't throw CapiWrongState any more +- added waiting for disconnection after timeout +- added result value for reason of abortion or success (getResult() added) +- always return Connection object, don't delete it in any case + +Revision 1.2 2002/12/05 15:55:54 ghillie +- small typo fixed + +Revision 1.1 2002/12/05 15:07:44 ghillie +- initial checking + +*/ diff --git a/src/modules/calloutgoing.h b/src/modules/calloutgoing.h new file mode 100644 index 0000000..fabb683 --- /dev/null +++ b/src/modules/calloutgoing.h @@ -0,0 +1,112 @@ +/** @file calloutgoing.h + @brief Contains CallOutgoing - Call Module for establishment of an outgoing connection and wait for successful connect + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CALLOUTGOINGMODULE_H +#define CALLOUTGOINGMODULE_H + +#include "callmodule.h" +#include "../backend/connection.h" + + +using namespace std; + +/** @brief Call Module for establishment of an outgoing connection and wait for successful connect + + This module serves to initiate an outgoing call and wait for the connection + establishment. The module can finish for three reasons: + - timeout for connecting exceeded + - error during call setup + - successful connect + + You can get the reason for exiting with getResult(). + + @author Gernot Hillier +*/ +class CallOutgoing: public CallModule +{ + public: + /** @brief Constructor. Create object. + + @param capi reference to object of Capi to use + @param controller controller number to use + @param call_from string containing the number to call + @param call_to string containing the own number to use + @param service service to call with as described in Connection::service_t + @param timeout timeout to wait for connection establishment + @param faxStationID fax station ID, only necessary when connecting in FAXG3 mode + @param faxHeadline fax headline, only necessary when connecting in FAXG3 mode + @param clir set to true to disable sending of own number + */ + CallOutgoing(Capi *capi, _cdword controller, string call_from, string call_to, Connection::service_t service, int timeout, string faxStationID, string faxHeadline, bool clir); + + /** @brief Initiate connection, wait for it to succeed + + This call module does never throw CapiWrongState! see getResult() if you need to know if conneciton succeeded. + + @throw CapiExternalError Thrown by Connection::Connection(Capi*,_cdword,string,bool,string,service_t,string,string) + @throw CapiMsgError Thrown by Connection::Connection(Capi*,_cdword,string,bool,string,service_t,string,string) + */ + void mainLoop() throw (CapiExternalError, CapiMsgError); + + /** @brief Finish if we got connection + + */ + void callConnected(); + + /** @brief return reference to the established connection + + @return reference to the established connection, NULL if timout exceeded w/o successful connection + */ + Connection* getConnection(); + + /** @brief return result of connection establishment + + 0: connection is up, call was successful + 1: call timeout exceeded + 2: connection not successful, no reason available + 0x3301-0x34FF: connection not successful, reason given in CAPI coding. See CAPI spec for details. + @return result, see above + */ + int getResult(); + + private: + Connection::service_t service; ///< service with which we should connect + string call_from, ///< CallingPartyNumber + call_to, ///< CalledPartyNumber + faxStationID, ///< fax Station ID to use + faxHeadline; ///< fax headlint to use + Capi *capi; ///< reference to object of Capi to use + _cdword controller; ///< controller to use + bool clir; ///< enable CLIR? (don't show own number to called party) + int result; ///< result of the call establishment process (0=success, 1=timeout exceeded, 2=aborted w/o reason, 0x3301-0x34FF=CAPI errors) +}; + +#endif + +/* History + +$Log: calloutgoing.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.2 2002/12/06 13:12:23 ghillie +- mainLoop() doesn't throw CapiWrongState any more +- added getResult() + +Revision 1.1 2002/12/05 15:07:44 ghillie +- initial checking + +*/ diff --git a/src/modules/connectmodule.cpp b/src/modules/connectmodule.cpp new file mode 100644 index 0000000..5269d71 --- /dev/null +++ b/src/modules/connectmodule.cpp @@ -0,0 +1,61 @@ +/* @file connectmodule.cpp + @brief Contains ConnectModule - Call Module for connection establishment at incoming connection + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "connectmodule.h" + +ConnectModule::ConnectModule(Connection *conn_in, Connection::service_t service, string faxStationID, string faxHeadline) +:CallModule(conn_in),service(service),faxStationID(faxStationID),faxHeadline(faxHeadline) +{} + +void +ConnectModule::mainLoop() throw (CapiWrongState, CapiExternalError, CapiMsgError) +{ + conn->connectWaiting(service,faxStationID,faxHeadline); + CallModule::mainLoop(); +} + +void +ConnectModule::callConnected() +{ + finish=true; +} + +/* History + +$Log: connectmodule.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.6 2002/11/29 10:27:44 ghillie +- updated comments, use doxygen format now + +Revision 1.5 2002/11/25 11:57:19 ghillie +- use service_type instead of CIP value in application layer + +Revision 1.4 2002/11/22 15:18:56 ghillie +added faxStationID, faxHeadline parameters + +Revision 1.3 2002/11/21 15:33:44 ghillie +- moved code from constructor/destructor to overwritten mainLoop() method + +Revision 1.2 2002/11/19 15:57:19 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.1 2002/11/14 17:05:58 ghillie +initial checkin + +*/ diff --git a/src/modules/connectmodule.h b/src/modules/connectmodule.h new file mode 100644 index 0000000..c133f6b --- /dev/null +++ b/src/modules/connectmodule.h @@ -0,0 +1,94 @@ +/** @file connectmodule.h + @brief Contains ConnectModule - Call Module for connection establishment at incoming connection + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CONNECTMODULE_H +#define CONNECTMODULE_H + +#include "callmodule.h" +#include "../backend/connection.h" + + +using namespace std; + +/** @brief Call Module for connection establishment at incoming connection + + This module serves to accept an incoming call and wait for the connection + establishment. It is the first module you should call when an incoming + call is signalled and you want to accept it. + + @author Gernot Hillier +*/ +class ConnectModule: public CallModule +{ + public: + /** @brief Constructor. Create object. + + @param conn reference to Connection object + @param service service to connect with as described in Connection::service_t + @param faxStationID fax station ID, only necessary when connecting in FAXG3 mode + @param faxHeadline fax headline, only necessary when connecting in FAXG3 mode + */ + ConnectModule(Connection *conn, Connection::service_t service, string faxStationID, string faxHeadline); + + /** @brief Accept connection and wait for complete establishment + + @throw CapiWrongState Thrown by CallModule::mainLoop() + @throw CapiExternalError Thrown by Connection::connectWaiting() + @throw CapiMsgError Thrown by Connection::connectWaiting() + */ + void mainLoop() throw (CapiWrongState, CapiExternalError, CapiMsgError); + + /** @brief Finish mainLoop() if call is completely established + */ + void callConnected(); + + private: + Connection::service_t service; ///< service with which we should connect + string faxStationID, ///< fax Station ID to use + faxHeadline; ///< fax headlint to use +}; + +#endif + +/* History + +$Log: connectmodule.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.7 2002/11/29 10:27:44 ghillie +- updated comments, use doxygen format now + +Revision 1.6 2002/11/25 11:57:19 ghillie +- use service_type instead of CIP value in application layer + +Revision 1.5 2002/11/22 15:18:56 ghillie +added faxStationID, faxHeadline parameters + +Revision 1.4 2002/11/21 15:33:44 ghillie +- moved code from constructor/destructor to overwritten mainLoop() method + +Revision 1.3 2002/11/20 17:25:29 ghillie +added missing throw() declaration + +Revision 1.2 2002/11/19 15:57:19 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.1 2002/11/14 17:05:58 ghillie +initial checkin + +*/ diff --git a/src/modules/disconnectmodule.cpp b/src/modules/disconnectmodule.cpp new file mode 100644 index 0000000..4c04739 --- /dev/null +++ b/src/modules/disconnectmodule.cpp @@ -0,0 +1,62 @@ +/** @file disconnectmodule.cpp + @brief Contains DisconnectModule - Call Module for call clearing + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "disconnectmodule.h" + +DisconnectModule::DisconnectModule(Connection *conn, int reject_reason, bool quick_disconnect) +:CallModule(conn),reject_reason(reject_reason),quick_disconnect(quick_disconnect) +{} + +void +DisconnectModule::mainLoop() throw (CapiMsgError,CapiExternalError) +{ + Connection::connection_state_t state=conn->getState(); + if (state!=Connection::DOWN) { + try { + if (state==Connection::WAITING) + conn->rejectWaiting(reject_reason); + else + conn->disconnectCall(quick_disconnect ? Connection::PHYSICAL_ONLY : Connection::ALL); + CallModule::mainLoop(); + } + catch (CapiWrongState) {} // shouldn't throw CapiWrongState as abort won't get true, but who knows... + } +} + +void DisconnectModule::callDisconnectedLogical() +{} + +void DisconnectModule::callDisconnectedPhysical() +{ + finish=true; +} + +/* History + +$Log: disconnectmodule.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.3 2002/12/11 13:40:22 ghillie +- added support for quick disconnect (immediate physical disconnect) + +Revision 1.2 2002/12/06 15:26:30 ghillie +- supports rejecting of call now, too + +Revision 1.1 2002/12/06 12:48:38 ghillie +inital checkin + +*/ diff --git a/src/modules/disconnectmodule.h b/src/modules/disconnectmodule.h new file mode 100644 index 0000000..c54d0f7 --- /dev/null +++ b/src/modules/disconnectmodule.h @@ -0,0 +1,86 @@ +/** @file disconnectmodule.h + @brief Contains DisconnectModule - Call Module for call clearing + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef DISCONNECTMODULE_H +#define DISCONNECTMODULE_H + +#include "callmodule.h" +#include "../backend/connection.h" + + +using namespace std; + +/** @brief Call Module for call clearing + + This module initiates disconnection or rejection of connection and + waits until the physical connection is cleared completely. It's + no problem to call it when the connection is already (partly or completely) + cleared. + + @author Gernot Hillier +*/ +class DisconnectModule: public CallModule +{ + public: + /** @brief Constructor. Create object. + + @param conn reference to Connection object + @param reject_reason reason to give for rejecting a waiting call (1=ignore call, default; + for other values see Connection::rejectWaiting()), ignored for normal disconnect + @param quick_disconnect disconnect physical immediately, this will lead to a protocol error in Layer 3. Use for error cases. + */ + DisconnectModule(Connection *conn, int reject_reason=1, bool quick_disconnect=false); + + /** @brief Initiate call clearing and wait for successful physical disconnection. + + @throw CapiMsgError Thrown by Connection::disconnectCall() or Connection::rejectWaiting() + @throw CapiExternalError Thrown by Connection::rejectWaiting(). + */ + void mainLoop() throw (CapiMsgError,CapiExternalError); + + /** @brief Do nothing as we're waiting for physical disconnection. + */ + void callDisconnectedLogical (); + + /** @brief Finish current module if physical connection is cleared. + + This is overwritten here because we don't trigger an exception at call clearing. + */ + void callDisconnectedPhysical (); + + private: + int reject_reason; ///< saving reject reason given in constructor + bool quick_disconnect; ///< disconnect physical immediately w/o disconnection logical before +}; + +#endif + +/* History + +$Log: disconnectmodule.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.3 2002/12/11 13:40:22 ghillie +- added support for quick disconnect (immediate physical disconnect) + +Revision 1.2 2002/12/06 15:26:30 ghillie +- supports rejecting of call now, too + +Revision 1.1 2002/12/06 12:48:38 ghillie +inital checkin + +*/ diff --git a/src/modules/faxreceive.cpp b/src/modules/faxreceive.cpp new file mode 100644 index 0000000..929d2ad --- /dev/null +++ b/src/modules/faxreceive.cpp @@ -0,0 +1,100 @@ +/* @file faxreceive.cpp + @brief Contains FaxReceive - Call Module for receiving an analog fax (group 3) + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "../backend/connection.h" +#include "faxreceive.h" + + +FaxReceive::FaxReceive(Connection *conn, string file) throw (CapiExternalError) +:CallModule(conn),file(file) +{ + if (conn->getService()!=Connection::FAXG3) + throw CapiExternalError("Connection not in fax mode","FaxReceive::FaxReceive()"); +} + + +void +FaxReceive::mainLoop() throw (CapiWrongState, CapiExternalError) +{ + conn->start_file_reception(file); + CallModule::mainLoop(); + conn->stop_file_reception(); +} + +void +FaxReceive::transmissionComplete() +{ + finish=true; +} + +/* History + +$Log: faxreceive.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.12 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.11 2002/12/09 15:43:45 ghillie +- small typo... + +Revision 1.10 2002/11/29 10:27:44 ghillie +- updated comments, use doxygen format now + +Revision 1.9 2002/11/25 11:58:04 ghillie +- test for fax mode before receiving now + +Revision 1.8 2002/11/21 15:32:40 ghillie +- moved code from constructor/destructor to overwritten mainLoop() method + +Revision 1.7 2002/11/21 11:37:09 ghillie +make sure that we don't use Connection object after call was finished + +Revision 1.6 2002/11/19 15:57:19 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.5 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.4 2002/11/13 15:26:28 ghillie +removed unnecessary member attribute filename + +Revision 1.3 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.2 2002/10/29 14:28:22 ghillie +added stop_file_* calls to make sure transmission is cancelled when it's time... + +Revision 1.1 2002/10/25 13:29:39 ghillie +grouped files into subdirectories + +Revision 1.7 2002/10/23 14:34:26 ghillie +call modules must register itself at CallControl now + +Revision 1.6 2002/10/23 09:45:00 ghillie +changed to fit into new architecture + +*/ diff --git a/src/modules/faxreceive.h b/src/modules/faxreceive.h new file mode 100644 index 0000000..196ed5a --- /dev/null +++ b/src/modules/faxreceive.h @@ -0,0 +1,121 @@ +/** @file faxreceive.h + @brief Contains FaxReceive - Call Module for receiving an analog fax (group 3) + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef FAXRECEIVE_H +#define FAXRECEIVE_H + +#include +#include "callmodule.h" + +class Connection; + +using namespace std; + +/** @brief Call Module for receiving an analog fax (group 3). + + This module handles the reception of an analog fax (fax group 3). It starts the reception and waits for the end of the connection. + + Fax polling isn't supported yet. + + Fax mode must have been established before using this (by connecting in fax mode or switching to fax with Switch2FaxG3), + otherwise an exception is caused. + + The created file will be saved in the format received by Capi, i.e. as Structured Fax File (SFF). + + @author Gernot Hillier +*/ +class FaxReceive: public CallModule +{ + public: + /** @brief Constructor. Test if we are in fax mode and create an object. + + @param conn reference to Connection object + @param file name of file to save recorded stream to + @throw CapiExternalError Thrown if we are not in fax mode. + */ + FaxReceive(Connection *conn, string file) throw (CapiExternalError); + + /** @brief Start file reception, wait for disconnect and stop the reception afterwards + + @throw CapiWrongState Thrown when disconnection takes place. + @throw CapiExternalError Thrown by Connection::start_file_reception. See there for explanation. + */ + void mainLoop() throw (CapiWrongState, CapiExternalError); + + /** @brief finish main loop if file is completely received + */ + void transmissionComplete(); + + private: + string file; ///< file name to save file to +}; + +#endif + +/* History + +$Log: faxreceive.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.11 2002/12/13 11:47:40 ghillie +- added comment about fax polling + +Revision 1.10 2002/11/29 10:27:44 ghillie +- updated comments, use doxygen format now + +Revision 1.9 2002/11/25 21:00:53 ghillie +- improved documentation, now doxygen-readabl + +Revision 1.8 2002/11/25 11:58:04 ghillie +- test for fax mode before receiving now + +Revision 1.7 2002/11/21 15:32:40 ghillie +- moved code from constructor/destructor to overwritten mainLoop() method + +Revision 1.6 2002/11/19 15:57:19 ghillie +- Added missing throw() declarations +- phew. Added error handling. All exceptions are caught now. + +Revision 1.5 2002/11/14 17:05:19 ghillie +major structural changes - much is easier, nicer and better prepared for the future now: +- added DisconnectLogical handler to CallInterface +- DTMF handling moved from CallControl to Connection +- new call module ConnectModule for establishing connection +- python script reduced from 2 functions to one (callWaiting, callConnected + merged to callIncoming) +- call modules implement the CallInterface now, not CallControl any more + => this freed CallControl from nearly all communication stuff + +Revision 1.4 2002/11/13 15:26:28 ghillie +removed unnecessary member attribute filename + +Revision 1.3 2002/11/13 08:34:54 ghillie +moved history to the bottom + +Revision 1.2 2002/10/29 14:28:22 ghillie +added stop_file_* calls to make sure transmission is cancelled when it's time... + +Revision 1.1 2002/10/25 13:29:39 ghillie +grouped files into subdirectories + +Revision 1.7 2002/10/23 14:34:26 ghillie +call modules must register itself at CallControl now + +Revision 1.6 2002/10/23 09:46:08 ghillie +changed to fit into new architecture + +*/ diff --git a/src/modules/faxsend.cpp b/src/modules/faxsend.cpp new file mode 100644 index 0000000..cdc0eaa --- /dev/null +++ b/src/modules/faxsend.cpp @@ -0,0 +1,56 @@ +/* @file faxsend.cpp + @brief Contains FaxSend - Call Module for sending an analog fax (group 3) + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "../backend/connection.h" +#include "faxsend.h" + + +FaxSend::FaxSend(Connection *conn, string file) throw (CapiExternalError) +:CallModule(conn),file(file) +{ + if (conn->getService()!=Connection::FAXG3) + throw CapiExternalError("Connection not in fax mode","FaxSend::FaxSend()"); +} + + +void +FaxSend::mainLoop() throw (CapiWrongState, CapiExternalError, CapiMsgError) +{ + conn->start_file_transmission(file); + CallModule::mainLoop(); + conn->stop_file_transmission(); +} + +void +FaxSend::transmissionComplete() +{ + finish=true; +} + +/* History + +$Log: faxsend.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.2 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.1 2002/12/13 11:44:34 ghillie +added support for fax send + +*/ diff --git a/src/modules/faxsend.h b/src/modules/faxsend.h new file mode 100644 index 0000000..1867105 --- /dev/null +++ b/src/modules/faxsend.h @@ -0,0 +1,78 @@ +/** @file faxsend.h + @brief Contains FaxSend - Call Module for sending an analog fax (group 3) + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef FAXSEND_H +#define FAXSEND_H + +#include +#include "callmodule.h" + +class Connection; + +using namespace std; + +/** @brief Call Module for sending an analog fax (group 3). + + This module handles the send of an analog fax (fax group 3). It starts the send and waits for the end of the connection. + + Fax polling isn't supported yet. + + Fax mode must have been established before using this (by connecting in fax mode or switching to fax with Switch2FaxG3), + otherwise an exception is caused. + + The given file must be in the format used by Capi, i.e. Structured Fax File (SFF). + + @author Gernot Hillier +*/ +class FaxSend: public CallModule +{ + public: + /** @brief Constructor. Test if we are in fax mode and create an object. + + @param conn reference to Connection object + @param file name of file to send + @throw CapiExternalError Thrown if we are not in fax mode. + */ + FaxSend(Connection *conn, string file) throw (CapiExternalError); + + /** @brief Start file send, wait for disconnect and stop the send afterwards + + @throw CapiWrongState Thrown when disconnection takes place. + @throw CapiExternalError Thrown by Connection::start_file_transmission. See there for explanation. + @throw CapiMsgError Thrown by Connection::start_file_transmission. See there for explanation. + */ + void mainLoop() throw (CapiWrongState, CapiExternalError, CapiMsgError); + + /** @brief finish main loop if file is completely sent + */ + void transmissionComplete(); + + private: + string file; ///< file name to send +}; + +#endif + +/* History + +$Log: faxsend.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.1 2002/12/13 11:44:34 ghillie +added support for fax send + +*/ diff --git a/src/modules/readDTMF.cpp b/src/modules/readDTMF.cpp new file mode 100644 index 0000000..92ca13e --- /dev/null +++ b/src/modules/readDTMF.cpp @@ -0,0 +1,78 @@ +/* @file readDTMF.cpp + @brief Contains ReadDTMF - Call Module for waiting for DTMF signals + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "../backend/connection.h" +#include "readDTMF.h" + +ReadDTMF::ReadDTMF(Connection *conn, int timeout, int min_digits, int max_digits) + :CallModule(conn, timeout, false),min_digits(min_digits),max_digits(max_digits) +{ + if (conn->getState()!=Connection::UP) + throw CapiWrongState("Disconnection occured.","ReadDTMF::ReadDTMF()"); + digit_count=conn->getDTMF().size(); +} + +void +ReadDTMF::mainLoop() throw (CapiWrongState) +{ + if (!max_digits || (digit_count < max_digits)) { + do { + finish=false; + CallModule::mainLoop(); + } while (!abort && (digit_countgetDTMF().size(); + if (max_digits && (digit_count >= max_digits)) + finish=true; + else + exit_time=getTime()+timeout; +} + +/* History + +$Log: readDTMF.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.7 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.6 2002/12/06 13:13:06 ghillie +- use Connection::getState() insted of Connection::isUp() + +Revision 1.5 2002/12/05 15:56:24 ghillie +- added checks for connection state to throw exception if connection is down + +Revision 1.4 2002/12/02 21:03:46 ghillie +assured that while loop is entered even if digit_count>=min_digits + +Revision 1.3 2002/11/29 10:28:33 ghillie +- updated comments, use doxygen format now +- removed unnecessary attribute again + +Revision 1.2 2002/11/25 21:01:55 ghillie +- simplified timeout handling (end point is changed now, no new iteration of mainLoop for every DTMF signal) + +Revision 1.1 2002/11/25 11:42:07 ghillie +initial checkin + +*/ diff --git a/src/modules/readDTMF.h b/src/modules/readDTMF.h new file mode 100644 index 0000000..ad09ca8 --- /dev/null +++ b/src/modules/readDTMF.h @@ -0,0 +1,83 @@ +/** @file readDTMF.h + @brief Contains ReadDTMF - Call Module for waiting for DTMF signals + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef READDTMF_H +#define READDTMF_H + +#include "callmodule.h" + +class Connection; + +/** @brief Call Module for waiting for DTMF signals + + This module allows the user to specify how much DTMF digits he wants to read and how long to + wait for them. It doesn't do the actual read, just waits for the given conditions to be fulfilled. + + To use it, create an object and call mainLoop(). After mainLoop() finished, call Connection::getDTMF() to read + the received signals. + + @author Gernot Hillier + +*/ +class ReadDTMF: public CallModule +{ + public: + /** @brief Constructor. Create Object and read the current digit count from Connection. + + @param conn reference to Connection object + @param timeout timeout in seconds after which reading is terminated (only terminates when min_digits are reached!), restarts after each digit + @param min_digits minimum number of digits which must be read in ANY case without respect to timout. Only set to value >0 if you're sure the user will input a digit. + @param max_digits maximum number of digits to read, we abort immediately if this number is reached (0=infinite, only timeout counts) + */ + ReadDTMF(Connection *conn, int timeout, int min_digits, int max_digits); + + /** @brief mainLoop: Waits until the given conditions (see constructor) have been fulfilled + + The module will finish if one of these conditions are true: + + - max_digits is fulfilled + - timeout was reached AND min_digits is fulfilled + + @throw CapiWrongState Thrown if disconnection is recognized + */ + void mainLoop() throw (CapiWrongState); + + /** @brief finish if max_digits is reached, otherwise restart timeout when DTMF signal is received + */ + void gotDTMF(); + + private: + int digit_count, ///< save the current number of digits in receive buffer + min_digits, ///< save min_digits parameter + max_digits; ///< save max_digits parameter +}; + +#endif + +/* History + +$Log: readDTMF.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.2 2002/11/29 10:28:34 ghillie +- updated comments, use doxygen format now +- removed unnecessary attribute again + +Revision 1.1 2002/11/25 11:42:07 ghillie +initial checkin + +*/ diff --git a/src/modules/switch2faxG3.cpp b/src/modules/switch2faxG3.cpp new file mode 100644 index 0000000..cedf931 --- /dev/null +++ b/src/modules/switch2faxG3.cpp @@ -0,0 +1,81 @@ +/* @file switch2faxG3.cpp + @brief Contains Switch2FaxG3 - Call Module for switching to FAXG3 service from another one. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "../backend/connection.h" +#include "switch2faxG3.h" + +Switch2FaxG3::Switch2FaxG3(Connection *conn_in, string faxStationID, string faxHeadline) +:CallModule(conn_in), faxStationID(faxStationID), faxHeadline(faxHeadline) +{} + +void +Switch2FaxG3::mainLoop() throw (CapiWrongState, CapiExternalError, CapiMsgError) +{ + if (conn->getState()!=Connection::UP) + throw CapiWrongState("Disconnection detected","Switch2FaxG3::mainLoop()"); + conn->debugMessage("switching to fax protocol",1); + conn->disconnectCall(Connection::LOGICAL_ONLY); + CallModule::mainLoop(); // wait for DISCONNECT_B3_IND + finish=false; + conn->changeProtocol(Connection::FAXG3,faxStationID,faxHeadline); // change to FaxG3 + CallModule::mainLoop(); // wait for CONNECT_B3_IND + conn->debugMessage("connection re-established, switching to fax protocol finished",1); +} + +void +Switch2FaxG3::callDisconnectedLogical() +{ + finish=true; +} + +void +Switch2FaxG3::callConnected() +{ + finish=true; +} + +/* History + +$Log: switch2faxG3.cpp,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.8 2003/02/10 14:08:21 ghillie +- cosmetical log improvement + +Revision 1.7 2003/01/19 16:50:27 ghillie +- removed severity in exceptions. No FATAL-automatic-exit any more. + Removed many FATAL conditions, other ones are exiting now by themselves + +Revision 1.6 2002/12/18 14:46:23 ghillie +- removed done TODO + +Revision 1.5 2002/12/06 13:13:06 ghillie +- use Connection::getState() insted of Connection::isUp() + +Revision 1.4 2002/12/05 15:56:24 ghillie +- added checks for connection state to throw exception if connection is down + +Revision 1.3 2002/11/29 10:29:12 ghillie +- updated comments, use doxygen format now + +Revision 1.2 2002/11/25 11:58:53 ghillie +use service_type instead of hardcoded CIP values now + +Revision 1.1 2002/11/22 14:59:36 ghillie +initial checkin + +*/ diff --git a/src/modules/switch2faxG3.h b/src/modules/switch2faxG3.h new file mode 100644 index 0000000..dc5dfc9 --- /dev/null +++ b/src/modules/switch2faxG3.h @@ -0,0 +1,86 @@ +/** @file switch2faxG3.h + @brief Contains Switch2FaxG3 - Call Module for switching to FAXG3 service from another one. + + @author Gernot Hillier + $Revision: 1.1 $ +*/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef SWITCH2FAXG3_H +#define SWITCH2FAXG3_H + +#include "callmodule.h" + +class Connection; + +using namespace std; + +/** @brief Call Module for switching to FAXG3 service from another one. + + This module does all the necessary steps to switch from another service (mostly VOICE) + to FaxG3 service (see Connection::service_t). The steps are: + + - disconnect logical connection + - wait for completion of disconnect + - call Connection::changeProtocol() to switch to faxG3 + - wait for logical connection to re-establish +*/ +class Switch2FaxG3: public CallModule +{ + public: + /** @brief Constructor. Create object. + + @param conn reference to Connection object + @param faxStationID fax station ID to use + @param faxHeadline fax headline to use + */ + Switch2FaxG3(Connection *conn, string faxStationID, string faxHeadline); + + /** @brief Do all needed steps (disconnect logical, wait, switch to fax, wait). + + @throw CapiWrongState Thrown by CallModule::mainLoop, Connection::changeProtocol + @throw CapiExternalError Thrown by Connection::changeProtocol + @throw CapiMsgError Thrown by Connection::changeProtocol, Connection::disconnectCall + */ + void mainLoop() throw (CapiWrongState, CapiExternalError, CapiMsgError); + + /** @brief Finish first wait if the logical disconnection succeeded. + */ + void callDisconnectedLogical(); + + /** @brief Finish second wait if logical connection has been re-established + */ + void callConnected(); + + + private: + string faxStationID, ///< fax station ID to use + faxHeadline; ///< fax headline to use +}; + +#endif + +/* History + +$Log: switch2faxG3.h,v $ +Revision 1.1 2003/02/19 08:19:53 gernot +Initial revision + +Revision 1.3 2002/12/02 12:32:54 ghillie +renamed Connection::SPEECH to Connection::VOICE + +Revision 1.2 2002/11/29 10:29:12 ghillie +- updated comments, use doxygen format now + +Revision 1.1 2002/11/22 14:59:36 ghillie +initial checkin + +*/