Initial revision

git-svn-id: https://svn.ibp.de/svn/capisuite/trunk/capisuite@3 4ebea2bb-67d4-0310-8558-a5799e421b66
This commit is contained in:
gernot 2003-02-19 08:19:51 +00:00
commit 4f7e2b2a4e
133 changed files with 14414 additions and 0 deletions

11
.cvsignore Normal file
View File

@ -0,0 +1,11 @@
Makefile
Makefile.in
aclocal.m4
autom4te.cache
stamp-h1
config.*
configure
configure.scan
autoscan.log
capisuite.cron
rc.capisuite

1
AUTHORS Normal file
View File

@ -0,0 +1 @@
Gernot Hillier <gernot@hillier.de>

0
ChangeLog Normal file
View File

25
Makefile.am Normal file
View File

@ -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

103
NEWS Normal file
View File

@ -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

6
README Normal file
View File

@ -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

24
TODO Normal file
View File

@ -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

98
acinclude.m4 Normal file
View File

@ -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

38
capisuite.cronin Executable file
View File

@ -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 <gernot@hillier.de>
#
#
# 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

72
capisuite.spec Normal file
View File

@ -0,0 +1,72 @@
#
# spec file for package capisuite
#
# Author: Gernot Hillier <gernot@hillier.de>
#
# 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

26
configure.in Normal file
View File

@ -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)

9
docs/.cvsignore Normal file
View File

@ -0,0 +1,9 @@
reference
reference/*
manual
manual/*
susebuch
susebuch/*
Doxyfile
Makefile
Makefile.in

969
docs/Doxyfile.in Normal file
View File

@ -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 <filter> <input-file>, where <filter>
# is the value of the INPUT_FILTER tag, and <input-file> 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 =

41
docs/Makefile.am Normal file
View File

@ -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;

11
docs/mainpage.doxy Normal file
View File

@ -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 <a href="../manual/index.html">../manual/index.html</a>.
@endhtmlonly
Thx!
@author Gernot Hillier <gernot@hillier.de>
*/

22
docs/manual.README Normal file
View File

@ -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

1371
docs/manual.docbook Normal file

File diff suppressed because it is too large Load Diff

180
rc.capisuite.in Executable file
View File

@ -0,0 +1,180 @@
#! /bin/bash
# Copyright (c) 1995-2002 SuSE Linux AG, Nuernberg, Germany.
# All rights reserved.
#
# Author: Kurt Garloff <feedback@suse.de>, Gernot Hillier <gernot@hillier.de>
#
# 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 <num> set local and overall rc status to <num>
# 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

5
scripts/.cvsignore Normal file
View File

@ -0,0 +1,5 @@
answering_machine.conf
fax.conf
cs_helpers.py
Makefile
Makefile.in

37
scripts/Makefile.am Normal file
View File

@ -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

2
scripts/README Normal file
View File

@ -0,0 +1,2 @@
This directory holds the global python scripts for CapiSuite. Please
see the CapiSuite documentation for further details.

View File

@ -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 number>"
# 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="<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"

168
scripts/capisuitefax Executable file
View File

@ -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 <dial> file1 [file2...] or"
print "capisuitefax [-q] -a <id> or"
print "capisuitefax [-h] [-l]"
print
print "possible options are:"
print
print "-d <dial>, --dialstring=<dial> send fax to this number (required)"
print "-a <id>, --abort=<id> 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

340
scripts/cs_helpers.pyin Normal file
View File

@ -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
#

142
scripts/fax.confin Normal file
View File

@ -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 <space>,'+','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="<num1>,<num2>, ..."
# 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="<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"

187
scripts/idle.py Normal file
View File

@ -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<len(delays)):
next_delay=delays[tries]
else:
next_delay=delays[-1]
starttime=time.time()+next_delay
capisuite.log("job "+job_fax+": delayed for "+str(next_delay)+" seconds",2)
tries+=1
cs_helpers.writeDescription(sendq+job_fax,"dialstring=\""+dialstring+"\"\n"
+"starttime=\""+time.ctime(starttime)+"\"\ntries=\""+str(tries)+"\"\n"
+"user=\""+user+"\"")
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
#

464
scripts/incoming.py Normal file
View File

@ -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 (i<len(messages)):
oldmessages.append(messages[i])
if (messages[i]<=lastinquiry):
del messages[i]
else:
i+=1
print "messages",messages
print "oldmessages",oldmessages
cs_helpers.sayNumber(call,str(len(messages)),curr_user,config)
if (len(messages)==1):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-nachricht.la"),1)
else:
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-nachrichten.la"),1)
# menu for record new announcement
cmd=""
while (cmd not in ("1","9")):
if (len(oldmessages)):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"zum-abhoeren-1.la"),1)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"fuer-neue-ansage-9.la"),1)
cmd=capisuite.read_DTMF(call,0,1)
if (cmd=="9"):
newAnnouncement(call,userdir,curr_user,config)
return
# start inquiry
for curr_msgs in (messages,oldmessages):
cs_helpers.sayNumber(call,str(len(curr_msgs)),curr_user,config)
if (curr_msgs==messages):
if (len(curr_msgs)==1):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-nachricht.la"),1)
else:
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-nachrichten.la"),1)
else:
if (len(curr_msgs)==1):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"nachricht.la"),1)
else:
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"nachrichten.la"),1)
i=0
while (i<len(curr_msgs)):
filename=userdir+"received/voice-"+str(curr_msgs[i])+".la"
descr=cs_helpers.readConfig(filename[:-2]+"txt")
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"nachricht.la"),1)
cs_helpers.sayNumber(call,str(i+1),curr_user,config)
if (descr.get('GLOBAL','call_from')!="??"):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"von.la"),1)
cs_helpers.sayNumber(call,descr.get('GLOBAL','call_from'),curr_user,config)
if (descr.get('GLOBAL','call_to')!="??"):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"fuer.la"),1)
cs_helpers.sayNumber(call,descr.get('GLOBAL','call_to'),curr_user,config)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"am.la"),1)
calltime=time.strptime(descr.get('GLOBAL','time'))
cs_helpers.sayNumber(call,str(calltime[2]),curr_user,config)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"..la"),1)
cs_helpers.sayNumber(call,str(calltime[1]),curr_user,config)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"..la"),1)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"um.la"),1)
cs_helpers.sayNumber(call,str(calltime[3]),curr_user,config)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"uhr.la"),1)
cs_helpers.sayNumber(call,str(calltime[4]),curr_user,config)
capisuite.audio_send(call,filename,1)
cmd=""
while (cmd not in ("1","4","5","6")):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"erklaerung.la"),1)
cmd=capisuite.read_DTMF(call,0,1)
if (cmd=="1"):
os.remove(filename)
os.remove(filename[:-2]+"txt")
if (curr_msgs==messages): # if we are in new message mode...
oldmessages.remove(curr_msgs[i]) # ... don't forget to delete it in both lists
del curr_msgs[i]
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"nachricht-gelöscht.la"))
elif (cmd=="4"):
if (curr_msgs[i]>lastinquiry):
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
#

59
scripts/remote-connect.py Normal file
View File

@ -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.time()<start_t+15)):
cmd=os.popen("/usr/sbin/cinternet --status")
lines=cmd.readlines()
cmd.close()
for l in lines:
if (l[0:6]=="status"):
c_status=l[9:]
c_status=string.strip(c_status)
print c_status
time.sleep(1)
# get ip address
save_lang=os.environ["LANG"]
os.environ["LANG"]=""
cmd=os.popen("/sbin/ifconfig ppp0")
lines=cmd.readlines()
cmd.close()
os.environ["LANG"]=save_lang
for l in lines:
index=string.find(l,"inet addr:")
if (index!=-1):
index+=10
rindex=string.find(l," ",index)
ip_address=l[index:rindex]
# play ip address
for i in ip_address:
cc.audio_send(i+".la")
cc.disconnect()
except cc.CallGoneError:
print "schimpf schimpf"
cc.callWaiting=callWaiting
cc.callConnected=callConnected

BIN
scripts/waves/..la Normal file

Binary file not shown.

2
scripts/waves/.cvsignore Normal file
View File

@ -0,0 +1,2 @@
Makefile
Makefile.in

BIN
scripts/waves/0.la Normal file

Binary file not shown.

BIN
scripts/waves/1.la Normal file

Binary file not shown.

BIN
scripts/waves/10.la Normal file

Binary file not shown.

BIN
scripts/waves/11.la Normal file

Binary file not shown.

BIN
scripts/waves/12.la Normal file

Binary file not shown.

BIN
scripts/waves/13.la Normal file

Binary file not shown.

BIN
scripts/waves/14.la Normal file

Binary file not shown.

BIN
scripts/waves/15.la Normal file

Binary file not shown.

BIN
scripts/waves/16.la Normal file

Binary file not shown.

BIN
scripts/waves/17.la Normal file

Binary file not shown.

BIN
scripts/waves/18.la Normal file

Binary file not shown.

BIN
scripts/waves/19.la Normal file

Binary file not shown.

BIN
scripts/waves/2.la Normal file

Binary file not shown.

BIN
scripts/waves/20.la Normal file

Binary file not shown.

BIN
scripts/waves/3.la Normal file

Binary file not shown.

BIN
scripts/waves/30.la Normal file

Binary file not shown.

BIN
scripts/waves/4.la Normal file

Binary file not shown.

BIN
scripts/waves/40.la Normal file

Binary file not shown.

BIN
scripts/waves/5.la Normal file

Binary file not shown.

BIN
scripts/waves/50.la Normal file

Binary file not shown.

BIN
scripts/waves/6.la Normal file

Binary file not shown.

BIN
scripts/waves/60.la Normal file

Binary file not shown.

52
scripts/waves/7.la Normal file
View File

@ -0,0 +1,52 @@
ª*
j+j«ë+ë+뫪**ê++ª«êª*ë‹+ª*j+k*«j*jªªŠ ëªê몪 k«*+Š+*jêŠëkkjë+j+«êª*J*+k««êk:ê *««ª û+ºª«ª«*Šêk+ê+ëêŠëêë+*ªŠk++ ***Ê+«ê+ª«*ª‹ëª*+ꪫ+k***ëªë*:+*++j««Ë««Š«ëªêjj*ê*jkÊK+«kªª*ˋꪪÊ:*«+Ë+kk+ëªëê:Šj
ë‹
ŠË«‹+«kŠ*ÊëêªkªÊkj+Kë
« êk«êêëjz*ê+ ë*뫪ꊫK kjŠÊ*««+êjjëË«Ê*« ªŠ«ê‹«
Ê«ë ++«Šëêj+ë«+kj«*
*
ú ë*KË+kŠŠ+*
Šª*ª«êkë«ëªŠªËúªûjë*:* ««Ë º+Šj+ª«êËŠêKêªË«Jj«êêJŠ*k+ ëëë+ë«««*ë
ŠëJêk ªêjê++jŠÊ«ë«jêË˪êúÊ«Ë[º:*ª;ºŠ;«+«« ëª kª:újëëªÊJÛ*:*Kjª Ê몚ÊKêj˪«û
«ëª«ÊÚêûªê‹ «;JŠªŠ:›Ë
ªjʪ»**Ë+Š «Jºëë+»ªÊª : +«»êK:
+jjëŠë+«*«*Këjû*zëŠÊk+jj«J««ûË+«ú»ªªŠjúº*+«ª
«kJ;Kªûëk*ªŠkŠ:
*kj»K
k**ºëš»ëJŠª*+J* ++**j«ëk;êúªjª;«Š‹*
« ëkú
»ÊÚêKjKkÊëªzËz{ªJ ‹«:;êJkJ* ë*+ *j+j*ªj*kjk «ªkŠú+ k+ª
êkëjjë«*j«ûjjK*ªªëª««ºêjëë+kê «j«+ªk «
êêÊ
++ê‹‹« «êëë*ê*Š« jëkŠª+êÊ«ëê‹‹*ëªj«+«ªë«**Šªªj*ª*ª++ë+ëªëkª+**ª«««+«*êêêjj««*«*ê+ë«ë‹êª ª«ëª+«+k«ê*ê*«
*ÊŠ«ê«++«ëª«+ªëë++«*k+ª‹*ë *kë*+«ª**ŠŠªê*ªŠjŠzÊ
zÊ«kûãó3¯/ƒ£ËŠrî>^ž~Þ¾n²z*cïÏߧ'_ߟ<C39F>¯³kJ¢òRnN>~Þ>ŽzêËÿ¯Ç¿g§ïoºZNFÆvöFæÎrã/_Ç÷ש)W÷wGߟï#Zî¾öV(hˆè訆N2ϧÇw©©W—Çg›£Šº+ ;ïccÊú¦¨è¨¨VFÞ®J“§7W)))©W—‡‡gsƒ{r¦¾†ÆÖ(VvÖöf~.Ês'GWWW©÷Ç'Oó»:º¢*Ê;3ªª
⦞FV(¨¨–†ËÏç·—©)©)©·Ç翯ûkb®Ž¦&¶6–Öö6&Îâësg·—W©W—·'Ïs«ò.N®â«Ã³/Ã#ËRŽ&Æv¨VV¶&ŽÿçÇ—W©W×7ÇSOÛj R~&~v¶vVV6v¶Þ>2k“Ÿg·×Wש—Ç_osê¢òî"
úƒ³3«£¢Ž¾F¶V(Ö¨vÆÞÂêS_÷—©)©W—·çŸÏÓ»ë.¾¦fF¶6Vv6Æf>îzã¯ßgGww—÷Ç'¿³;R2îŽ"z:{#³3{º.þ†¶¨VÖö†ž2«S§·—×WWwGGg?ŸS»[Ò¾&¦vöö¨¨FF^ÎêƒO§‡7————7ǧëîÒŽ2Zšk£ÛCë+ÊξæÆöÖ6öF^ÎK/§‡·w×—w7‡''ÿ³;¢zŽþž†6öv6Ff¦Ž¢ Ó?çÇ7—÷GgŸs ŠR¾n>.zë[£#[«:òΦ†F6¶ÆfÞ.úo§gÇ7÷w÷ÇÇß?ŸSË£:.Ž&F6övF†žîb+C¿§gG7777ç?ƒ›ë®>òËŠƒÓkºëZ>Þæ¶ö¶¦NÂK/Ÿ‡·÷——w7G‡çßSÃ#*"îζ6vö66¦N2*³Oç··Çç_O¯»ÊârîÎÎŽ®rbzjË[#;ëºZ2R.n.šŠ»[ÓÓ“3³Cƒã#[{;»Ë+jŠ:BòòÒr²BâZ:Š«»ãÃsÓ¯¯s3ƒÛ;k*
:zšÚšzúººº:ºjjjê*ªjŠjŠŠêª«+++««+kkëë Ë»û{ÛÛ[Û›û ëëë«+*ÊJJúZ¢bâ""Úš:º
Êê«kK{Û#ƒÃûK+j:Ú¢¢"âZzºÊj+Ë»K»»KK»»»»»;K 몊ŠJúzššzzzúúúzZJ#Ûêkë*jË‹Û#k«ë+ªëËk ‹Ë‹ª***jêjJ
jj
ÊÊÊŠŠÊºÊjj*j
ªëë++ Kûû;ËK;ûK+«‹Ë +jêŠ
:úJ
*jJººÊŠêjŠÊÊ
Šª+kK;û;;»û{»ËË k+jJJŠ
ÊJº:ººÊj«+«+«ª+ëk Ë ËËk++«**jŠŠÊºº
**ÊŠÛCÛÛó»{
ÂÎî.Zëª;SÏ¿Ï/{»jÂÂ2òBÂ2ÂâÂBâÚZzúúúÚ¢¢:ësÿ_§_ÿÏS"®~f†††¦>î«c¯ÿ¿Ïï¯/Ó³ãã#»ë+*b²ÒînnŽîî.²šŠªk»ãÃó¯SoOo<>/û«J2ž¦††æ¦ž>r«c“<63>Ï¿O<0F>ﯯsÛ»{*júÚÚ"bÂbÂ2Ò2ò2B2²ÂbZ+ï<C383>Ÿ§§§ç_Ï/Ã
RŽž&æ&æ&~RÚ*;ãS/¯S¯/ïo¯SSCc‹ëºB2²ÒÒ®.î.®r²BZ
ë«‹[££³#C#<13>ƒ³{{ûŠb2®NNÎ>¾îRš«kÃÓ/<2F>O//¯sƒckË늺Ê
Z:úÚZâ‚₲BBrb+#ÃS?§§?<3F>ÓK"Rî~ÞÞ¦&ž®+ sï/ï¯/¯C“sC[ K *šÚ®²rrššÊ
zºjê*kË»[óSï¿¿ÏOoƒ;
®î>ÞÞžjË[[Ã3/¿O¯ÓsÓ/‹‹Š"zÚšzbÂÚú¢""âbšúZâZª3o/OŸ?ÿß?¯C²ŽnþÞŽRš KÛ³³cSÿ¿sóC“SS»»{úêêÊš2râb22Z2²¢â¢ššš
º
{C“ï¿?Ï¿ÿÏ/ÃKÂn~þþ~¾.Új«Ë㓯o¯o¯3ãc ;«êŠ
úzú¢"Ú‚²¢²"¢Ú⚊K3¯<33>?OÏ??<3F>ó#ºòR¾¾¾~~Nî
{ÃÃï¯/ïï3ãã{{k«êJŠŠÊúÚ²bbâââbbZz
Ê ïOo<>o<EFBFBD>/ó;zbŽÎŽNîBj{£ƒóÓ/o/SC[»»K» «
JJʺÚZââ"¢âZšúºŠjšº£<0F>³¯ocËK

BIN
scripts/waves/70.la Normal file

Binary file not shown.

BIN
scripts/waves/8.la Normal file

Binary file not shown.

BIN
scripts/waves/80.la Normal file

Binary file not shown.

BIN
scripts/waves/9.la Normal file

Binary file not shown.

BIN
scripts/waves/90.la Normal file

Binary file not shown.

12
scripts/waves/Makefile.am Normal file
View File

@ -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-gelöscht.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)

2
scripts/waves/README Normal file
View File

@ -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.

BIN
scripts/waves/am.la Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
scripts/waves/beep.la Normal file
View File

@ -0,0 +1 @@
‡ם<EFBFBD><EFBFBD>ם+ל<><D79C>ל«ם<C2AB><D79D>ם«ל<C2AB><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>ם×ל<C397><D79C>ל×ם<C397><D79D>םJל<4A><D79C>ל־ ֽ

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
scripts/waves/ein.la Normal file

Binary file not shown.

BIN
scripts/waves/erklaerung.la Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
scripts/waves/fuer.la Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
scripts/waves/nachricht.la Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
scripts/waves/uhr.la Normal file

Binary file not shown.

BIN
scripts/waves/um.la Normal file

Binary file not shown.

31
scripts/waves/und.la Normal file
View File

@ -0,0 +1,31 @@
©‰¹y™¹é‡‹æ¨ȈV†n+Ó?ÿ?oC«²N¦&æÞ2
Ë;K[óϧ?“z²¢£'©‰‰©OhØ àX¸v®Ã_‡w©‰¹yÙyÉ7Ûfèȸˆ¨f.«“oOÏÏoóK¢.~¾Ž’"J
Š‹[/oo¯ó¿G× É)÷Šv8à`€ ¸î³—)Éù™YÙ9é§öˆHHhöZc??ï¯c*.^†FƦ/oÓó#s///ÃÛJ¢C_w‰ iÇúø `àˆÆb¯§ÇW‰¹¡Ù9©¾VˆÈˆæ.J[³Ã¯oï/ÊÎf6Æ~¢¿ÏSƒ{[Óï/Cj²Ãç)¹Iig¾hX`à ÈÖžªcÿ'× y¡¡‰ç"6èh(ö^îÊê{[oÏïëîfv6¦"#O3Ë+êoO#ZRwIÙ9éCø`` øÖ6¾òú/çiy!a¡IǶ¨¨öf¾Žîʯ_‡ç<E280A1>JÆvÆ^‚ëûƒ£[S??ÿ<>Ûʾ~Ú÷‰™Ùé'h¸x¸¸èhVæi!ÙyigϪb®Þ^6¶†æ«/çOSú.Î.¾f¶&K§?/++{#/+îŽ>Kw y9—sfhHˆˆHˆÈ¨Î¯©IùI)×7#†ÆfÎkÃ[ŠzúK;jR¦¶Æ^ÒÊ#c[ƒ[ƒsã;êʳs;šú; I >((È88ˆvß·——©é©ÓÚÒBºëº².BKóï#Zž¾Rî&ÆƦÚS<C39A>Ó ‹ƒ[ëªê{{{{;'i éfvˆx8¨æ."k<>)i)wç'/;2RÚê;»k*ŠºšÚÒNξÞ&^Î꣓S/ó££o¿ÏkëêKSÏÃJJºÊêÚžîR榎BÊËûcOç§ß_?ïã«Êºb²âZÚ¢B"ZB2"ºJ:ŠŠªkk «‹›ûË+K£Ïß_ßOSóÛº®žfFvv6Ææ>òÊ[<5B>_gÇGÇçO¯C£K*JúúšÚ²n>žþNNnÒò"Š+Ë»›#ãƒCCcÃ/?Ÿ'ç?ÏÓz.^†¶ö66Fæ~îZK/ÿ§çg租OËkëk+
Êz²r.>>>Nn®2â:JJ«»ãCƒ£{›³ï??“ûÊþ&¶¶F&>®šûÓŸ§§ßŸ?O<>/“Ã[K +«ªjÊz"B®ŽÎî.RâZºjkûÛ[{C/¿§'§_ÿ<5F>¯cŠ2^fÆFÆæžN®ï¿ÿŸ¿ÏﯯÓ3ãË +j
ºúÒînn®RÒrB⺊+KË s<>ß?S šN^æ†f¦ž¾."ª[S<>Ï?ÿ?OïSÓó ë«ê
ºúzbÒ®Ò2ÂòBBbÚÚ:ŠªËk;c“o¿?O<>¯ó+šòŽ>¦^ž¾Žò*{C/ÏO¿??Ï<>ï“ÃÛ»ëjÊJ:š"²ÒîŽ.²2ÂbbÚúJjK;Këkûc“/oï“c£{+J.N~ž~¾Îîò¢j»Ã¯<C383>OOO<0F>o¯óÛ»«Ê:úbB®RòB2²b"¢ºŠ* »‹‹û[s¯o<C2AF>o/“c{ëênN>þþNîrZJ+{¯o<C2AF><6F><EFBFBD>oï/S“ãûË+Š:ÚBÒRr2²²²ÂâÚúʪ««ë K;c3Óï¯ÓsãËêúâînŽn.²âz
ÛƒóS¯/¯SÓsC#K+ê
:Úâb"¢"bbâ"ZúÊŠ
ÊŠêª+k û[#ãÃ33³[û‹ª
ú"""‚Â":ʪ+ û[c#£ã#ÛKk+«*jÊJÊ
Šjêj
ººJÊŠŠê«ëë kkËkK»ûû{»ë«*êŠ
jŠjjjj*««*ªª*ª«ª*ªªªª+ª«+kë««ª««*««ª+këë+«*ê*êŠjê***jêê*ª+ë+ë+«ªªª+ë«««++ëë««««**ªªª«ªªªêꪫª«++ëë뫪*êêê*ê*ª+몪ª**«ªª+ëë++««+뫪«+ª«*****ê**«+++ë«******ªª««++«««ëë««ªª«+ª*ªªª*jª«««+«ªª«+ª**ª+««ª««ª++ªª««ª*ê*«+«+ë++k뫪**ª«ª*ê*ª*êª*ªªªë«««ëëª*ª****ª*ªëëë«+ë«++«ªª*«ª**ê*êª+«++««*ꪪ««+«ª+ë+ªª*ê*«ªª««ë+++«ª***ê*«++««ªª+«ªêjꪫª*«ª«+««««««**ª+ë+++««««ª*ªª«««+«**ª*jê**+++«««ª«««+«ª«««*ªªªª**ª++«««ª«*ê**ªª«+ë‹ë«ªêê**«ª++++««ªªª****ªªª«+ªª««+«*««++ªªªªª«««++««+ªªªª«ªªª«««+ªªªª«**ªª+ªªªª**ªªª«+++++«ª«««««««ªªªª*«+ªªªªª«ª***ª«ª«ª«+««ª*ª«+««««+««*ê*ª«ªª««++«**ªª««ªª«««««ªªª+++«ª««****ª«ë+++«êêê***++«ªª+««ªj**
7I;æoö¨/.¾šJ3Ë['ëOOºËoÚ"Ó^‚Þ
‡n¾2/û:.ʯò?âzOzã:£{ӊ毺¾Ïþz¯N<C2AF>Úÿ"
/rcÃ:oK.«cš:£Îj«Žâ;Z/¢ªo®ZOâ:kŠ«ºóz§Š[³êÊBcnCz[
{"o:Ûº>ÚznJjËŠ cº“oj³Kúª¢Ë®âãÒâëjËË£{óêk :ËJš+Ê«+Jkú:+ÊKKº¢Ê«òŠ
ÛS**ãÛKŠúkÊë:¢BšëÛ£o{ºkzŠ"ârªê[ƒÓ{sãK¢»¢
Ž:j+;»³{ëj{š
Â.KûÛÛk㪻ë2ËÛêJ:*«JËâZŠK+£#zëZzêZJÊK[êJë»;Ë£Kº«ê""ªJÊ;k*+»«Kã+ªk«JJâš+Kc»ëkÊŠk
"ZŠªº+ëêË;k
ªëk*Ë{»ÛkkêjJúJÛ
ú"Z"*{ÛJººË»j*šÊêº
º+ã;Ë Kªj*zº
ÊJzê+«këk«+Kk«**k+ë
j««*jê+ë*몊*ªëkª«*Ê*+ªª*ªªë+*ªëkë+*j*êjê*ª+k«*ªë++«ª+«««ªª+«ªª*êª+++«««+++ªêjêꪫ««««««««ª««+*ê++««««ª«ë«ê**ª««ª«+++ª***ª*ꪪ+«++«ªª+«ª«ªêŠ*ª**ª+‹ë++++ªª«**«+««ª««ª*«ªª«+«*ª«ªªª*ª*ª«ª««+«««ªªª«+*«+«++ªªª*«ë+ªª«ªª+«ê*ª«ªª«««««ªªª«ª*ª«+«+«ª«ªª«+«ª«+++ªêjªª***«««+++«ª++«*j*«««ª«+

BIN
scripts/waves/von.la Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

7
src/.cvsignore Normal file
View File

@ -0,0 +1,7 @@
*.o
capisuite
capisuite.conf
.deps
.deps/*
Makefile.in
Makefile

20
src/Makefile.am Normal file
View File

@ -0,0 +1,20 @@
pkgsysconfdir = @sysconfdir@/capisuite
bin_PROGRAMS = capisuite
capisuite_LDADD=application/libccapplication.a modules/libccmodules.a \
backend/libccbackend.a
capisuite_SOURCES=main.cpp
SUBDIRS = application backend modules
pkgsysconf_DATA = capisuite.conf
EXTRA_DIST = capisuite.conf.in
capisuite.conf: capisuite.conf.in
rm -f $@
sed -e 's,@pkgdatadir\@,$(pkgdatadir),g' \
-e 's,@pkglibdir\@,$(pkglibdir),g' \
-e 's,@localstatedir\@,$(localstatedir),g' \
-e 's,@spooldir\@,$(spooldir),g' $< >$@
clean-local:
rm -f capisuite.conf

View File

@ -0,0 +1,3 @@
.deps
Makefile
Makefile.in

View File

@ -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

View File

@ -0,0 +1,121 @@
/** @file applicationexception.h
@brief Contains ApplicationError - Exception class for errors in the application layer
@author Gernot Hillier <gernot@hillier.de>
$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 <iostream>
#include <string>
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<string>, 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
*/

View File

@ -0,0 +1,590 @@
/* @file capisuite.cpp
@brief Contains CapiSuite - Main application class, implements ApplicationInterface
@author Gernot Hillier <gernot@hillier.de>
$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 <Python.h>
#include <cStringIO.h>
#include <sstream>
#include <signal.h>
#include <getopt.h>
#include <unistd.h>
#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;i<info.size();i++) {
(*debug) << info[i];
if (i!=info.size()-1 && info[i]=='\n')
(*debug) << prefix();
}
(*error) << prefix() << "CapiSuite " << VERSION << " started." << endl;
// backend init
capi=new Capi(*debug,debug_level,*error);
capi->registerApplicationInterface(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<cStringIO.h> 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<t.size();i++)
if (t[i]<'0' || t[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 <gernot@hillier.de>" << 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<char*>() 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
*/

277
src/application/capisuite.h Normal file
View File

@ -0,0 +1,277 @@
/** @file capisuite.h
@brief Contains CapiSuite - Main application class, implements ApplicationInterface
@author Gernot Hillier <gernot@hillier.de>
$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 <Python.h>
#include <map>
#include <queue>
#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 <Connection*, CallControl*> instances; ///< saving pointers to all created CallControl instances indexed by Connection pointers
queue <Connection*> 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<string,string> 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
*/

File diff suppressed because it is too large Load Diff

View File

@ -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 <gernot@hillier.de>
$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 <Python.h>
#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 <b>borrowed</b> 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
*/

View File

@ -0,0 +1,186 @@
/* @file incomingscript.cpp
@brief Contains IncomingScript - Incoming call handling. One object for each incoming call is created.
@author Gernot Hillier <gernot@hillier.de>
$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<IdleScript*>(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<IdleScript*>(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()
*/

View File

@ -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 <gernot@hillier.de>
$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 <string>
#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()
*/

View File

@ -0,0 +1,318 @@
/* @file incomingscript.cpp
@brief Contains IncomingScript - Incoming call handling. One object for each incoming call is created.
@author Gernot Hillier <gernot@hillier.de>
$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<IncomingScript*>(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<IncomingScript*>(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
*/

View File

@ -0,0 +1,216 @@
/** @file incomingscript.h
@brief Contains IncomingScript - Incoming call handling. One object for each incoming call is created.
@author Gernot Hillier <gernot@hillier.de>
$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
*/

View File

@ -0,0 +1,138 @@
/* @file pythonscript.cpp
@brief Contains PythonScript - Read a python script and call a function in own thread
@author Gernot Hillier <gernot@hillier.de>
$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 <cStringIO.h>
#include <sstream>
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<char*>(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<char*>(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<length-1;i++) {
error << traceback[i];
if (traceback[i]=='\n')
error << prefix() << "Python traceback: ";
}
error << endl;
// undo redirection
if ( PyDict_SetItemString(module_dict,"stderr",PyDict_GetItemString(module_dict,"__stderr__"))!=0 ) {
throw ApplicationError("unable to reset sys.stderr","PythonScript::run()");
}
} else {
if (result!=Py_None)
error << prefix() << "WARNING: "+functionname+" shouldn't return anything";
Py_DECREF(result);
result=NULL;
}
}
catch(ApplicationError e) {
if (scriptfile)
fclose(scriptfile);
if (result)
Py_DECREF(result);
throw;
}
}
void
PythonScript::final()
{
delete this;
}

View File

@ -0,0 +1,115 @@
/** @file pythonscript.h
@brief Contains PythonScript - Read a python script and call a function in own thread
@author Gernot Hillier <gernot@hillier.de>
$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 <Python.h>
#include <ostream>
#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!
*/

Some files were not shown because too many files have changed in this diff Show More