- merge branches/py-lib, revisions 265-325 to trunk

git-svn-id: https://svn.ibp.de/svn/capisuite/trunk/capisuite@328 4ebea2bb-67d4-0310-8558-a5799e421b66
This commit is contained in:
gernot 2004-10-31 14:07:07 +00:00
parent b75a6e0f54
commit a22481c4fc
48 changed files with 4088 additions and 1526 deletions

View File

@ -1 +1,2 @@
Gernot Hillier <gernot@hillier.de>
Hartmut Goebel <h.goebel@crazy-compilers.com>

113
ChangeLog
View File

@ -1,9 +1,32 @@
2004-10-25 Gernot Hillier <gernot@hillier.de>
* src/capisuite-py/config.py.in: minor documentation changes
* src/capisuite-py/__init__.py: Likewise.
* src/capisuite-py/config.py.in (class NoGlobalSectionError): moved
to exceptions.py
* src/capisuite-py/exceptions.py: Likewise.
2004-09-04 Gernot Hillier <gernot@hillier.de>
* scripts/cs_helpers.pyin (__sendmail, __call, sff2tif, cff2ps,
la2wav, sendMIMEMail): fix several bugs, answering machine now seems
to work basically
* scripts/answering_machine.confin: update description of parameter
voice_email to new behaviour (empty string not allowed any more)
* NEWS: Likewise.
2004-07-15 Gernot Hillier <gernot@hillier.de>
* configure.in: support new parameter in ALERT_REQ of newer capi4linux
versions, thx to Steffen Barszus for reporting and the fix
* src/backend/capi.cpp (Capi::alert_req): Likewise.
* acinclude.m4 (CS_TEST_CAPI4LINUX): Likewise.
2004-06-12 Gernot Hillier <gernot@hillier.de>
* scripts/incoming.py (voiceIncoming): create file in received queue
only if necessary (i.e. as late as possible)
* src/capisuite-py/fileutils.py (_releaseLock): ignore insignificant
error resulting from race condition
* src/capisuite-py/voice.py (createReceivedJob): fix argument passing
* scripts/cs_helpers.pyin (sendMIMEMail): stringify exception instance
2004-06-10 Gernot Hillier <gernot@hillier.de>
* src/backend/connection.cpp (info_ind_called_party_nr): prevent
CapiSuite from crashing when a too high DDIBaseLength was configured,
@ -17,9 +40,33 @@
to avoid using phony targets in man page creation
* docs/manual.docbook.in: see above.
2004-05-10 Gernot Hillier <gernot@hillier.de>
* src/capisuite-py/core.py (Call.log,Call.__init__,Call.reject,
Call.disconnect): fix minor bugs in function calls, import some
additional symbols from _capisuite to our namespace, rename call_from
and call_to attributes of class Call to from_nr and to_nr, don't set
_handle to None after disconnect/reject
* scripts/incoming.py (callIncoming,faxIncoming,voiceIncoming): rename
call_from and call_to attributes of class Call to from_nr and to_nr;
use them everywhere instead of old global variables
* src/application/capisuitemodule.cpp (convertConnRef,convertCapiRef):
improve misleading error message
2004-04-18 Gernot Hillier <gernot@hillier.de>
* scripts/capisuitefax.in: allow * and # in destination numbers
2004-04-09 Gernot Hillier <gernot@hillier.de>
* src/capisuite-py/core.py (Capi.call_voice,Capi.call_faxG3): fix
some namespace problems and typos
2004-04-04 Gernot Hillier <gernot@hillier.de>
* configure.in: add support to install new capisuite Python module
* src/Makefile.am: Likewise
* src/capisuite-py/Makefile.am: Likewise (new file)
* src/capisuite-py/core.py (class Call): new method Call.log
* scripts/incoming.py (callIncoming,faxIncoming,voiceIncoming): use
Call.log instead of core.log where appropriate
2004-03-24 Gernot Hillier <gernot@hillier.de>
* docs/Makefile.am: change pathes to Docbook stylesheets to reflect
changes in SUSE 9.1
@ -31,16 +78,72 @@
2004-03-23 Gernot Hillier <gernot@hillier.de>
* docs/manual-de.docbook (capicodes_protocol): fix small typo
2004-03-21 Hartmut Goebel <h.goebel@crazy-compilers.com>
* scripts/fax.confin: Removed spaces from section names "Mail ...".
* scripts/answering_machine.confin: Likewise.
* scripts/incoming.py: Likewise.
* scripts/idle.py: Likewise.
* rc.capisuite.in: Likewise within a comment.
* scripts/incoming.py: Removed some outdated comments.
2004-03-20 Gernot Hillier <gernot@hillier.de>
* src/application/pythonscript.h: extend prefix() so that it can create
a short prefix, too; use short prefix for Python traceback (fixes bug
* src/application/pythonscript.h (prefix): add support for short
logging prefixes; use them for Python tracebacks (fixes bug
#63, reported anonymously)
* src/application/pythonscript.cpp: Likewise.
* src/application/pythonscript.cpp (prefix,run): Likewise.
* scripts/incoming.py: Fix typo.
2004-03-19 Gernot Hillier <gernot@hillier.de>
* scripts/idle.py: Remove old CVS history
* scripts/incoming.py: Likewise.
* scripts/incoming.py: changed some comments, moved connection
accept code to voiceIncoming
2004-03-14 Hartmut Goebel <h.goebel@crazy-compilers.com>
* scripts/incoming.py: Use capisuite.core instead of _capisuite.
* src/capisuite-py/fax.py: Removed faxInfo2dict.
* src/capisuite-py/core.py: Added OO layer for _capisuite
functions and classes Capi and Call.
* src/capisuite-py/core.py: New class FaxInfo.
2004-03-09 Hartmut Goebel <h.goebel@crazy-compilers.com>
* src/application/idlescript.cpp: Reduced delay until
* SConstruct: new file for building with SCons build-system.
* SConscript: Likewise.
* SConscript-Config: Likewise.
* SConscript-Options: Likewise.
* docs/SConscript: Likewise.
* scripts/SConscript: Likewise.
* scripts/waves/SConscript: Likewise.
* src/SConscript: Likewise.
* src/application/SConscript: Likewise.
* src/backend/SConscript: Likewise.
* src/modules/SConscript: Likewise.
* src/application/capisuitemodule.cpp (capisuitemodule_init): Renamed
built-in module to '_capisuite'
* src/application/idlescript.cpp (run): Reduced delay until
idlescript.py is called the first time after startup.
* src/capisuite-py: Added python library modules.
* src/capisuite-py/SConscript: Likewise.
* src/capisuite-py/__init__.py: Likewise.
* src/capisuite-py/config.py.in: Likewise.
* src/capisuite-py/consts.py: Likewise.
* src/capisuite-py/core.py: Likewise.
* src/capisuite-py/exceptions.py: Likewise.
* src/capisuite-py/fax.py: Likewise.
* src/capisuite-py/fileutils.py: Likewise.
* src/capisuite-py/pychecker.rc: Likewise.
* src/capisuite-py/voice.py: Likewise.
* scripts/fax.confin: Added sections 'Mail Fax|Voice ...'.
* scripts/answering_machine.confin: Likewise.
* rc.capisuite.in: Added 'todo' note.
* scripts/capisuite-checkconfig: new file (thx to
carsten@capimin.cbclass.net for the idea and a first version).
* scripts/cs_helper.pyin: Changed to use new python library.
* scripts/capisuitefax.in: Reworked to use new python library.
* scripts/idle.py: Likewise.
* scripts/incoming.py: Likewise.
* scripts/remote-connect.py: Likewise.
* ChangeLog, TODO, AUTHORS: updated.
2004-03-07 Gernot Hillier <gernot@hillier.de>
* docs/manual.docbook: update links to new web site structure
@ -69,7 +172,7 @@
confused the config file parser when not deleted by the user
* scripts/fax.confin: Likewise.
2004-02-20 Hartmut Goebel <h.goeben@goebel-consult.de>
2004-02-20 Hartmut Goebel <h.goebel@crazy-compilers.com>
* docs/Doxyfile.in: modified variable replacement for possible
change of build system
* docs/Makefile.am: Likewise.

View File

@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.8.3 from Makefile.am.
# Makefile.in generated by automake 1.9.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@ -38,7 +38,7 @@ subdir = .
DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in $(srcdir)/config.h.in \
$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
TODO depcomp install-sh missing mkinstalldirs
TODO depcomp install-sh missing mkinstalldirs py-compile
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
$(top_srcdir)/configure.in
@ -57,6 +57,12 @@ RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
install-recursive installcheck-recursive installdirs-recursive \
pdf-recursive ps-recursive uninstall-info-recursive \
uninstall-recursive
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
am__installdirs = "$(DESTDIR)$(docdir)"
docDATA_INSTALL = $(INSTALL_DATA)
DATA = $(doc_DATA)
@ -136,6 +142,8 @@ am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
datadir = @datadir@
@ -235,7 +243,7 @@ install-docDATA: $(doc_DATA)
test -z "$(docdir)" || $(mkdir_p) "$(DESTDIR)$(docdir)"
@list='$(doc_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " $(docDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(docdir)/$$f'"; \
$(docDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(docdir)/$$f"; \
done
@ -243,7 +251,7 @@ install-docDATA: $(doc_DATA)
uninstall-docDATA:
@$(NORMAL_UNINSTALL)
@list='$(doc_DATA)'; for p in $$list; do \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(docdir)/$$f'"; \
rm -f "$(DESTDIR)$(docdir)/$$f"; \
done
@ -321,14 +329,16 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
if (etags --etags-include --version) >/dev/null 2>&1; then \
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
include_option=--etags-include; \
empty_fix=.; \
else \
include_option=--include; \
empty_fix=; \
fi; \
list='$(SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test -f $$subdir/TAGS && \
test ! -f $$subdir/TAGS || \
tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
fi; \
done; \
@ -338,9 +348,11 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$tags$$unique" \
|| $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique; \
fi
ctags: CTAGS
CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
@ -393,15 +405,17 @@ distdir: $(DISTFILES)
|| exit 1; \
fi; \
done
list='$(SUBDIRS)'; for subdir in $$list; do \
list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test -d "$(distdir)/$$subdir" \
|| mkdir "$(distdir)/$$subdir" \
|| $(mkdir_p) "$(distdir)/$$subdir" \
|| exit 1; \
distdir=`$(am__cd) $(distdir) && pwd`; \
top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
(cd $$subdir && \
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="../$(top_distdir)" \
distdir="../$(distdir)/$$subdir" \
top_distdir="$$top_distdir" \
distdir="$$distdir/$$subdir" \
distdir) \
|| exit 1; \
fi; \
@ -412,15 +426,15 @@ distdir: $(DISTFILES)
! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
|| chmod -R a+r $(distdir)
dist-gzip: distdir
$(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
$(am__remove_distdir)
dist-bzip2: distdir
$(AMTAR) chof - $(distdir) | bzip2 -9 -c >$(distdir).tar.bz2
tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
$(am__remove_distdir)
dist-tarZ: distdir
$(AMTAR) chof - $(distdir) | compress -c >$(distdir).tar.Z
tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
$(am__remove_distdir)
dist-shar: distdir
@ -433,7 +447,7 @@ dist-zip: distdir
$(am__remove_distdir)
dist dist-all: distdir
$(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
$(am__remove_distdir)
# This target untars the dist file and tries a VPATH configuration. Then
@ -442,13 +456,13 @@ dist dist-all: distdir
distcheck: dist
case '$(DIST_ARCHIVES)' in \
*.tar.gz*) \
GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(AMTAR) xf - ;;\
GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
*.tar.bz2*) \
bunzip2 -c $(distdir).tar.bz2 | $(AMTAR) xf - ;;\
bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
*.tar.Z*) \
uncompress -c $(distdir).tar.Z | $(AMTAR) xf - ;;\
uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
*.shar.gz*) \
GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | unshar ;;\
GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
*.zip*) \
unzip $(distdir).zip ;;\
esac
@ -530,7 +544,7 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f $(CONFIG_CLEAN_FILES)
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"

25
NEWS
View File

@ -1,6 +1,11 @@
0.5 (CVS nn):
=============
* scripts: removed the special support for giving an empty voice_email/
fax_email parameter. This is NOT allowed any more and will result in
an error when sending the mail. Simply don't set these parameters
any more if you don't need them!
* core: use shorter, more readable format for Python traceback logging
* documentation: 5 man pages are now also created: capisuite(8),
@ -13,6 +18,22 @@
* scripts: capisuitefax now also accepts PDF files (thx to Jochen Meyer
and Eckhard Rüggeberg for reporting)
* scripts: capisuitefax now submits a single job if several
files are given (implemented by Hartmut Goebel, fixes bug #38)
* scripts: New script capisuite-checkconfig for checking the
configuration (thx to carsten@capimin.cbclass.net for the
idea and a first version, improved by Hartmut Goebel).
* core: major refactoring of the Python based interface has
been done. Now a complete new Python module library
implements a lot of useful functions and offers handy
abstractions. (planned and implemented by Hartmut Goebel)
* scripts: the mails sent can now be configured in the special sections
MailFaxReceived, MailFaxSent, MailFaxFailed and MailVoiceReceived in
answering_machine.conf and fax.conf (implemented by Hartmut Goebel)
* core: fixed a bug which could lead to a crash when some unexpected
CAPI messages arrived (thx to Karsten Keil for analyzing)
@ -42,8 +63,8 @@
should only contain important changes from a user's point of view, while
ChangeLog records all changes in detail.
0.4.4 (CVS tag NN):
===================
0.4.4
=====
* Makefiles: fixed "make" to not trying to create directories any more -
this prevented normal users to call "make" sometimes (thx to Steffen

17
SConscript Normal file
View File

@ -0,0 +1,17 @@
# -*- python -*-
# build top level files
# this is a SConscript, too, to support really build-dirs
Import('env')
cronfile = env.FileSubst('capisuite.cron', 'capisuite.cronin')
rcfile = env.FileSubst('rc.capisuite', 'rc.capisuite.in')
env.AddPostAction([cronfile, rcfile], 'chmod gu+x $TARGET')
Alias('install',
env.Install('$docdir', Split('COPYING NEWS README')),
env.Install('$sysconfdir/init.d/capisuite', rcfile),
env.InstallAs('$sysconfdir/cron.daily/capisuite', cronfile),
env.InstallAs('$pkgsysconfdir/cronjob.conf','cronjob.conf'),
)

192
SConscript-Config Normal file
View File

@ -0,0 +1,192 @@
# -*- python -*-
"""
Check the build environment for CapiSuite
(c) Copyright 2004 by Hartmut Goebel <h.goebel@crazy-compilers.com>
"""
Import(['env'])
headerfilename = 'config.h'
havedict = {}
open(headerfilename, 'w') # clear file contents
###--- hack SCons.SConftest ---###
import SCons.Conftest, string
from types import IntType
def _Have(context, key, have):
"""
Slightly modified version of SCons.Conftest._Have which uses
global havedict and headerfilename instead of context.*. This is
necessary until SCons support a way to actually use this feature.
In addition this writes '#define ... 1'.
"""
key_up = string.upper(key)
key_up = string.replace(key_up, ':', '_')
key_up = string.replace(key_up, '.', '_')
key_up = string.replace(key_up, '/', '_')
key_up = string.replace(key_up, ' ', '_')
havedict[key_up] = have
if headerfilename:
f = open(headerfilename, "a")
if have == 1:
f.write("#define %s 1\n" % key_up)
elif have == 0:
f.write("/* #undef %s */\n" % key_up)
elif type(have) == IntType:
f.write("#define %s %d\n" % (key_up, have))
else:
f.write('#define %s "%s"\n' % (key_up, str(have)))
f.close()
# need to monkey-patch it into SCons.Conftest :-(
SCons.Conftest._Have = _Have
_Have(None, 'PACKAGE', env['PACKAGE'])
_Have(None, 'VERSION', env['VERSION'])
## these were defined by auto-rools but are unused:
## #define PACKAGE_BUGREPORT ""
## #define PACKAGE_NAME ""
## #define PACKAGE_STRING ""
## #define PACKAGE_TARNAME ""
## #define PACKAGE_VERSION ""
###--- autoconf-like checks ---###
### these tests are build like the corresponding autoconf tests
def CheckHeadersStdC(context):
context.Message("Checking for ANSI C header files ... ")
text = """
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <float.h>
\n"""
# Some tests for SunOS 4.x, ISC 2.0.2 and Irix-4.0.5 skipped here,
# since CapiSuite requires Linux anway.
ret = context.CompileProg(text, '.cpp')
SCons.Conftest._YesNoResult(context, ret, "STDC_HEADERS", text)
context.did_show_result = 1
return ret
def CheckHeaderTime(context):
context.Display("Checking whether time.h and sys/time.h may both be "
"included ... ")
text = """
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
int main() {
if ((struct tm *) 0) return 0;
return 0;
}
\n"""
ret = context.CompileProg(text, '.cpp')
SCons.Conftest._YesNoResult(context, ret, "TIME_WITH_SYS_TIME", text)
context.did_show_result = 1
return ret
###--- custom checks for CapiSuite ---###
def CheckStringClear(context):
context.Display('Checking for string::clear method ... ')
text = """
#include <string>
int main() {
std::string a; a.clear();
}
\n"""
ret = context.CompileProg(text, '.cpp')
SCons.Conftest._YesNoResult(context, ret, "HAVE_STRING_CLEAR", text)
context.did_show_result = 1
return ret
conf = Configure(env,
custom_tests = {'CheckHeadersStdC': CheckHeadersStdC,
'CheckHeaderTime': CheckHeaderTime,
'CheckStringClear': CheckStringClear,
},
conf_dir = '.sconf_temp', # no '#' to build in build-dir
log_file = 'config.log' # no '#' to build in build-dir
)
conf.CheckHeadersStdC()
missing = 0
for h in Split("sys/types.h sys/stat.h stdlib.h string.h "
"memory.h strings.h inttypes.h stdint.h unistd.h"):
missing += not conf.CheckHeader(h, language='C++')
if missing:
print 'Required headers missing - aborting.'
Exit(5)
conf.CheckFunc('gettimeofday', language='C++')
conf.CheckHeader('sys/time.h', language='C++')
conf.CheckHeaderTime()
###--- CapiSuite specials ---###
# new gcc3 feature: we can #include<ostream> instead of ostream.h
conf.CheckHeader('ostream', language='C++')
conf.CheckStringClear() # checking for string::clear method
###--- libs required by CapiSuite ---###
# checking for capi20_register in -lcapi20... yes
if not conf.CheckLib('capi20', 'capi20_register', language='C++'):
print 'libcapi not found - aborting'
Exit(5)
# checking for pthread_create in -lpthread... yes
if not conf.CheckLib('pthread', 'pthread_create', language='C++'):
print 'lipthread not found - aborting'
Exit(5)
# todo
# Which(doxygen)
# Which(pychecker)
conf.Finish()
"""
These are the checks 'configure' does
Legende:
- not neccessary
+ Scons build-in
? unsure
~ implemented in SConscript/SConstruct
-checking for a BSD-compatible install... /usr//bin/install -c
-checking whether build environment is sane... yes
-checking for gawk... gawk
-checking whether make sets $(MAKE)... yes
+checking for gcc... gcc
+checking for C compiler default output... a.out
+checking whether the C compiler works... yes
?checking whether we are cross compiling... no
+checking for suffix of executables...
+checking for suffix of object files... o
?checking whether we are using the GNU C compiler... yes
?checking whether gcc accepts -g... yes
?checking for gcc option to accept ANSI C... none needed
-checking for style of include used by make... GNU
-checking dependency style of gcc... gcc3
+checking for g++... g++
checking whether we are using the GNU C++ compiler... yes
checking whether g++ accepts -g... yes
-checking dependency style of g++... gcc3
-checking for a BSD-compatible install... /usr//bin/install -c
+checking for ranlib... ranlib
-checking whether make sets $(MAKE)... (cached) yes
+checking how to run the C++ preprocessor... g++ -E
-checking for egrep... grep -E
"""

43
SConscript-Options Normal file
View File

@ -0,0 +1,43 @@
# -*- python -*-
"""
Options for building CapiSuite
(c) Copyright 2004 by Hartmut Goebel <h.goebel@crazy-compilers.com>
"""
# default prefix
prefix = '/usr/local'
oldincludedir = '/usr/include'
opts = Options('options.cache', ARGUMENTS)
opts.AddOptions(
# Installation directories:
('prefix', 'install architecture-independent files in prefix', prefix),
('execprefix','install architecture-dependent files in execprefix','$prefix'),
# Fine tuning of the installation directories:
('bindir', 'user executables', '$execprefix/bin'),
('sbindir', 'system admin executables', '$execprefix/sbin'),
('libexecdir', 'program executables', '$execprefix/libexec'),
('datadir', 'read-only architecture-independent data','$prefix/share'),
('sysconfdir', 'read-only single-machine data', '$prefix/etc',),
('sharedstatedir','modifiable architecture-independent data','$prefix/com',),
('localstatedir', 'modifiable single-machine data', '$prefix/var'),
('libdir', 'object code libraries', '$execprefix/lib'),
('includedir', 'C header files', '$prefix/include'),
('oldincludedir', 'C header files for non-gcc', oldincludedir),
('infodir', 'info documentation', '$prefix/info'),
('mandir', 'man documentation', '$prefix/man'),
# install into another filesystem base
('INSTALL_BASE', 'base dir for installation (userfull for RPMs)',
'#/dist'),
# this is a capisuite-special
('docdir', 'other documentation', '$datadir/doc/capisuite'),
)
Import(['env', '__targets__'])
opts.Update(env)
Help(opts.GenerateHelpText(env) + __targets__)
opts.Save('options.cache', env)

246
SConstruct Normal file
View File

@ -0,0 +1,246 @@
# -*- python -*-
"""
Main SCons build script for CapiSuite
(c) Copyright 2004 by Hartmut Goebel <h.goebel@crazy-compilers.com>
CapiSuite is (c) Copyright by Gernot Hiller <gernot@hiller.de>
Use 'scons --help' for a list of available options.
Options have only to be given once, since they are cached in a file
'options.cache'. To change an option, simply pass it again with a
different value.
Example:
scons prefix=/ # build for prefix=/
scons # prefix=/ is taken from 'options.cache'
scons prefix=/usr/local # build for prefix=/usr/local
# since this is the default, the entry in options.cache
# will be renmoved
scons # default prefix is used
"""
__targets__ = """
Additional targets:
configure : build the 'configure' script
(this is automatically done if 'config.h' is missing)
pycheck : check Python sources with PyChecker (not yet implemented)
install : install all files
install-pylib : install only the python library
install-scripts : install only the python scripts
install-exec : install only the executables
For all install-targets base may be set with INSTALL_BASE=...
"""
# File-Content Substitution will (hopefully ) be part of SCons 0.95
def _file_subst(target, source, env):
import os, re
import SCons.Util
def _substitute(matchobj, env=env):
sym = matchobj.group(1)
try:
return env.subst(str(env[sym]))
except: # TypeError: # sym not a string
print 'Not substituting', sym
return matchobj.group(0) # matched
delim = re.escape(env.get('SUBST_DELIM', '@'))
subst_pattern = re.compile('%s(.*?)%s' % (delim, delim))
for t, s in zip(target, source):
t = str(t)
s = s.rstr()
text = open(s, 'rb').read()
text = subst_pattern.sub(_substitute, text)
open(t, 'wb').write(text)
os.chmod(t, os.stat(s)[0])
return None
def _fs_strfunc(target, source, env):
return "generating '%s' from '%s'" % (target[0], source[0])
_fs_builder = Builder(action = Action(_file_subst, strfunction = _fs_strfunc))
import sys, os, os.path
import SCons.Util
import SCons.Node.FS
EnsurePythonVersion(2,2) # capisuite requires this
#EnsureSConsVersion(0,94)
build_dir = Dir('#/build')
class InstallableEnv(Environment):
def Install(self, dir, source):
"""Install specified files in the given directory."""
if self.has_key('INSTALL_BASE'):
def _Dir(name):
return SCons.Node.FS.Dir(name, parent, self.fs)
parent = self['INSTALL_BASE']
dir = self.arg2nodes(dir, _Dir)
return Environment.Install(self, dir, source)
def InstallAs(self, target, source):
"""Install sources as targets."""
def _File(name):
return SCons.Node.FS.File(name, dir, self.fs)
if self.has_key('INSTALL_BASE'):
dir = self['INSTALL_BASE']
targets = self.arg2nodes(target, _File)
return Environment.InstallAs(self, target, source)
env = InstallableEnv()
env.Append(
BUILDERS={'FileSubst' : _fs_builder},
PACKAGE = 'capisuite',
VERSION = '0.5.cvs',
srcdir = build_dir,
pkgdatadir = '${datadir}/${PACKAGE}',
pkglibdir = '${libdir}/${PACKAGE}',
pkgincludedir = '${includedir}/${PACKAGE}',
pkgbindir = '${bindir}',
pkgsbindir = '${sbindir}',
pkgsysconfdir = '${sysconfdir}/${PACKAGE}',
#pkglibdir = '${prefix}/lib',
spooldir = '${localstatedir}/spool/${PACKAGE}',
docdir = '${pkgdatadir}/doc/${PACKAGE}',
)
env.SConscript('SConscript-Options', exports=['env', '__targets__'])
if env.has_key('INSTALL_BASE'):
env.Replace(INSTALL_BASE=env.Dir('$INSTALL_BASE'))
Export('env')
env.BuildDir(build_dir=build_dir, src_dir='.')
###---####---###---####---###---####---###---####---###---####---###---###
# call configure if required
# if config.h does not exist, build it using Scons' conftest
if not os.path.exists(str(File('config.h', build_dir))) \
or 'configure' in COMMAND_LINE_TARGETS:
env.SConscript('SConscript-Config', build_dir=build_dir)
###---####---###---####---###---####---###---####---###---####---###---###
def GetPythonModuleSetup(env):
"""
Get configuration for linking with the Python library.
"""
import distutils.sysconfig
from distutils.sysconfig import get_python_lib, get_config_vars, \
get_config_var
env.Append(
python_version = distutils.sysconfig.get_python_version(),
python_prefix = distutils.sysconfig.PREFIX,
python_execprefix = distutils.sysconfig.EXEC_PREFIX,
python_libdir = get_python_lib(plat_specific=1, standard_lib=1),
python_moduledir = get_python_lib(plat_specific=0, standard_lib=0),
python_moduleexecdir = get_python_lib(plat_specific=1, standard_lib=0),
python_includespec = get_config_vars('INCLUDEPY', 'CONFINCLUDEPY'),
python_linkforshared = get_config_var('LINKFORSHARED'),
pkgpython_moduledir = '${python_moduledir}/${PACKAGE}',
pkgpython_moduleexecdir = '${python_moduleexecdir}/${PACKAGE}',
LINKFLAGS = ['${python_linkforshared}'],
LIBS = ['python${python_version}'],
)
env.Append(
CPPPATH = env['python_includespec'],
PYTHON = sys.executable,
)
def GetPythonEmbeddedSetup(env):
"""
Get configuration for linking an embedded Python application.
"""
# Base idea on which variable to check are from the INN 2.3.1 package
import distutils.sysconfig
print 'Checking how to link an embedded Python application:',
libvars = Split("LIBS LIBC LIBM LOCALMODLIBS BASEMODLIBS")
python_libspec = []
for libvar in distutils.sysconfig.get_config_vars(*libvars):
libvar = libvar.split()
for lib in libvar:
# sanity check
if not lib.startswith('-l'):
print "Error in Python Makefile: shared lib spec does ", \
"not start with '-l':", lib
python_libspec.append(lib[2:])
env.Append(python_libspec = python_libspec,
python_configdir = distutils.sysconfig.get_config_var('LIBPL'),
# preferable these should be '$python..', but SCons
# doe not yet support this (SCons 0.94)
LIBS = python_libspec,
LIBPATH = ['${python_configdir}'],
)
print ' '.join(python_libspec)
#print '>>>', env['LIBS']
GetPythonModuleSetup(env)
GetPythonEmbeddedSetup(env)
env.Append(
CCFLAGS = Split('-g -O2'),
LIBS = Split('pthread capi20'),
CPPDEFINES={'LOCALSTATEDIR': r'\"${localstatedir}\"',
'PKGDATADIR' : r'\"${pkgdatadir}\"',
'PKGSYSCONFDIR': r'\"${pkgsysconfdir}\"',
'PKGLIBDIR' : r'\"${pkglibdir}\"',
},
)
## # build debug?
## if ARGUMENTS.get('debug', 0):
## env.Append(CXXFLAGS = ['-g', '-DDEBUG'])
## else:
## env.Append(CXXFLAGS = ['-O2'])
##
## if env['CXX'] in ('g++', 'c++'):
## env.Append(CXXFLAGS = ['-Wall', '-Wno-non-virtual-dtor'])
###---####---###---####---###---####---###---####---###---####---###---###
# snippet for unittest
#mytest = env.program(...)
#Alias( 'unittest', mytest )
#Alias( 'all', 'unittest' )
#unittetsInstall = env.Install(...)
#Alias('unittest', unitteststInstall)
# now build the subdirectories' stuff
env.SConscript(dirs=[Dir('.', build_dir),
Dir('src', build_dir),
Dir('scripts', build_dir),
Dir('scripts/waves', build_dir),
Dir('docs', build_dir),
])
#env.SourceCode('.',
# env.CVS('pserver:anonymous@cvs.capisuite.berlios.de:/cvsroot/capisuite',
# 'capisuite'))
"""
make DESTDIR=$RPM_BUILD_ROOT install
mkdir -p $RPM_BUILD_ROOT/usr/sbin
ln -sf ../../etc/init.d/capisuite $RPM_BUILD_ROOT/usr/sbin/rccapisuite
"""
#EXTRA_DIST = rc.capisuite.in capisuite.cronin cronjob.conf
#install-data-local:
# mkdir -p $(DESTDIR)$(localstatedir)/log
# $(mkinstalldirs) $(DESTDIR)$(spooldir)/sendq
# $(mkinstalldirs) $(DESTDIR)$(spooldir)/done
# $(mkinstalldirs) $(DESTDIR)$(spooldir)/failed
# $(mkinstalldirs) $(DESTDIR)$(spooldir)/users

15
TODO
View File

@ -1,4 +1,18 @@
Important for 0.5.0/capisuite-py:
* Check and fix all 'todo:'s in the python library and scripts.
* Update documentation to include
- capisuite-checkconfig
- the python library modules.
- 'Mail Fax|Voice ...' sections in config files
* Add update instructions for 0.5.0/capisuite-py
- 'Mail Fax|Voice ...' sections in config files
- new modulename '_capisuite' (but use capisuite.core instead)
* Add option '--is-configured' to capisuite-checkconfig to be used
by rc-file.
* Add 'scons-local' to distribution.
NICE:
- more checks/options for capisuite-checkconfig
- ?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
@ -7,4 +21,3 @@ FUTURE PLANS:
- setuid away from root (problem: chown of recorded file to user)
- test-implement the whole application part in Python
- rewrite capisuitefax and idle.py to use named socket communication

92
configure vendored
View File

@ -309,7 +309,7 @@ ac_includes_default="\
# include <unistd.h>
#endif"
ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO AMTAR install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE RANLIB ac_ct_RANLIB doxygen CXXCPP EGREP docdir PYTHON PYTHON_VERSION PYTHON_PREFIX PYTHON_EXEC_PREFIX PYTHON_PLATFORM pythondir pkgpythondir pyexecdir pkgpyexecdir python_version python_prefix python_execprefix python_configdir python_moduledir python_moduleexecdir python_includespec python_linkforshared LIBOBJS LTLIBOBJS'
ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE RANLIB ac_ct_RANLIB doxygen CXXCPP EGREP docdir PYTHON PYTHON_VERSION PYTHON_PREFIX PYTHON_EXEC_PREFIX PYTHON_PLATFORM pythondir pkgpythondir pyexecdir pkgpyexecdir python_version python_prefix python_execprefix python_configdir python_moduledir python_moduleexecdir python_includespec python_linkforshared LIBOBJS LTLIBOBJS'
ac_subst_files=''
# Initialize some variables set by options.
@ -1311,7 +1311,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
am__api_version="1.8"
am__api_version="1.9"
ac_aux_dir=
for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
if test -f $ac_dir/install-sh; then
@ -1488,13 +1488,21 @@ echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
fi
if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
# Keeping the `.' argument allows $(mkdir_p) to be used without
# argument. Indeed, we sometimes output rules like
# We used to keeping the `.' as first argument, in order to
# allow $(mkdir_p) to be used without argument. As in
# $(mkdir_p) $(somedir)
# where $(somedir) is conditionally defined.
# (`test -n '$(somedir)' && $(mkdir_p) $(somedir)' is a more
# expensive solution, as it forces Make to start a sub-shell.)
mkdir_p='mkdir -p -- .'
# where $(somedir) is conditionally defined. However this is wrong
# for two reasons:
# 1. if the package is installed by a user who cannot write `.'
# make install will fail,
# 2. the above comment should most certainly read
# $(mkdir_p) $(DESTDIR)$(somedir)
# so it does not work when $(somedir) is undefined and
# $(DESTDIR) is not.
# To support the latter case, we have to write
# test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
# so the `.' trick is pointless.
mkdir_p='mkdir -p --'
else
# On NextStep and OpenStep, the `mkdir' command does not
# recognize any option. It will interpret all options as
@ -1638,9 +1646,6 @@ AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
AMTAR=${AMTAR-"${am_missing_run}tar"}
install_sh=${install_sh-"$am_aux_dir/install-sh"}
# Installed binaries are usually stripped using `strip' when the user
@ -1733,6 +1738,13 @@ INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
# We need awk for the "check" target. The system "awk" is bad on
# some platforms.
# Always define AMTAR for backward compatibility.
AMTAR=${AMTAR-"${am_missing_run}tar"}
am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
@ -2811,9 +2823,14 @@ else
grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
# icc doesn't choke on unknown options, it will just issue warnings
# (even with -Werror). So we grep stderr for any message
# that says an option was ignored.
if grep 'ignoring option' conftest.err >/dev/null 2>&1; then :; else
# or remarks (even with -Werror). So we grep stderr for any message
# that says an option was ignored or not supported.
# When given -MP, icc 7.0 and 7.1 complain thusly:
# icc: Command line warning: ignoring option '-M'; no argument required
# The diagnosis changed in icc 8.0:
# icc: Command line remark: option '-MP' not supported
if (grep 'ignoring option' conftest.err ||
grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
am_cv_CC_dependencies_compiler_type=$depmode
break
fi
@ -3270,9 +3287,14 @@ else
grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
# icc doesn't choke on unknown options, it will just issue warnings
# (even with -Werror). So we grep stderr for any message
# that says an option was ignored.
if grep 'ignoring option' conftest.err >/dev/null 2>&1; then :; else
# or remarks (even with -Werror). So we grep stderr for any message
# that says an option was ignored or not supported.
# When given -MP, icc 7.0 and 7.1 complain thusly:
# icc: Command line warning: ignoring option '-M'; no argument required
# The diagnosis changed in icc 8.0:
# icc: Command line remark: option '-MP' not supported
if (grep 'ignoring option' conftest.err ||
grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
am_cv_CXX_dependencies_compiler_type=$depmode
break
fi
@ -4692,6 +4714,7 @@ echo "$as_me: error: too old" >&2;}
{ (exit 1); exit 1; }; }
fi
am_display_PYTHON=$PYTHON
else
# Otherwise, try each interpreter until we find one that satisfies
# VERSION.
@ -4902,7 +4925,7 @@ echo "${ECHO_T}${python_libspec}" >&6
CPPFLAGS='-DLOCALSTATEDIR=\"$(localstatedir)\" -DPKGDATADIR=\"$(pkgdatadir)\" -DPKGSYSCONFDIR=\"$(sysconfdir)/capisuite\" -DPKGLIBDIR=\"$(pkglibdir)\" $(python_includespec)'
ac_config_files="$ac_config_files Makefile src/Makefile src/backend/Makefile src/modules/Makefile src/application/Makefile scripts/Makefile scripts/waves/Makefile docs/Makefile"
ac_config_files="$ac_config_files Makefile src/Makefile src/backend/Makefile src/modules/Makefile src/application/Makefile src/capisuite-py/Makefile scripts/Makefile scripts/waves/Makefile docs/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
# tests run on this system so they can be shared between configure
@ -5464,6 +5487,7 @@ do
"src/backend/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/backend/Makefile" ;;
"src/modules/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/modules/Makefile" ;;
"src/application/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/application/Makefile" ;;
"src/capisuite-py/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/capisuite-py/Makefile" ;;
"scripts/Makefile" ) CONFIG_FILES="$CONFIG_FILES scripts/Makefile" ;;
"scripts/waves/Makefile" ) CONFIG_FILES="$CONFIG_FILES scripts/waves/Makefile" ;;
"docs/Makefile" ) CONFIG_FILES="$CONFIG_FILES docs/Makefile" ;;
@ -5565,7 +5589,6 @@ s,@AUTOCONF@,$AUTOCONF,;t t
s,@AUTOMAKE@,$AUTOMAKE,;t t
s,@AUTOHEADER@,$AUTOHEADER,;t t
s,@MAKEINFO@,$MAKEINFO,;t t
s,@AMTAR@,$AMTAR,;t t
s,@install_sh@,$install_sh,;t t
s,@STRIP@,$STRIP,;t t
s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t
@ -5574,6 +5597,9 @@ s,@mkdir_p@,$mkdir_p,;t t
s,@AWK@,$AWK,;t t
s,@SET_MAKE@,$SET_MAKE,;t t
s,@am__leading_dot@,$am__leading_dot,;t t
s,@AMTAR@,$AMTAR,;t t
s,@am__tar@,$am__tar,;t t
s,@am__untar@,$am__untar,;t t
s,@CC@,$CC,;t t
s,@CFLAGS@,$CFLAGS,;t t
s,@LDFLAGS@,$LDFLAGS,;t t
@ -6243,27 +6269,21 @@ echo X"$mf" |
else
continue
fi
grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue
# Extract the definition of DEP_FILES from the Makefile without
# running `make'.
# Extract the definition of DEPDIR, am__include, and am__quote
# from the Makefile without running `make'.
DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
test -z "$DEPDIR" && continue
am__include=`sed -n 's/^am__include = //p' < "$mf"`
test -z "am__include" && continue
am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
# When using ansi2knr, U may be empty or an underscore; expand it
U=`sed -n 's/^U = //p' < "$mf"`
test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR"
# We invoke sed twice because it is the simplest approach to
# changing $(DEPDIR) to its actual value in the expansion.
for file in `sed -n '
/^DEP_FILES = .*\\\\$/ {
s/^DEP_FILES = //
:loop
s/\\\\$//
p
n
/\\\\$/ b loop
p
}
/^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \
# Find all dependency output files, they are included files with
# $(DEPDIR) in their names. We invoke sed twice because it is the
# simplest approach to changing $(DEPDIR) to its actual value in the
# expansion.
for file in `sed -n "
s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
# Make sure the directory exists.
test -f "$dirpart/$file" && continue

View File

@ -26,4 +26,4 @@ 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)
AC_OUTPUT(Makefile src/Makefile src/backend/Makefile src/modules/Makefile src/application/Makefile src/capisuite-py/Makefile scripts/Makefile scripts/waves/Makefile docs/Makefile)

View File

@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.8.3 from Makefile.am.
# Makefile.in generated by automake 1.9.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@ -46,7 +46,8 @@ CONFIG_CLEAN_FILES =
SOURCES =
DIST_SOURCES =
man1dir = $(mandir)/man1
am__installdirs = "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"
am__installdirs = "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" \
"$(DESTDIR)$(man8dir)"
man5dir = $(mandir)/man5
man8dir = $(mandir)/man8
NROFF = nroff
@ -114,6 +115,8 @@ am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
datadir = @datadir@
@ -381,7 +384,7 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f $(CONFIG_CLEAN_FILES)
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@ -435,9 +438,9 @@ uninstall-am: uninstall-info-am uninstall-local uninstall-man
uninstall-man: uninstall-man1 uninstall-man5 uninstall-man8
.PHONY: all all-am check check-am clean clean-generic distclean \
distclean-generic distdir dvi dvi-am html html-am info info-am \
install install-am install-data install-data-am \
.PHONY: all all-am check check-am clean clean-generic dist-hook \
distclean distclean-generic distdir dvi dvi-am html html-am \
info info-am install install-am install-data install-data-am \
install-data-local install-exec install-exec-am install-info \
install-info-am install-man install-man1 install-man5 \
install-man8 install-strip installcheck installcheck-am \

66
docs/SConscript Normal file
View File

@ -0,0 +1,66 @@
# -*- python -*-
Import('env')
#EXTRA_DIST = Doxyfile.in mainpage.doxy manual.docbook manual.README
## dist-hook: manual-html manual-pdf reference-html
## mkdir $(distdir)/manual
## cp -r $(srcdir)/manual/* $(distdir)/manual/
## mkdir $(distdir)/reference
## cp $(srcdir)/reference/* $(distdir)/reference/
## cp manual.pdf $(distdir)/
import re
def patch_version(target, source, env):
"""
Change version contained in the <title> tag.
This is done in-place to avoid yet another .in file.
"""
text = source[0].get_contents()
text = re.sub(r'<title>CapiSuite [\w.]*</title>',
r'<title>CapiSuite %s</title>' % env['VERSION'],
text)
open(source[0].abspath, 'w').write(text)
env.Append(docbuilddir=Dir('.'))
manualdir = Dir('manual')
stylesheetdir = Dir('/usr/share/sgml/docbook/xsl-stylesheets')
#images = Install(Dir('images', manualdir), images)
# create HTML manual
manual_html = env.Command(File('index.html', manualdir),
'manual.docbook', [
patch_version,
['xmllint', '--noout', '--valid', '$SOURCE'],
['xsltproc', '-o', '${TARGET.dir}/',
File('xhtml/chunk.xsl', stylesheetdir), '$SOURCE']
])
#env.Depends(manual_html, images)
# copy missing images
env.AddPostAction(manual_html, [ \
['rm', '-fr', Dir('images', manualdir)],
['cp', '-r', Dir('images', stylesheetdir), Dir(manualdir)]
])
# create PDF manual
manual_pdf = env.Command('manual.pdf', 'manual.docbook',
'db2pdf -o ${TARGET.dir} $SOURCE'
)
# substitute version, capisuite_sources, srcdir
doxyfile = env.FileSubst('Doxyfile', 'Doxyfile.in')
ref_html = env.Command('reference/index.html', doxyfile, [ \
['doxygen', doxyfile]
])
Alias('install',
env.Install('$docdir', manual_pdf),
#env.Install('$docdir', 'manual'),
#env.Install('$docdir', 'reference'),
)

View File

@ -82,6 +82,8 @@ case "$1" in
# answering machine. Otherwise exit.
# IMPORTANT: Change this or comment it out if you want to use
# your own CapiSuite scripts.
# todo: change these tests since they are not longer valid due to
# [Mail...] sections. Consider using capisuite-checkconfig!
while read -r sec rest ; do
if [ "${sec:0:1}" = "[" -a "$sec" != "[GLOBAL]" ]; then
configured_fax=yes

View File

@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.8.3 from Makefile.am.
# Makefile.in generated by automake 1.9.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@ -46,7 +46,8 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(pkgsysconfdir)" "$(DESTDIR)$(python_moduledir)"
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibdir)" \
"$(DESTDIR)$(pkgsysconfdir)" "$(DESTDIR)$(python_moduledir)"
binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
SCRIPTS = $(bin_SCRIPTS)
SOURCES =
@ -57,6 +58,12 @@ RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
install-recursive installcheck-recursive installdirs-recursive \
pdf-recursive ps-recursive uninstall-info-recursive \
uninstall-recursive
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
dist_pkglibDATA_INSTALL = $(INSTALL_DATA)
pkgsysconfDATA_INSTALL = $(INSTALL_DATA)
python_moduleDATA_INSTALL = $(INSTALL_DATA)
@ -127,6 +134,8 @@ am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
datadir = @datadir@
@ -227,7 +236,7 @@ install-dist_pkglibDATA: $(dist_pkglib_DATA)
test -z "$(pkglibdir)" || $(mkdir_p) "$(DESTDIR)$(pkglibdir)"
@list='$(dist_pkglib_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " $(dist_pkglibDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(pkglibdir)/$$f'"; \
$(dist_pkglibDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(pkglibdir)/$$f"; \
done
@ -235,7 +244,7 @@ install-dist_pkglibDATA: $(dist_pkglib_DATA)
uninstall-dist_pkglibDATA:
@$(NORMAL_UNINSTALL)
@list='$(dist_pkglib_DATA)'; for p in $$list; do \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \
rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \
done
@ -244,7 +253,7 @@ install-pkgsysconfDATA: $(pkgsysconf_DATA)
test -z "$(pkgsysconfdir)" || $(mkdir_p) "$(DESTDIR)$(pkgsysconfdir)"
@list='$(pkgsysconf_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " $(pkgsysconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(pkgsysconfdir)/$$f'"; \
$(pkgsysconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(pkgsysconfdir)/$$f"; \
done
@ -252,7 +261,7 @@ install-pkgsysconfDATA: $(pkgsysconf_DATA)
uninstall-pkgsysconfDATA:
@$(NORMAL_UNINSTALL)
@list='$(pkgsysconf_DATA)'; for p in $$list; do \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(pkgsysconfdir)/$$f'"; \
rm -f "$(DESTDIR)$(pkgsysconfdir)/$$f"; \
done
@ -261,7 +270,7 @@ install-python_moduleDATA: $(python_module_DATA)
test -z "$(python_moduledir)" || $(mkdir_p) "$(DESTDIR)$(python_moduledir)"
@list='$(python_module_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " $(python_moduleDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(python_moduledir)/$$f'"; \
$(python_moduleDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(python_moduledir)/$$f"; \
done
@ -269,7 +278,7 @@ install-python_moduleDATA: $(python_module_DATA)
uninstall-python_moduleDATA:
@$(NORMAL_UNINSTALL)
@list='$(python_module_DATA)'; for p in $$list; do \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(python_moduledir)/$$f'"; \
rm -f "$(DESTDIR)$(python_moduledir)/$$f"; \
done
@ -347,14 +356,16 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
if (etags --etags-include --version) >/dev/null 2>&1; then \
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
include_option=--etags-include; \
empty_fix=.; \
else \
include_option=--include; \
empty_fix=; \
fi; \
list='$(SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test -f $$subdir/TAGS && \
test ! -f $$subdir/TAGS || \
tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
fi; \
done; \
@ -364,9 +375,11 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$tags$$unique" \
|| $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique; \
fi
ctags: CTAGS
CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
@ -417,15 +430,17 @@ distdir: $(DISTFILES)
|| exit 1; \
fi; \
done
list='$(SUBDIRS)'; for subdir in $$list; do \
list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test -d "$(distdir)/$$subdir" \
|| mkdir "$(distdir)/$$subdir" \
|| $(mkdir_p) "$(distdir)/$$subdir" \
|| exit 1; \
distdir=`$(am__cd) $(distdir) && pwd`; \
top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
(cd $$subdir && \
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="../$(top_distdir)" \
distdir="../$(distdir)/$$subdir" \
top_distdir="$$top_distdir" \
distdir="$$distdir/$$subdir" \
distdir) \
|| exit 1; \
fi; \
@ -457,7 +472,7 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f $(CONFIG_CLEAN_FILES)
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@ -522,15 +537,16 @@ uninstall-info: uninstall-info-recursive
distclean-recursive distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-binSCRIPTS \
install-data install-data-am install-dist_pkglibDATA \
install-exec install-exec-am install-info install-info-am \
install-man install-pkgsysconfDATA install-python_moduleDATA \
install-strip installcheck installcheck-am installdirs \
installdirs-am maintainer-clean maintainer-clean-generic \
maintainer-clean-recursive mostlyclean mostlyclean-generic \
mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \
uninstall uninstall-am uninstall-binSCRIPTS \
uninstall-dist_pkglibDATA uninstall-info-am \
uninstall-pkgsysconfDATA uninstall-python_moduleDATA
install-exec install-exec-am install-exec-hook install-info \
install-info-am install-man install-pkgsysconfDATA \
install-python_moduleDATA install-strip installcheck \
installcheck-am installdirs installdirs-am maintainer-clean \
maintainer-clean-generic maintainer-clean-recursive \
mostlyclean mostlyclean-generic mostlyclean-recursive pdf \
pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \
uninstall-binSCRIPTS uninstall-dist_pkglibDATA uninstall-hook \
uninstall-info-am uninstall-pkgsysconfDATA \
uninstall-python_moduleDATA
capisuitefax: capisuitefax.in

53
scripts/SConscript Normal file
View File

@ -0,0 +1,53 @@
# -*- python -*-
Import('env')
def py_compile(target, source, env):
"""compile python modules for .../python2.x/site-packages/"""
# Note: this differs from #/capisuite/SConscript.py_compile in 'dfile'
import py_compile, os.path
py_compile.compile(source[0].abspath,
#cfile=target[0].abspath,
dfile = os.path.join(env.subst('$python_moduledir'),
env.subst('$SOURCE.file')),
)
# these ar meant to be used by users
user_scripts = [env.FileSubst('capisuitefax', 'capisuitefax.in'),]
env.AddPostAction(user_scripts, 'chmod 755 $TARGETS')
# these are meant to be used by the admin
sbin_scripts = [File('capisuite-checkconfig'),]
#env.AddPostAction(sbin_scripts, 'chmod 755 $TARGETS')
# config files
configs = [
env.FileSubst('fax.conf', 'fax.confin'),
env.FileSubst('answering_machine.conf', 'answering_machine.confin')
]
# this is no longer needed
# todo: check cs_helper.py into cvs instead of cs_helper.pyin
env.FileSubst('cs_helpers.py', 'cs_helpers.pyin')
pymodules = []
for mod in Split('cs_helpers'):
pymodules.append(mod+'.py')
pymodules.append(env.Command(mod + '.pyc', mod+'.py', py_compile))
#--- install ---
install_pylib = env.Install('$python_moduledir', pymodules)
Alias('install-pylib',install_pylib)
for i in [env.Install('$pkgbindir', user_scripts),
env.Install('$pkgsbindir', sbin_scripts),
env.Install('$pkglibdir', Split('idle.py incoming.py'))]:
Alias('install-scripts', i)
Alias('install', i)
Alias('install',
env.Install('$python_moduledir', pymodules),
env.Install('$pkgsysconfdir', configs),
env.Install('$pkglibdir', 'README'),
)

View File

@ -85,6 +85,24 @@ record_silence_timeout="5"
# header field.
voice_email_from="capisuite daemon <root>"
###############################################################################
############################# Mail settings ###################################
###############################################################################
# defined for voice receive: call_from, call_to, date,
# msg_length, filename, hostname
[MailVoiceReceived]
subject = Received a voice call from %(call_from)s to %(call_to)s
text =
You got a voice call from %(call_from)s to %(call_to)s
Date: %(date)s
Length: %(msg_length)i seconds
See attached file.
The original file was saved to file://%(filename)s on host "%(hostname)s".
###############################################################################
############################# user settings ###################################
###############################################################################
@ -108,12 +126,12 @@ voice_email_from="capisuite daemon <root>"
# is necessary for example for the austrian "Global Call" where no number is
# signalled when the main MSN is called (sic).
#
# voice_email="<mailaddress1>,<mailaddress2>,..." (optional, defaults to empty string)
# voice_email="<mailaddress1>,<mailaddress2>,..." (optional)
#
# If given, this string indicates email-addresses where the received faxes
# and voice calls will be sent to. If it is empty, the recorded calls and
# and voice calls will be sent to. If it is not given, the recorded calls and
# faxes will be sent to the user on the current system. If you don't want to
# get emails, see the "action" option below
# get emails, see the "action" option below.
#
# pin="<PIN number>" (optional, defaults to empty)
#
@ -142,6 +160,5 @@ voice_email_from="capisuite daemon <root>"
#voice_action="MailAndSave"
#voice_delay="10"
#record_length="60"
#voice_email=""
#pin="99*45"

131
scripts/capisuite-checkconfig Executable file
View File

@ -0,0 +1,131 @@
#!/usr/bin/python
import sys, os, os.path
def error(*msgs):
for m in msgs:
print >>sys.stderr, m,
print >>sys.stderr
sys.exit(10)
try:
import capisuite.config
import capisuite.fileutils
from capisuite.consts import *
except Exception, e:
error("failed to import capisuite's python modules", str(e))
# Sompe helper functions, if you don't know what they do, just skip to
# the easy part
def test_nextfile(user, path, queue, prefix):
path = os.path.join(path, user, queue)
if not os.path.exists(path):
print "Directory", path, "does not exist"
print " ==> this is ok, if this queue has never been",
print "used before (e.g. never send a fax)"
print
return
file = os.path.join(path, prefix+"-nextnr")
if not os.path.exists(file):
print "File", file, "does not exist"
print " ==> this is ok, if this queue has never been",
print "used before (e.g. never send a fax)"
print
return
print "Trying to read file:", file
num = None
try:
num = capisuite.fileutils.readCounter(file, default=None)
except IOError, err:
print >>sys.stderr, "****Failed to read/parse the *nextnr file:", err
except ValueError:
print >>sys.stderr, "****Failed to convert file content to int number"
else:
if num is None:
print " ", file, 'is unset'
else:
print " ", file, 'contains number', num
def checkQdirs(user, basedir, queuedir):
path = os.path.join(basedir, user, queuedir)
if not os.path.exists(path):
if os.getuid() == 0:
print 'creating user queue dir', path
capisuite.fileutils._mkuserdir(user, basedir, user, queuedir)
return 1
else:
print >>sys.stderr, 'missing user queue dir', path
return 0
return 1
# END helper functions ================
def checkGlobalConfig(file=None):
print "Reading global configuration ...",
try:
config = capisuite.config.readGlobalConfig(file)
except IOError, err:
print
error("Failed to read/parse config file:", err)
print 'okay'
print
print "========= current configuration ==================="
for section in config.sections():
print "----------------", '['+section+']', "-------------"
options = config.options(section)
options.sort()
for option in options:
print option, '=', config.get(section, option)
print
print
print "=========== Testing User Queues and Sequence Files ====="
print
basedir = config.get('GLOBAL', 'fax_user_dir')
userdirs_missing = 0
for user in config.listUsers():
print "----------------", user, "-------------"
for queue in (SEND_Q, RECEIVED_Q):
if not checkQdirs(user, basedir, queue):
userdirs_missing += 1
else:
test_nextfile(user, basedir, queue, "fax")
if queue == RECEIVED_Q:
test_nextfile(user, basedir, queue, "voice")
if userdirs_missing:
print
print 'Some user queue dirs are missing.'
print "Rerun as 'root' to create them."
print
#----------- main -----------#
if len(sys.argv) == 1:
checkGlobalConfig()
elif 0:
checkGlobalConfig(sys.argv[1])
else:
config = capisuite.config.readGlobalConfig(sys.argv[1])
sections = config.sections()
sections.sort()
for section in sections:
print "----------------", '['+section+']', "-------------"
options = config.options(section)
options.sort()
for option in options:
print option, '=', config.get(section, option)
print
print 'known users:',
print config.listUsers()
# todo: these checks:
# - are all users valid system users?
# - do all voice-users have 'voice_delay' set?
# - option --configured to chekc if any fax or voice user is configured

View File

@ -1,4 +1,5 @@
#!@PYTHON@
# -*- mode: python -*-
#
# capisuitefax - capisuite tool for enqueuing faxes
# ---------------------------------------------------
@ -11,18 +12,10 @@
# 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,string
import getopt, os, sys, pwd, string, commands
# capisuite stuff
import cs_helpers
dialstring=""
addressee=""
subject=""
abort=""
user=""
quiet=0
listqueue=0
useprefix=1
import capisuite.fax
import capisuite.config
def usage(error=""):
print """capisuitefax - capisuite tool for enqueueing faxes
@ -50,165 +43,163 @@ other options:
-l, --list print jobs in the send queue
The given files must be in Adobe PostScript or PDF format"""
if (error!=""):
if error:
print
print "ERROR:",error
sys.exit(1)
def showlist(config,user):
sendq=cs_helpers.getOption(config,"","fax_user_dir")
if (sendq==None):
print "ERROR: option fax_user_dir not set in fax configuration"
sys.exit(1)
sendq=os.path.join(sendq,user,"sendq")+"/"
print "ID Nr./Addressee Tries Next try Subject"
def convert2Fax(filenames, faxname):
# todo: clean this up, maybe rewrite it
"""
convert file 'filename' to a SFF fax file 'faxfile'.
"""
def checkFile(fname, fname_):
if not os.access(fname, os.R_OK):
print >> sys.stderr, "can't open", fname
return 0
status, filetype = commands.getstatusoutput(
"file -b -i %s 2>/dev/null" % fname_)
if status:
usage("Error when executing command 'file'")
return 0
if filetype.find("application/postscript") < 0 \
and filetype.find("application/pdf") < 0:
print >> sys.stderr, arg, "is not a PostScript/PDF file"
return 0
return 1
files=os.listdir(sendq)
files=filter (lambda s: re.match("fax-.*\.txt",s),files)
if (not len(files)):
print "--- queue empty ---"
# todo: catch errors!
files = []
for fname in filenames:
fname_ = commands.mkarg(fname)
if not checkFile(fname, fname_):
return 0
files.append(fname_)
command = "gs -dNOPAUSE -dQUIET -dBATCH -sDEVICE=cfax " \
"-sOutputFile=%s %s" % (faxname, ' '.join(files))
ret = os.system(command) >> 8
if ret:
print >> sys.stderr, "error during SFF-conversion ot file", arg
print >> sys.stderr, "Ghostscript not installed?"
sys.exit()
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")
dest=cs_helpers.getOption(control,"GLOBAL","addressee","")
if (dest==""):
dest=control.get("GLOBAL","dialstring")
sys.stdout.write(dest+"\t")
if (len(dest)<8):
sys.stdout.write("\t")
sys.stdout.write(control.get("GLOBAL","tries")+"\t")
sys.stdout.write(control.get("GLOBAL","starttime")+"\t")
sys.stdout.write(cs_helpers.getOption(control,"GLOBAL","subject","")+"\n")
sys.exit(0)
def abortjob(config,user,job):
sendq=cs_helpers.getOption(config,"","fax_user_dir")
if (sendq==None):
print "ERROR: option fax_user_dir not set in fax configuration"
sys.exit(1)
sendq=os.path.join(sendq,user,"sendq")+"/"
job="fax-"+job+".txt"
if (not os.access(sendq+job,os.W_OK)):
print "job to abort not valid"
sys.exit(1)
def showQueue(config, user):
try:
lockfile=open(sendq+job[:-3]+"lock","w")
# lock so that it isn't deleted while sending
fcntl.lockf(lockfile,fcntl.LOCK_EX | fcntl.LOCK_NB)
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)):
jobs = capisuite.fax.getQueueDetails(config, user)
except capisuite.config.NoOptionError:
print "ERROR: option fax_user_dir not set in fax configuration"
return 10
if not jobs:
print "--- queue empty ---"
else:
format = "%s\t%s %2s\t%-24s %s"
print format % ('ID', 'Numb./Addr.', 'Try', 'Next try', 'Subject')
for entry in jobs:
print format % entry
return 0
def abortJob(config, user, job):
# todo: test whether 'job' is an integer
try:
capisuite.fax.abortUserJob(config, user, jobnum=job)
except capisuite.fax.InvalidJob:
print "job to abort not valid"
return 1
except capisuite.fax.JobLockedError:
print "Job is currently in transmission. Can't abort."
return 1
return 0
try:
optlist,args = getopt.getopt(sys.argv[1:], "d:a:u:lhqnA:S:"
,['dialstring=','noprefix','help',"abort=","list","quiet","user=",
optlist,args = getopt.getopt(sys.argv[1:], "d:a:u:lhqnA:S:",
['dialstring=','noprefix','help',"abort=","list","quiet","user=",
'addressee=','subject='])
except getopt.GetoptError, e:
usage(e.msg)
sys.exit(1)
# read options
for option,param in optlist:
if option in ('-d','--dialstring'): dialstring=param
if option in ('-A','--addressee'): addressee=param
if option in ('-S','--subject'): subject=param
if option in ('-n','--noprefix'): useprefix=0
dialstring = addressee = subject = abort = user = ""
quiet = listqueue = 0
useprefix = 1
for option, param in optlist:
# commands
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 option in ('-u','--user'):
if (os.getuid()==0):
user=param
elif option in ('-l','--list'): listqueue = 1
elif option in ('-a','--abort'): abort = param
# send options
elif option in ('-d','--dialstring'): dialstring = param
elif option in ('-A','--addressee'): addressee = param
elif option in ('-S','--subject'): subject = param
elif option in ('-n','--noprefix'): useprefix = 0
elif option in ('-q','--quiet'): quiet = 1
elif option in ('-u','--user'):
if not os.getuid():
user = param
else:
usage("--user may only used as root!")
if (not abort and not listqueue and not dialstring):
if not abort and not listqueue and not dialstring:
usage("No usable command given.")
# filter out common separators from dialstring, check it
dialstring=dialstring.translate(string.maketrans("",""),"-/ ()")
for i in dialstring:
if ((i>'9' or i<'0') and i not in ('+*#')):
usage("Invalid dialstring given.")
config = capisuite.config.readGlobalConfig()
if not user:
user = pwd.getpwuid(os.getuid()).pw_name
if (dialstring and len(args)==0):
if listqueue:
sys.exit(showQueue(config, user))
elif abort:
try:
jobnum = int(abort)
except ValueError:
print "Error: job id has to be a number, but is %r" % abort
sys.exit(1)
sys.exit(abortJob(config, user, jobnum))
else:
# queue fax for sending
# filter out common separators from dialstring, check it
dialstring = dialstring.translate(string.maketrans("",""),"-/ ()")
for i in dialstring:
if not i in '+0123456789*#':
usage("Invalid dialstring given, character %r is not allowed." % i)
if dialstring and len(args)==0:
usage("No fax files given")
# test if this user is allowed to send faxes
config=cs_helpers.readConfig()
if (user==""):
user=pwd.getpwuid(os.getuid())[0]
if (not config.has_section(user)):
# test if this user is allowed to send faxes
if not config.has_section(user):
print "Sorry, you're no valid user for CapiSuite"
sys.exit(1)
if ((cs_helpers.getOption(config,user,"outgoing_MSN","")=="") and (config.get(user,"fax_numbers","")=="")):
if not config.has_option(user, "outgoing_MSN") and \
not config.has_option(user, "fax_numbers"):
print "Sorry, you're not allowed to use fax services"
sys.exit(1)
# test environment
sendq=cs_helpers.getOption(config,"","fax_user_dir")
if (sendq==None):
print "ERROR: option fax_user_dir not set in fax configuration"
sys.exit(1)
sendq=os.path.join(sendq,user,"sendq")+"/"
if (not os.access(sendq,os.W_OK)):
if useprefix:
dialstring = config.getUser(user, "dial_prefix", '') + dialstring
# convert and enqueue files
jobDesc = {
"dialstring": dialstring,
#"user": user,
"addressee": addressee,
"subject": subject,
# todo: allow these to be specified for this jobs
#'stationID': ...
#'headline': ...
}
try:
jobnum = capisuite.fax.enqueueJob(config, user, args,
convert2Fax, **jobDesc)
except IOError, e:
print e
print "can't write to queue dir"
sys.exit(1)
if (listqueue):
showlist(config,user)
if (abort):
abortjob(config,user,abort)
prefix=cs_helpers.getOption(config,user,"dial_prefix","")
if (useprefix):
dialstring=prefix+dialstring
# 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 "+cs_helpers.escape(i)+" 2>/dev/null")
filetype=t.read()
if (t.close()):
usage("can't execute \"file\"")
if (not re.search("application/postscript",filetype) \
and not re.search("application/pdf",filetype)):
sys.stderr.write(i+" is not a PostScript/PDF file\n")
continue
newname=cs_helpers.uniqueName(sendq,"fax","sff")
command="gs -dNOPAUSE -dQUIET -dBATCH -sDEVICE=cfax -sOutputFile=" \
+ newname+" "+cs_helpers.escape(i)
ret=(os.system(command))>>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+"\"\naddressee=\""+addressee+"\"\nsubject=\""
+subject+"\"\n")
os.chmod(newname,0600)
os.chmod(newname[:-3]+"txt",0600)
if (os.getuid()==0):
user_entry=pwd.getpwnam(user)
os.chown(newname,user_entry[2],user_entry[3])
os.chown(newname[:-3]+"txt",user_entry[2],user_entry[3])
print i,"successful enqueued as",newname,"for",dialstring
print "Successful enqueued as job", jobnum, "for", dialstring

View File

@ -1,3 +1,4 @@
# -*- mode: python -*-
# cs_helpers.py - some helper functions for CapiSuite scripts
# -----------------------------------------------------------
# copyright : (C) 2002 by Gernot Hillier
@ -9,131 +10,84 @@
# 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"
import os, commands
# @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 config file, section GLOBAL missing")
return config
from capisuite.config import *
from capisuite.voice import sayNumber, getAudio
# @brief escape a filename to include it savely in a shell command
#
# The filename is enclosed in single quotation marks and quotation
# marks therein are quoted
#
# @return the escaped filename
def escape(filename):
return "'%s'" % filename.replace("'","'\\''")
# Note: readConfig is now imported from capisuite.config
# @brief get an option from the user or global section
# @brief escape a argument to include it savely in a shell command
#
# The option is searched in the users section and if not found
# in the global section.
# This is just a wrapper to commands.mkarg which strips the leading
# space which mkarg() adds.
#
# @param config the ConfigParser object containing the values
# @param user user section to use, if empty only global section is read
# @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,default=None):
if config.has_option(user,option):
return config.get(user,option)
elif config.has_option('GLOBAL',option):
return config.get('GLOBAL',option)
else:
return default
# @return the escaped argument
def escape(arg):
arg = commands.mkarg(arg)
if arg[0] == ' ':
arg = arg[1:]
return arg
# @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,capisuite
systemdir=getOption(config,"","audio_dir")
if (systemdir==None):
raise IOError("option audio_dir not found.")
userdir=getOption(config,"","voice_user_dir")
if (userdir==None):
raise IOError("option voice_user_dir not found.")
userdir=os.path.join(userdir,user)
if (int(getOption(config,"","user_audio_files","0"))
and os.access(os.path.join(userdir,filename),os.R_OK)):
return os.path.join(userdir,filename)
else:
return os.path.join(systemdir,filename)
def getOption(config, user, option, default=None):
return config.getUser(user, option, default)
# @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(os.path.join(directory,"cs_lock"),"w")
fcntl.lockf(lockfile,fcntl.LOCK_EX)
# Note: getAudio is now imported from capisuite.voice
# Note: uniqueName is now imported from capisuite.fileutils
def uniqueName(*args, **kwargs):
return capisuite.fileutils.uniqueName(*args, **kwargs)[1]
def __sendmail(mail_from, mail_to, msg):
import popen2, capisuite.core
r,w = popen2.popen2("{ /usr/sbin/sendmail -t -f %s } 2>&1" % escape(mail_from))
try:
countfile=open("%s-nextnr" % os.path.join(directory,basename),"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("%s-.*\.%s" % (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()
w.write(msg.as_string())
except IOError: #Errno 32: Broken Pipe
capisuite.core.error("Error while calling sendmail. Not installed?\n")
return 0
w.close()
#ret = sendmail.wait()
text = r.read()
r.close()
if text:
capisuite.core.error("Error while calling sendmail")#, return code=%i" % ret)
capisuite.core.error(text)
return 0
capisuite.core.log("sendmail finished successful",3)
return 1
newname="%s-%i.%s" % (os.path.join(directory,basename),nextnr,suffix)
def __sendmail(fromaddr, toaddr, msg):
import smtplib, capisuite.core
smtpserver, port = 'localhost', 25
server = smtplib.SMTP(smtpserver, port)
server.sendmail(fromaddr, (toaddr), msg.as_string())
server.quit()
capisuite.core.log("mail sent successfully",3)
return 1
countfile=open("%s-nextnr" % os.path.join(directory,basename),"w")
countfile.write('%i\n' % (nextnr+1))
countfile.close()
# unlock
fcntl.lockf(lockfile,fcntl.LOCK_UN)
lockfile.close()
os.unlink(os.path.join(directory,"cs_lock"))
return newname
def __call(msg, cmd, *args):
"""
outfile MUST be last parameter!
"""
# todo: think about using commands.getstatusoutput() here
ret = os.spawnlp(os.P_WAIT, cmd, cmd, *(args))
if ret or not os.access(args[-1], os.F_OK):
raise ConvertionError("Error while converting %s. "
"File damaged or %s not installed?" %(msg, cmd))
def sff2tif(infile, outfile):
__call('sff to tif', "sfftobmp", "-tif", infile, outfile)
def cff2ps(infile, outfile):
__call("cff to ps", "jpeg2ps", "-m", infile , "-o", outfile)
def la2wav(infile, outfile):
__call('la to wav', "sox", infile, '-w', outfile)
class ConvertionError(Exception): pass
# @brief send email with text and attachment of type sff or la converted to pdf/wav
#
@ -149,8 +103,11 @@ def uniqueName(directory,basename,suffix):
# @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,encodings.ascii,os,sys,popen2,capisuite
def sendMIMEMail(mail_from, mail_to, mail_subject, mail_type,
text, attachment):
import email.MIMEBase, email.MIMEText, email.MIMEAudio, email.Encoders
import encodings.ascii, os
msg = email.MIMEBase.MIMEBase("multipart","mixed")
msg['Subject']=mail_subject
msg['From']=mail_from
@ -159,85 +116,71 @@ def sendMIMEMail(mail_from,mail_to,mail_subject,mail_type,text,attachment):
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]
basepath = os.path.splitext(attachment)[0]
basename = os.path.basename(basepath)
try:
if (mail_type=="sff"): # normal fax file
# sff -> tif
ret=os.spawnlp(os.P_WAIT,"sfftobmp","sfftobmp","-tif",attachment,"%stif" % basename)
if (ret or not os.access("%stif" % basename,os.F_OK)):
raise "conv-error","Error while converting sff to tif. File damaged or sfftobmp not installed?"
# tif -> ps -> pdf
# the first pipe must be handled by the shell so that the output of
# of ps2pdf can be read immediately. Handling this shell in Python
# leads to an overflow of the ps2pdf output pipe...
command="tiff2ps -a %s | ps2pdf - -" % escape("%stif" % basename)
tiff2pdf=popen2.Popen3(command)
if (tiff2pdf.poll()!=-1):
raise "conv-error","Error while calling tiff2ps or ps2pdf. Not installed?"
tiff2pdf.tochild.close() # we don't need the input pipe
# create attachment with pdf stream
filepart = email.MIMEBase.MIMEBase("application","pdf",name="%spdf" % os.path.basename(basename))
filepart.add_header('Content-Disposition','attachment',filename="%spdf" % os.path.basename(basename))
filepart.set_payload(tiff2pdf.fromchild.read())
tiff2pdf.fromchild.close()
ret=tiff2pdf.wait()
if (ret!=0):
raise "conv-error","Error %i occured during tiff2ps or ps2pdf" % ret
os.unlink("%stif" % basename)
if mail_type == "sff": # normal fax file
# convert sff -> tif
sff2tif(attachment, "%s.tif" % basepath)
# convert tif -> ps -> pdf
cmd = "tiff2ps -a %s | ps2pdf - -" % escape("%s.tif" %basepath)
try:
status, content = commands.getstatusoutput(cmd)
finally:
os.unlink("%s.tif" % basepath)
if status:
raise ConvertionError("Error while calling tiff2ps or ps2pdf. "
"Not installed?")
filepart = email.MIMEBase.MIMEBase("application","pdf",
name = "%s.pdf" % basename)
filepart.add_header('Content-Disposition','attachment',
filename = "%s.pdf" % basename)
filepart.set_payload(content)
email.Encoders.encode_base64(filepart)
elif (mail_type=="cff"): # color fax file
# cff -> ps
ret=os.spawnlp(os.P_WAIT,"jpeg2ps","jpeg2ps","-m",attachment,"-o","%sps" % basename)
if (ret or not os.access("%sps" % basename,os.F_OK)):
raise "conv-error","Can't convert cff to ps. File damaged or jpeg2ps not installed?"
# tif -> ps -> pdf
# the first pipe must be handled by the shell so that the output of
# of ps2pdf can be read immediately. Handling this shell in Python
# leads to an overflow of the ps2pdf output pipe...
command="ps2pdf %s -" % escape("%sps" % basename)
ps2pdf=popen2.Popen3(command)
if (ps2pdf.poll()!=-1):
raise "conv-error","Error while calling ps2pdf. Not installed?"
ps2pdf.tochild.close() # we don't need the input pipe
# create attachment with pdf stream
filepart = email.MIMEBase.MIMEBase("application","pdf",name="%spdf" % os.path.basename(basename))
filepart.add_header('Content-Disposition','attachment',filename="%spdf" % os.path.basename(basename))
filepart.set_payload(ps2pdf.fromchild.read())
ps2pdf.fromchild.close()
ret=ps2pdf.wait()
if (ret!=0):
raise "conv-error","Error %i occured during ps2pdf" % ret
os.unlink("%sps" % basename)
elif mail_type == "cff": # color fax file
# convert cff -> ps
cff2ps(attachment, "%s.ps" % basepath)
# convert ps -> pdf
cmd = "ps2pdf %s -" % escape("%s.ps" % basepath)
try:
status, content = commands.getstatusoutput(cmd)
finally:
os.unlink("%s.ps" % basepath)
if status:
raise ConvertionError("Error while calling ps2pdf. "
"Not installed?")
filepart = email.MIMEBase.MIMEBase("application", "pdf",
name = "%s.pdf" % basename)
filepart.add_header('Content-Disposition', 'attachment',
filename="%s.pdf" % basename)
filepart.set_payload(content)
email.Encoders.encode_base64(filepart)
elif (mail_type=="la"): # voice file
elif mail_type == "la": # voice file
# 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,"-w","%swav" % basename)
if (ret or not os.access("%swav" % basename,os.R_OK)):
raise "conv-error","Error while calling sox. File damaged or sox not installed?"
filepart = email.MIMEAudio.MIMEAudio(open("%swav" % basename).read(),"x-wav",email.Encoders.encode_base64,name="%swav" % os.path.basename(basename))
filepart.add_header('Content-Disposition','attachment',filename="%swav" % os.path.basename(basename))
os.unlink("%swav" % basename)
la2wav(attachment, "%s.wav" % basepath)
content = open("%s.wav" % basepath).read()
os.unlink("%s.wav" % basepath)
filepart = email.MIMEAudio.MIMEAudio(content, "x-wav",
email.Encoders.encode_base64,
name = "%s.wav" % basename)
filepart.add_header('Content-Disposition', 'attachment',
filename = "%s.wav" % basename)
textpart = email.MIMEText.MIMEText(text)
msg.attach(textpart)
msg.attach(filepart)
except "conv-error",errormessage:
text="%s\n\nERROR occured while converting file: %s\nPlease talk to your friendly administrator.\n" % (text,errormessage)
textpart = email.MIMEText.MIMEText(text)
except ConvertionError, errormessage:
text = [text, ''
'The following error occured while converting file:',
str(errormessage), ''
'Please talk to your friendly administrator.', ''
]
# todo: add encoding (at least as example)
# todo: use latin-1 as std-encoding instead of ascii
textpart = email.MIMEText.MIMEText('\n'.join(text))
msg.attach(textpart)
sendmail = popen2.Popen3("sendmail -t -f %s" % escape(mail_from))
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=%i" % ret)
else:
capisuite.log("sendmail finished successful",3)
return __sendmail(mail_from, mail_to, msg)
# @brief send a simple text email
#
@ -247,28 +190,21 @@ def sendMIMEMail(mail_from,mail_to,mail_subject,mail_type,text,attachment):
# @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,encodings.ascii,popen2,sys,capisuite
#
# @result returns true on success
def sendSimpleMail(mail_from, mail_to, mail_subject, text):
#import email.Encoders, email.MIMEText, encodings.ascii
# Create a text/plain message. Don't forget to change charset here
# if you want to use non-us-ascii characters in the mail!
# todo: add encoding anyway (at least as example)
# todo: use latin-1 as std-encoding instead of ascii
import email.MIMEText
msg = email.MIMEText.MIMEText(text)
msg['Subject'] = mail_subject
msg['From'] = mail_from
msg['To'] = mail_to
sendmail = popen2.Popen3("sendmail -t -f %s" % escape(mail_from))
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=%i" % ret)
else:
capisuite.log("sendmail finished successful",3)
return __sendmail(mail_from, mail_to, msg)
# @brief write description file for received fax or voice
@ -279,157 +215,19 @@ def sendSimpleMail(mail_from,mail_to,mail_subject,text):
#
# @param filename the data filename (with extension!)
# @param content the content as string
def writeDescription(filename,content):
descr=open("%stxt" % filename[:filename.rindex('.')+1],"w")
descr.write("# Description file for %s\n" % filename)
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=\"%s\"\n" % filename)
descr.write(content)
def writeDescription(filename, content):
from types import StringType
assert isinstance(content, StringType)
import capisuite.fileutils
descrname = capisuite.fileutils.controlname(filename)
descr = open(descrname, "w")
print >> descr, "# Description file for", filename
print >> descr, "# This if for internal use of CapiSuite."
print >> descr, "# Only change if you know what you do!"
print >> descr, "[GLOBAL]"
print >> descr, 'filename="%s"' % filename
print >> descr, 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.
# An input of "-" produces the word "unbekannt" (unknown)
#
# @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
# @param gender if the number is used in connection with a singular noun ("f" --> "eine Nachricht")
def sayNumber(call,number,curr_user,config,gender="-"):
import capisuite
if (number=="-" or number=="??"): # "??" is needed for backward compatibility to versions <= 0.4.1a
capisuite.audio_send(call,getAudio(config,curr_user,"unbekannt.la"),1)
elif (gender!="-" and number in ("1","01")):
if (gender in ("n","m")):
capisuite.audio_send(call,getAudio(config,curr_user,"ein.la"),1)
else:
capisuite.audio_send(call,getAudio(config,curr_user,"eine.la"),1)
elif (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,"%s.la" % number),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,"%s0.la" % number[0]),1)
else:
capisuite.audio_send(call,getAudio(config,curr_user,"%s.la" % number[1]),1)
capisuite.audio_send(call,getAudio(config,curr_user,"und.la"),1)
capisuite.audio_send(call,getAudio(config,curr_user,"%s0.la" % number[0]),1)
else:
for i in number:
capisuite.audio_send(call,getAudio(config,curr_user,"%s.la" % i),1)
# sayNumber is now imported from capisuite.voice
# Old Log (for new changes see ChangeLog):
# Revision 1.14 2003/10/19 20:17:54 gernot
# - sendMIMEMail: better wording for some error messages during file conversion
#
# Revision 1.13 2003/07/21 17:44:07 gernot
# - forgot one import in last commit :-|
#
# Revision 1.12 2003/07/20 10:27:51 gernot
# - workaround for Python RuntimeError "cannot unmarshal code objects in
# restricted execution mode", thx to Sander Roest for finally finding
# this solution
#
# Revision 1.11 2003/06/16 10:20:36 gernot
# - use new multipage feature of jpeg2ps (requires special jpeg2ps version!)
#
# Revision 1.10 2003/05/25 13:38:30 gernot
# - support reception of color fax documents
#
# Revision 1.9 2003/04/24 21:04:20 gernot
# - replace functions deprecated in Python 2.2.2 (mainly related to the email
# module)
#
# Revision 1.8 2003/04/24 14:03:18 gernot
# - shortened some long lines
# - added function escape which escapes a string for shell usage
# - escape mail addresses given to sendmail
#
# Revision 1.7 2003/04/16 07:16:35 gernot
# - fixed pipe buffer overflow for conversion of long fax documents to PDF
#
# Revision 1.6 2003/04/10 21:29:51 gernot
# - support empty destination number for incoming calls correctly (austrian
# telecom does this (sic))
# - core now returns "-" instead of "??" for "no number available" (much nicer
# in my eyes)
# - new wave file used in remote inquiry for "unknown number"
#
# Revision 1.5 2003/04/10 20:54:44 gernot
# - allow multiple mail addresses to be set as fax_email or voice_email
#
# Revision 1.4 2003/04/08 07:59:56 gernot
# - replace some wrong space indentations by tabs...
#
# Revision 1.3 2003/04/07 15:58:37 gernot
# - attachments to sent e-mails now get a valid filename
#
# Revision 1.2 2003/03/20 09:12:42 gernot
# - error checking for reading of configuration improved, many options got
# optional, others produce senseful error messages now if not found,
# fixes bug# 531, thx to Dieter Pelzel for reporting
#
# Revision 1.1.1.1 2003/02/19 08:19:54 gernot
# initial checkin of 0.4
#
# 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
#

View File

@ -114,6 +114,55 @@ fax_headline="Sent by CapiSuite (www.CapiSuite.de)"
# messages.
fax_email_from="capisuite daemon <root>"
###############################################################################
############################# Mail settings ###################################
###############################################################################
#########
# defined for fax receive: call_from, call_to, stationID, bitRate,
# resolution color, numPages, filename, hostname
[MailFaxReceived]
subject = Received a fax from %(call_from)s to %(call_to)s
text =
You got a fax from %(call_from)s to %(call_to)s
Station ID: %(stationID)s
Transmission Details: bit rate %(bitRate)i %(resolution)s %(color)s
Pages: %(numPages)i
See attached file.
The original file was saved to file://%(filename)s on host "%(hostname)s".
#########
# defined for fax send: addressee, dialstring, subject, filename, tries,
# result, resultB3, filename, hostname
[MailFaxSent]
subject = Fax to %(addressee)s (%(dialstring)s) sent successfully.
text =
Your fax job to %(addressee)s (%(dialstring)s) was sent successfully.
Subject: %(subject)s
Filename: %(filename)s
Number of tries: %(tries)i
Last result: 0x%(result)x/0x%(resultB3)x
It was moved to file://%(filename)s on host %(hostname)s.
[MailFaxFailed]
subject = Fax to %(addressee)s (%(dialstring)s) FAILED.
text =
I'm sorry, but your fax job to %(addressee)s (%(dialstring)s) failed finally.
Subject: %(subject)s
Filename: %(filename)s
Number of tries: %(tries)i
Last result: 0x%(result)x/0x%(resultB3)x
It was moved to file://%(filename)s on host %(hostname)s.
###############################################################################
############################# user settings ###################################
###############################################################################

View File

@ -1,3 +1,5 @@
# -*- mode: python ; coding: iso_8859_15 -*-
#
# idle.py - default script for capisuite
# ---------------------------------------------
# copyright : (C) 2002 by Gernot Hillier
@ -10,247 +12,140 @@
# (at your option) any later version.
#
import os,re,time,pwd,fcntl
import os, time, pwd, fcntl
# capisuite stuff
import capisuite,cs_helpers
#import capisuite
import capisuite.fax
from capisuite.config import NoOptionError
import capisuite.core as core
# todo: eliminate this
import cs_helpers
# sendfax is now imported from capisuite.fax
from capisuite.fileutils import _releaseLock, _getLock, LockTakenError
def idle(capi):
config=cs_helpers.readConfig()
spool=cs_helpers.getOption(config,"","spool_dir")
if (spool==None):
capisuite.error("global option spool_dir not found.")
return
done=os.path.join(spool,"done")
failed=os.path.join(spool,"failed")
if (not os.access(done,os.W_OK) or not os.access(failed,os.W_OK)):
capisuite.error("Can't read/write to the necessary spool dirs")
return
userlist=config.sections()
userlist.remove('GLOBAL')
for user in userlist: # search in all user-specified sendq's
userdata=pwd.getpwnam(user)
outgoing_nr=cs_helpers.getOption(config,user,"outgoing_MSN","")
if (outgoing_nr==""):
incoming_nrs=cs_helpers.getOption(config,user,"fax_numbers","")
if (incoming_nrs==""):
continue
else:
outgoing_nr=(incoming_nrs.split(','))[0]
udir=cs_helpers.getOption(config,"","fax_user_dir")
if (udir==None):
capisuite.error("global option fax_user_dir not found.")
return
udir=os.path.join(udir,user)
sendq=os.path.join(udir,"sendq")
if (not os.access(udir,os.F_OK)):
os.mkdir(udir,0700)
os.chown(udir,userdata[2],userdata[3])
if (not os.access(sendq,os.F_OK)):
os.mkdir(sendq,0700)
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="%ssff" % job[:-3]
real_user_c=os.stat(os.path.join(sendq,job)).st_uid
real_user_j=os.stat(os.path.join(sendq,job_fax)).st_uid
if (real_user_j!=pwd.getpwnam(user)[2] or real_user_c!=pwd.getpwnam(user)[2]):
capisuite.error("job %s seems to be manipulated (wrong uid)! Ignoring..." % os.path.join(sendq,job_fax))
continue
lockfile=open(os.path.join(sendq,"%slock" % job[:-3]),"w")
# read directory contents
fcntl.lockf(lockfile,fcntl.LOCK_EX) # lock so that it isn't deleted while sending
if (not os.access(os.path.join(sendq,job),os.W_OK)): # perhaps it was cancelled?
fcntl.lockf(lockfile,fcntl.LOCK_UN)
lockfile.close()
os.unlink(os.path.join(sendq,"%slock" % job[:-3]))
continue
control=cs_helpers.readConfig(os.path.join(sendq,job))
# set DST value to -1 (unknown), as strptime sets it wrong for some reason
starttime=(time.strptime(control.get("GLOBAL","starttime")))[0:8]+(-1,)
starttime=time.mktime(starttime)
if (starttime>time.time()):
fcntl.lockf(lockfile,fcntl.LOCK_UN)
lockfile.close()
os.unlink(os.path.join(sendq,"%slock" % job[:-3]))
continue
tries=control.getint("GLOBAL","tries")
dialstring=control.get("GLOBAL","dialstring")
addressee=cs_helpers.getOption(control,"GLOBAL","addressee","")
subject=cs_helpers.getOption(control,"GLOBAL","subject","")
mailaddress=cs_helpers.getOption(config,user,"fax_email","")
if (mailaddress==""):
mailaddress=user
fromaddress=cs_helpers.getOption(config,user,"fax_email_from","")
if (fromaddress==""):
fromaddress=user
capisuite.log("job %s from %s to %s initiated" % (job_fax,user,dialstring),1)
result,resultB3 = sendfax(capi,os.path.join(sendq,job_fax),outgoing_nr,dialstring,user,config)
tries+=1
capisuite.log("job %s: result was %x,%x" % (job_fax,result,resultB3),1)
if (result in (0,0x3400,0x3480,0x3490,0x349f) and resultB3==0):
movejob(job_fax,sendq,done,user)
capisuite.log("job %s: finished successfully" % job_fax,1)
mailtext="Your fax job to %s (%s) was sent successfully.\n\n" \
"Subject: %s\nFilename: %s\nNeeded tries: %i\n" \
"Last result: 0x%x/0x%x\n\nIt was moved to " \
"file://%s on host \"%s\"" % (addressee,dialstring, \
subject,job_fax,tries,result,resultB3, \
os.path.join(done,"%s-%s" % (user,job_fax)), \
os.uname()[1])
cs_helpers.sendSimpleMail(fromaddress,mailaddress,
"Fax to %s (%s) sent successfully." % (addressee,dialstring),
mailtext)
else:
max_tries=int(cs_helpers.getOption(config,"","send_tries","10"))
delays=cs_helpers.getOption(config,"","send_delays","60,60,60,300,300,3600,3600,18000,36000").split(",")
delays=map(int,delays)
if ((tries-1)<len(delays)):
next_delay=delays[tries-1]
else:
next_delay=delays[-1]
starttime=time.time()+next_delay
capisuite.log("job %s: delayed for %i seconds" % (job_fax,next_delay),2)
cs_helpers.writeDescription(os.path.join(sendq,job_fax), \
"dialstring=\"%s\"\nstarttime=\"%s\"\ntries=\"%i\"\n" \
"user=\"%s\"\naddressee=\"%s\"\nsubject=\"%s\"\n" \
% (dialstring,time.ctime(starttime),tries,user, \
addressee,subject))
if (tries>=max_tries):
movejob(job_fax,sendq,failed,user)
capisuite.log("job %s: failed finally" % job_fax,1)
mailtext="I'm sorry, but your fax job to %s (%s) " \
"failed finally.\n\nSubject: %s\n" \
"Filename: %s\nTries: %i\n" \
"Last result: 0x%x/0x%x\n\n" \
"It was moved to file://%s-%s on host %s.\n\n" \
% (addressee,dialstring,subject,job_fax,tries,result, \
resultB3,os.path.join(failed,user),job_fax,os.uname()[1])
cs_helpers.sendSimpleMail(fromaddress,mailaddress,
"Fax to %s (%s) FAILED." % (addressee,dialstring),
mailtext)
fcntl.lockf(lockfile,fcntl.LOCK_UN)
lockfile.close()
os.unlink("%slock" % os.path.join(sendq,job[:-3]))
def sendfax(capi,job,outgoing_nr,dialstring,user,config):
config = capisuite.config.readGlobalConfig()
try:
controller=int(cs_helpers.getOption(config,"","send_controller","1"))
timeout=int(cs_helpers.getOption(config,user,"outgoing_timeout","60"))
stationID=cs_helpers.getOption(config,user,"fax_stationID")
if (stationID==None):
capisuite.error("Warning: fax_stationID for user %s not set" % user)
stationID=""
headline=cs_helpers.getOption(config,user,"fax_headline","")
(call,result)=capisuite.call_faxG3(capi,controller,outgoing_nr,dialstring,timeout,stationID,headline)
if (result!=0):
return(result,0)
capisuite.fax_send(call,job)
return(capisuite.disconnect(call))
except capisuite.CallGoneError:
return(capisuite.disconnect(call))
spool = config.get('GLOBAL', "spool_dir")
max_tries = config.getint('GLOBAL', "send_tries")
delays = config.getList('GLOBAL', "send_delays")
except NoOptionError, err:
core.error("global option %s not found." % err.option)
return
def movejob(job,olddir,newdir,user):
os.rename(os.path.join(olddir,job),os.path.join(newdir,"%s-%s" % (user,job)))
os.rename(os.path.join(olddir,"%stxt" % job[:-3]),os.path.join(newdir, "%s-%stxt" % (user,job[:-3])))
# todo: implement config.getQueue(queue,user=None)
doneQ = os.path.join(spool, "done")
failedQ = os.path.join(spool, "failed")
#
# History:
#
# Old Log (for new changes see ChangeLog):
# Revision 1.11 2003/12/02 18:50:09 gernot
# - fax_numbers is really allowed to be empty now...
#
# Revision 1.10 2003/10/03 13:42:09 gernot
# - added new options "fax_email_from" and "voice_email_from"
#
# Revision 1.9 2003/09/21 12:34:37 gernot
# - add 0x349f to list of normal results
#
# Revision 1.8 2003/06/26 11:53:17 gernot
# - fax jobs can be given an addressee and a subject now (resolves #18, reported
# by Achim Bohnet)
#
# Revision 1.7 2003/06/19 14:58:43 gernot
# - fax_numbers is now really optional (bug #23)
# - tries counter was wrongly reported (bug #29)
#
# Revision 1.6 2003/04/06 11:07:40 gernot
# - fix for 1-hour-delayed sending of fax (DST problem)
#
# Revision 1.5 2003/03/20 09:12:42 gernot
# - error checking for reading of configuration improved, many options got
# optional, others produce senseful error messages now if not found,
# fixes bug# 531, thx to Dieter Pelzel for reporting
#
# Revision 1.4 2003/03/13 11:09:58 gernot
# - use stricted permissions for saved files and created userdirs. Fixes
# bug #544
#
# Revision 1.3 2003/03/09 11:48:10 gernot
# - removed wrong unlock operation (lock not acquired at this moment!)
#
# Revision 1.2 2003/03/06 09:59:11 gernot
# - added "file://" as prefix to filenames in sent mails, thx to
# Achim Bohnet for this suggestion
#
# Revision 1.1.1.1 2003/02/19 08:19:54 gernot
# initial checkin of 0.4
#
# 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
#
if not os.access(doneQ, os.W_OK) or not os.access(failedQ, os.W_OK):
core.error("Can't read/write to the necessary spool dirs")
return
# search in all user-specified sendq's
for user in config.listUsers():
outgoing_num = config.getUser(user, "outgoing_msn")
if not outgoing_num:
incoming_nums = config.getUser(user, "fax_numbers")
if not incoming_nums:
continue
outgoing_num = incoming_nums.split(',')[0].strip()
mailaddress = config.getUser(user, "fax_email", user)
fromaddress = config.getUser(user, "fax_email_from", user)
for jobnum, controlfile in capisuite.fax.getQueueFiles(config, user):
assert controlfile == os.path.abspath(controlfile)
try:
# lock the job so that it isn't deleted while sending
lock = _getLock(forfile=controlfile, blocking=0)
except LockTakenError:
# if we didn't get the lock, continue with next job
continue
try:
control = capisuite.config.JobDescription(controlfile)
fax_file = control.get('filename')
assert fax_file == os.path.abspath(fax_file)
# both the job control file and the fax file must have
# the users uid
uid = pwd.getpwnam(user).pw_uid
if os.stat(controlfile).st_uid != uid or \
os.stat(fax_file).st_uid != uid:
core.error("job %s seems to be manipulated (wrong uid)! "
"Ignoring..." % controlfile)
_releaseLock(lock)
continue
# todo: describe what is tested here
# perhaps it was cancelled?
if not os.access(controlfile, os.W_OK):
_releaseLock(lock)
continue
# set DST value to -1 (unknown), as strptime sets it wrong
# for some reason
starttime = time.strptime(control.get("starttime"))[:-1]+(-1, )
starttime = time.mktime(starttime)
if starttime > time.time():
_releaseLock(lock)
continue
sendinfo = {
'outgoing_num': outgoing_num,
'dialstring': control.get("dialstring")
}
# these options may overwrite the global settings per job
for n in ('stationID', 'headline'):
if control.has_option(n):
sendinfo[n] = control.get(n)
core.log("job %s from %s to %s initiated" %
(jobnum, user, sendinfo['dialstring']), 1)
result, resultB3 = capisuite.fax.sendfax(config, user, capi,
fax_file, **sendinfo)
core.log("job %s: result was %x, %x" % \
(jobnum, result, resultB3), 1)
sendinfo['result'] = result
sendinfo['resultB3'] = resultB3
# todo: use symbolic names for these results to be more
# meaningfull
send_ok = resultB3 == 0 \
and result in (0, 0x3400, 0x3480, 0x3490, 0x349f)
tries = control.getint("tries") +1
if send_ok:
core.log("job %s: finished successfully" % jobnum, 1)
control = capisuite.fax.moveJob(controlfile, doneQ, user)
sendinfo.update(control.items())
sendinfo['hostname'] = os.uname()[1]
cs_helpers.sendSimpleMail(
fromaddress, mailaddress,
config.get('MailFaxSent', 'subject') % sendinfo,
config.get('MailFaxSent', 'text') % sendinfo)
elif tries >= max_tries:
# too many ties, send failed
core.log("job %s: failed finally" % jobnum, 1)
control = capisuite.fax.moveJob(controlfile, failedQ, user)
sendinfo.update(control.items())
cs_helpers.sendSimpleMail(
fromaddress, mailaddress,
config.get('MailFaxFailed', 'subject') % sendinfo,
config.get('MailFaxFailed', 'text') % sendinfo)
else:
# delay next try
next_delay = int(delays[ min(len(delays),tries) -1 ])
core.log("job %s: delayed for %i seconds" % \
(jobnum, next_delay), 2)
starttime = time.time() + next_delay
control.set('starttime', time.ctime(starttime))
control.set('tries', tries)
control.write(controlfile)
finally:
_releaseLock(lock)

View File

@ -1,3 +1,5 @@
# -*- mode: python ; coding: iso_8859_15 -*-
#
# incoming.py - standard incoming script for capisuite
# ----------------------------------------------------
# copyright : (C) 2002 by Gernot Hillier
@ -11,581 +13,419 @@
#
# general imports
import time,os,re,string,pwd
import time, os
# CapiSuite imports
import capisuite,cs_helpers
import capisuite, cs_helpers
from capisuite.config import NoOptionError
import capisuite.fileutils as fileutils
import capisuite.fax
import capisuite.voice
from capisuite import core
say = capisuite.voice.say # shortcut
# @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
def callIncoming(call, service, call_from, call_to):
"""
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.
'call' reference to the call. Needed by all capisuite functions
'service' one of SERVICE_FAXG3, SERVICE_VOICE, SERVICE_OTHER
'call_from' string containing the number of the calling party
'call_to' string containing the number of the called party
"""
# convert into a python call handle
# TODO-gh: can't we get rid of this line?
call = core.Call(call, service, call_from, call_to)
# read config file and search for call.to_nr 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: %s Disconnecting..." % e)
capisuite.reject(call,0x34A9)
config = capisuite.config.readGlobalConfig()
except IOError, e:
core.error("Error occured during config file reading: %s "
"Disconnecting..." % e)
call.reject(0x34A9)
return
for user in config.listUsers():
# accept a voice call on 'voice_numbers'
if config.has_option(user, 'voice_numbers'):
numbers = config.getList(user, 'voice_numbers')
if numbers == ["*"] or call.to_nr in numbers:
if service in (core.SERVICE_VOICE, ):
break
# accept a voice or fax call on 'fax_numbers'
if config.has_option(user, 'fax_numbers'):
numbers = config.getList(user, 'fax_numbers')
if numbers == ["*"] or call.to_nr in numbers:
if service in (core.SERVICE_FAXG3, core.SERVICE_VOICE):
# set service type to 'fax'
service = core.SERVICE_FAXG3
break
else:
# no matching entry found (no users as this number)
call.log("call from %s to %s ignoring" % (call.from_nr, call.to_nr), 1)
call.reject(1)
return
# answer the call with the right service
if (curr_user==""):
capisuite.log("call from %s to %s ignoring" % (call_from,call_to),1,call)
capisuite.reject(call,1)
return
try:
if (curr_service==capisuite.SERVICE_VOICE):
delay=cs_helpers.getOption(config,curr_user,"voice_delay")
if (delay==None):
capisuite.error("voice_delay not found for user %s! -> rejecting call" % curr_user)
capisuite.reject(call,0x34A9)
return
capisuite.log("call from %s to %s for %s connecting with voice" % (call_from,call_to,curr_user),1,call)
capisuite.connect_voice(call,int(delay))
voiceIncoming(call,call_from,call_to,curr_user,config)
elif (curr_service==capisuite.SERVICE_FAXG3):
faxIncoming(call,call_from,call_to,curr_user,config,0)
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
# @param already_connected 1 if we're already connected (that means we must switch to fax mode)
def faxIncoming(call,call_from,call_to,curr_user,config,already_connected):
try:
udir=cs_helpers.getOption(config,"","fax_user_dir")
if (udir==None):
capisuite.error("global option fax_user_dir not found! -> rejecting call")
capisuite.reject(call,0x34A9)
return
udir=os.path.join(udir,curr_user)
if (not os.path.exists(udir)):
userdata=pwd.getpwnam(curr_user)
os.mkdir(udir,0700)
os.chown(udir,userdata[2],userdata[3])
if (not os.path.exists(os.path.join(udir,"received"))):
userdata=pwd.getpwnam(curr_user)
os.mkdir(os.path.join(udir,"received"),0700)
os.chown(os.path.join(udir,"received"),userdata[2],userdata[3])
except KeyError:
capisuite.error("user %s is not a valid system user. Disconnecting" % curr_user,call)
capisuite.reject(call,0x34A9)
return
filename="" # assure the variable is defined...
faxInfo=None
try:
stationID=cs_helpers.getOption(config,curr_user,"fax_stationID")
if (stationID==None):
capisuite.error("Warning: fax_stationID not found for user %s -> using empty string" % curr_user)
stationID=""
headline=cs_helpers.getOption(config,curr_user,"fax_headline","") # empty string is no problem here
capisuite.log("call from %s to %s for %s connecting with fax" % (call_from,call_to,curr_user),1,call)
if (already_connected):
faxInfo=capisuite.switch_to_faxG3(call,stationID,headline)
if service == core.SERVICE_VOICE:
voiceIncoming(config, user, call)
elif service == core.SERVICE_FAXG3:
faxIncoming(config, user, call, 0)
else:
faxInfo=capisuite.connect_faxG3(call,stationID,headline,0)
if (faxInfo!=None and faxInfo[3]==1):
faxFormat="cff" # color fax
else:
faxFormat="sff" # normal b&w fax
filename=cs_helpers.uniqueName(os.path.join(udir,"received"),"fax",faxFormat)
faxInfo=capisuite.fax_receive(call,filename)
(cause,causeB3)=capisuite.disconnect(call)
capisuite.log("connection finished with cause 0x%x,0x%x" % (cause,causeB3),1,call)
raise RuntimeError
except NoOptionError, err:
core.error("global option %s not found -> rejecting call" %
err.option)
call.reject(0x34A9)
except fileutils.UnknownUserError:
core.error("user %s is not a valid system user. Disconnecting." % user,
call)
call.reject(0x34A9)
except core.CallGoneError:
causes = call.disconnect()
call.log("connection lost with cause 0x%x, 0x%x" % causes, 1)
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=\"%s\"\ncall_to=\"%s\"\ntime=\"%s\"\n" \
"cause=\"0x%x/0x%x\"\n" % (call_from,call_to,time.ctime(),cause,causeB3))
userdata=pwd.getpwnam(curr_user)
os.chmod(filename,0600)
os.chown(filename,userdata[2],userdata[3])
os.chmod("%stxt" % filename[:-3],0600)
os.chown("%stxt" % filename[:-3],userdata[2],userdata[3])
def faxIncoming(config, user, call, already_connected):
"""
Called by callIncoming when an incoming fax call is received
fromaddress=cs_helpers.getOption(config,curr_user,"fax_email_from","")
if (fromaddress==""):
fromaddress=curr_user
mailaddress=cs_helpers.getOption(config,curr_user,"fax_email","")
if (mailaddress==""):
mailaddress=curr_user
action=cs_helpers.getOption(config,curr_user,"fax_action","").lower()
if (action not in ("mailandsave","saveonly")):
capisuite.error("Warning: No valid fax_action definition found for user %s -> assuming SaveOnly" % curr_user)
action="saveonly"
if (action=="mailandsave"):
mailText="You got a fax from %s to %s\nDate: %s" % (call_from,call_to,time.ctime())
if (faxInfo!=None and len(faxInfo)>=5):
mailText="%sStation ID: %s\nTransmission Details: bit rate %i " \
"%s %s\nPages: %i\n\nSee attached file.\n" \
"The original file was saved to file://%s " \
"on host \"%s\"." % (mailText,faxInfo[0], \
faxInfo[1],(faxInfo[2] and "hiRes" or "loRes"), \
(faxInfo[3] and "color" or ""),faxInfo[4], \
filename,os.uname()[1])
cs_helpers.sendMIMEMail(fromaddress, mailaddress, "Fax received from %s to %s" % (call_from,call_to),
faxFormat, mailText, filename)
'call' a python Call handle referencing to the call
'user' name of the user who is responsible for this
'config' ConfigParser instance holding the config data
'already_connected' ture if we're already connected (that means we must
switch to fax mode)
"""
# todo: use config.getQueue + _mkdir here
receivedQ = fileutils._mkuserdir(user,
config.get('GLOBAL', "fax_user_dir"),
user, "received")
# @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):
# assure these variables are defined
filename = faxInfo = None
stationID = config.getUser(user, "fax_stationID", default="")
if not stationID:
core.error("Warning: fax_stationID not found for user %s "
"-> using empty string" % user)
# empty string is no problem for headline
headline = config.getUser(user, "fax_headline", default="")
call.log("call from %s to %s for %s connecting with fax" % \
(call.from_nr, call.to_nr, user), 1)
try:
udir=cs_helpers.getOption(config,"","voice_user_dir")
if (udir==None):
capisuite.error("global option voice_user_dir not found! -> rejecting call")
capisuite.reject(call,0x34A9)
return
udir=os.path.join(udir,curr_user)
if (not os.path.exists(udir)):
userdata=pwd.getpwnam(curr_user)
os.mkdir(udir,0700)
os.chown(udir,userdata[2],userdata[3])
if (not os.path.exists(os.path.join(udir,"received"))):
userdata=pwd.getpwnam(curr_user)
os.mkdir(os.path.join(udir,"received"),0700)
os.chown(os.path.join(udir,"received"),userdata[2],userdata[3])
except KeyError:
capisuite.error("user %s is not a valid system user. Disconnecting" % curr_user,call)
capisuite.reject(call,0x34A9)
return
filename=cs_helpers.uniqueName(os.path.join(udir,"received"),"voice","la")
action=cs_helpers.getOption(config,curr_user,"voice_action","").lower()
if (action not in ("mailandsave","saveonly","none")):
capisuite.error("Warning: No valid voice_action definition found for user %s -> assuming SaveOnly" % curr_user)
action="saveonly"
try:
capisuite.enable_DTMF(call)
userannouncement=os.path.join(udir,cs_helpers.getOption(config,curr_user,"announcement","announcement.la"))
pin=cs_helpers.getOption(config,curr_user,"pin","")
if (os.access(userannouncement,os.R_OK)):
capisuite.audio_send(call,userannouncement,1)
if already_connected:
faxInfo = call.switch_to_faxG3(stationID, headline)
else:
if (call_to!="-"):
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)
faxInfo = call.connect_faxG3(stationID, headline)
filename = fileutils.uniqueName(receivedQ, "fax", faxInfo.format)[1]
call.fax_receive(filename)
causes = call.disconnect()
call.log("connection finished with cause 0x%x, 0x%x" % causes, 1)
except core.CallGoneError:
# catch this here to get the cause info into the mail
causes = call.disconnect()
call.log("connection lost with cause 0x%x, 0x%x" % causes, 1)
# todo: send error mail here? Don't think it makes sense to send
# a mail on each try, which would mean sending 10 mails for one fax...
# If the user wants to know the current status he should use "capisuitefax -l"
else:
assert filename
if os.access(filename, os.R_OK):
faxInfo = faxInfo.as_dict()
faxInfo.update({
'filename' : filename,
'call_from': call.from_nr,
'call_to' : call.to_nr,
'causes' : causes,
'hostname' : os.uname()[1]
})
capisuite.fax.createReceivedJob(user, **faxInfo)
action = _getAction(config, user, "fax_action",
("saveonly", "mailandsave"))
if action == "mailandsave":
fromaddress = config.getUser(user, "fax_email_from", user)
mailaddress = config.getUser(user, "fax_email", user)
if (action!="none"):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la"),1)
length=cs_helpers.getOption(config,curr_user,"record_length","60")
silence_timeout=cs_helpers.getOption(config,curr_user,"record_silence_timeout","5")
msg_length=capisuite.audio_receive(call,filename,int(length), int(silence_timeout),1)
cs_helpers.sendMIMEMail(
fromaddress, mailaddress,
config.get('MailFaxReceived', 'subject') % faxInfo,
faxInfo['format'],
config.get('MailFaxReceived', 'text') % faxInfo,
filename)
dtmf_list=capisuite.read_DTMF(call,0)
if (dtmf_list=="X"):
if (os.access(filename,os.R_OK)):
def _getAction(config, user, action_name, allowed_actions):
action = config.getUser(user, action_name, "").lower()
if action not in allowed_actions:
action = allowed_actions[0]
core.error("Warning: No valid %s definition found for user %s -> "
"assuming %s" % action_name, user, action)
return action
def voiceIncoming(config, user, call):
"""
Called by callIncoming when an incoming voice call is received
'call' a python Call handle referencing to the call
'user' name of the user who is responsible for this
'config' ConfigParser instance holding the config data
"""
try:
if not config.has_option(user, 'voice_delay'):
core.error("voice_delay not found for user %s! -> "
"rejecting call" % user)
call.reject(0x34A9)
return
delay = config.getint(user, "voice_delay")
call.log("call from %s to %s for %s connecting with voice" % \
(call.from_nr, call.to_nr, user), 1)
call.connect_voice(delay)
userdir = config.get('GLOBAL', "voice_user_dir")
action = _getAction(config, user, "voice_action",
("saveonly", "mailandsave", "none"))
receivedQ = fileutils._mkuserdir(user, userdir, user, "received")
userannouncement = os.path.join(userdir, user,
config.getUser(user, "announcement", "announcement.la"))
pin = config.getUser(user, "pin", "")
filename = None # assure it's defined
call.enable_DTMF()
if os.access(userannouncement, os.R_OK):
call.audio_send(userannouncement, 1)
else:
if call.to_nr != "-":
say(config, user, call, "anrufbeantworter-von.la")
capisuite.voice.sayNumber(config, user, call, call.to_nr)
say(config, user, call, "bitte-nachricht.la")
if action != "none":
say(config, user, call, "beep.la")
length = config.getUser(user, "record_length", 60)
# todo: put this into voice.getNameForRecord
filename = fileutils.uniqueName(receivedQ, "voice", 'la')[1]
silence_timeout = config.getUser(user, "record_silence_timeout", 5)
msg_length = call.audio_receive(filename, int(length),
int(silence_timeout), 1)
dtmf_list = call.read_DTMF(0)
if dtmf_list == "X":
if os.access(filename, os.R_OK):
os.unlink(filename)
faxIncoming(call,call_from,call_to,curr_user,config,1)
elif (dtmf_list!="" and pin!=""):
dtmf_list="%s%s" % (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)):
faxIncoming(config, user, call, 1)
elif dtmf_list and pin:
# wait 5 seconds for input
dtmf_list += call.read_DTMF(3)
count = 1
# allow three tries
while count < 3 and pin != dtmf_list:
call.log("wrong PIN entered...", 1)
say(config, user, call, "beep.la")
dtmf_list = call.read_DTMF(3)
count += 1
else:
# failed three time
# todo: hang up?
# gh: Yes.
pass
if pin == dtmf_list:
if os.access(filename, os.R_OK):
os.unlink(filename)
capisuite.log("Starting remote inquiry...",1,call)
remoteInquiry(call,udir,curr_user,config)
call.log("Starting remote inquiry...", 1)
remoteInquiry(call, user, config, receivedQ)
(cause,causeB3)=capisuite.disconnect(call)
capisuite.log("connection finished with cause 0x%x,0x%x" % (cause,causeB3),1,call)
except core.CallGoneError:
# catch this here to get the cause info in the mail
causes = call.disconnect()
call.log("connection lost with cause 0x%x, 0x%x" % causes, 1)
else:
causes = call.disconnect()
call.log("connection finished with cause 0x%x, 0x%x" % causes, 1)
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 (filename and os.access(filename, os.R_OK)):
info = capisuite.voice.createReceivedJob(user, filename, call.from_nr,
call.to_nr, causes)
fromaddress = config.getUser(user, "voice_email_from", user)
mailaddress = config.getUser(user, "voice_email", user)
if action == "mailandsave":
info['hostname'] = os.uname()[1]
info['msg_length'] = msg_length
cs_helpers.sendMIMEMail(
fromaddress, mailaddress,
config.get('MailVoiceReceived', 'subject') % info,
"la",
config.get('MailVoiceReceived', 'text') % info,
filename)
if (os.access(filename,os.R_OK)):
cs_helpers.writeDescription(filename,
"call_from=\"%s\"\ncall_to=\"%s\"\ntime=\"%s\"\n" \
"cause=\"0x%x/0x%x\"\n" % (call_from,call_to,time.ctime(),cause,causeB3))
userdata=pwd.getpwnam(curr_user)
os.chmod(filename,0600)
os.chown(filename,userdata[2],userdata[3])
os.chmod("%stxt" % filename[:-2],0600)
os.chown("%stxt" % filename[:-2],userdata[2],userdata[3])
def remoteInquiry(config, user, call, receivedQ):
"""
Remote inquiry function (uses german wave snippets!)
fromaddress=cs_helpers.getOption(config,curr_user,"voice_email_from","")
if (fromaddress==""):
fromaddress=curr_user
mailaddress=cs_helpers.getOption(config,curr_user,"voice_email","")
if (mailaddress==""):
mailaddress=curr_user
if (action=="mailandsave"):
mailText="You got a voice call from %s to %s\n" \
"Date: %s\nLength: %i s\n\nSee attached file.\n" \
"The original file was saved to file://%s on" \
"host \"%s\".\n\n" % (call_from,call_to,time.ctime(), \
msg_length,filename,os.uname()[1])
subject="Voice call received from %s to %s" % (call_from,call_to)
cs_helpers.sendMIMEMail(fromaddress,mailaddress,subject,"la",mailText,filename)
Implemented commands for remote inquiry are:
delete message - 1
next message - 4
last message - 5
repeat current message - 6
# @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(os.path.join(userdir,"received/inquiry_lock"),"w")
'user' name of the user who is responsible for this
'config' ConfigParser instance holding the config data
'call' reference to the call. Needed by all capisuite functions
'receivedQ' the received queue dir of the user
"""
try:
fcntl.lockf(lockfile,fcntl.LOCK_EX | fcntl.LOCK_NB) # only one inquiry at a time!
except IOError,err: # can't get the lock
if (err.errno in (errno.EACCES,errno.EAGAIN)):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"fernabfrage-aktiv.la"))
lockfile.close()
lock = fileutils._getLock(lockname=os.path.join(receivedQ,
'inquiry_lock'),
blocking=0)
except fileutils.LockTakenError:
say(config, user, call, "fernabfrage-aktiv.la")
return
try:
# read directory contents
messages=os.listdir(os.path.join(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()
messages = capisuite.voice.getQueue(config, user)
# read the number of the message heard last at the last inquiry
lastinquiry=-1
if (os.access(os.path.join(userdir,"received/last_inquiry"),os.W_OK)):
lastfile=open(os.path.join(userdir,"received/last_inquiry"),"r")
lastinquiry=int(lastfile.readline())
lastfile.close()
lastinquiry = capisuite.voice.getInquiryCounter(config, user)
# filter out old messages
oldmessages = [m for m in messages if m[0] <= lastinquiry]
messages = [m for m in messages if m[0] > lastinquiry]
oldmessages.sort()
messages.sort()
# sort out old messages
oldmessages=[]
i=0
while (i<len(messages)):
if (messages[i]<=lastinquiry):
oldmessages.append(messages[i])
del messages[i]
else:
i+=1
cs_helpers.sayNumber(call,str(len(messages)),curr_user,config,"f")
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)
announceNumMessages(config, user, call, len(messages), new=1)
# menu for record new announcement
cmd=""
while (cmd not in ("1","9")):
if (len(messages)+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)
cmd = ""
while cmd not in ("1", "9"):
if len(messages) or len(oldmessages):
say(config, user, call, "zum-abhoeren-1.la")
say(config, user, call, "fuer-neue-ansage-9.la")
cmd = call.read_DTMF(0, 1)
if cmd == "9":
cmd_recordNewAnnouncement(config, user, call, receivedQ)
return
# start inquiry
for curr_msgs in (messages,oldmessages):
cs_helpers.sayNumber(call,str(len(curr_msgs)),curr_user,config,"f")
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=os.path.join(userdir,"received/voice-%i.la" % curr_msgs[i])
descr=cs_helpers.readConfig("%stxt" % filename[:-2])
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"nachricht.la"),1)
cs_helpers.sayNumber(call,str(i+1),curr_user,config)
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)
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,"n")
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("%stxt" % filename[:-2])
del curr_msgs[i]
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"nachricht-geloescht.la"))
elif (cmd=="4"):
if (curr_msgs[i]>lastinquiry):
lastinquiry=curr_msgs[i]
lastfile=open(os.path.join(userdir,"received/last_inquiry"),"w")
lastfile.write("%i\n" % curr_msgs[i])
lastfile.close()
i+=1
elif (cmd=="5"):
i-=1
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"keine-weiteren-nachrichten.la"))
cmd_inquiry(config, user, call, oldmessages, messages, lastinquiry)
finally:
# unlock
fcntl.lockf(lockfile,fcntl.LOCK_UN)
lockfile.close()
os.unlink(os.path.join(userdir,"received/inquiry_lock"))
fileutils._releaseLock(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,os.path.join(userdir,"announcement-tmp.la"),60,3)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-ansage-lautet.la"))
capisuite.audio_send(call,os.path.join(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=os.path.join(userdir,cs_helpers.getOption(config,curr_user,"announcement","announcement.la"))
os.rename(os.path.join(userdir,"announcement-tmp.la"),userannouncement)
userdata=pwd.getpwnam(curr_user)
os.chown(userannouncement,userdata[2],userdata[3])
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"ansage-gespeichert.la"))
def announceNumMessages(config, user, call, numMessages, new):
if numMessages == 1:
msg = "nachricht.la"
else:
msg = "nachrichten.la"
if new:
msg = 'neue-' + msg
capisuite.voice.sayNumber(config, user, call, numMessages, gender="f")
say(config, user, call, msg)
def cmd_inquiry(config, user, call, oldmessages, messages, lastinquiry):
"""
Remote inquiry function (uses german wave snippets!)
Implemented commands for remote inquiry are:
delete message - 1
next message - 4
last message - 5
repeat current message - 6
'user' name of the user who is responsible for this
'config' ConfigParser instance holding the config data
'call' reference to the call. Needed by all capisuite functions
'userdir' spool_dir of the current_user
"""
for curr_msgs in (messages, oldmessages):
capisuite.voice.sayNumber(config, user, call, len(curr_msgs), "f")
announceNumMessages(config, user, call, len(messages),
new = (curr_msgs == messages))
i = 0
while i < len(curr_msgs):
msgnum, controlfile = curr_msgs[i]
descr = config.JobDescription(controlfile)
# play the announcement
for f in _getSoundsForAnnounceMessages(i, descr):
say(config, user, call, "%s.la" % f)
# play the recorded file
call.audio_send(descr.get('filename'), 1)
cmd = ""
while cmd not in ("1", "4", "5", "6"):
say(config, user, call, "erklaerung.la")
cmd = call.read_DTMF(0, 1)
if cmd == "1":
cmd_deleteMessage(config, user, call, controlfile)
del curr_msgs[i]
elif cmd == "4":
if msgnum > lastinquiry:
lastinquiry = msgnum
capisuite.voice.setInquiryCounter(config, user, msgnum)
i += 1
elif cmd == "5":
i -= 1
say(config, user, call, "keine-weiteren-nachrichten.la")
# todo: say 'hauptmenu'
def _getSoundsForAnnounceMessages(msgnum, descr):
"""
generated a list of sound to be played before replaying a recored message
"""
getNumberFiles = capisuite.voice.getNumberFiles
yield 'nachricht'
for f in getNumberFiles(msgnum+1): yield f
yield 'von'
for f in getNumberFiles(descr.get('call_from')): yield f
yield 'fuer'
for f in getNumberFiles(descr.get('call_to')): yield f
yield 'am'
calltime = time.strptime(descr.get('time'))
for f in getNumberFiles(calltime[2]): yield f
yield '.'
for f in getNumberFiles(calltime[1]): yield f
yield '.'
yield 'um'
for f in getNumberFiles(calltime[3], 'n'): yield f
yield 'uhr'
for f in getNumberFiles(calltime[4]): yield f
def cmd_deleteMessage(config, user, call, controlfile):
capisuite.fax.abortJob(controlfile)
say(config, user, call, "nachricht-geloescht.la")
def cmd_recordNewAnnouncement(config, user, call, userdir):
"""
remote inquiry command: record new announcement (uses german wave snippets!)
'config' ConfigParser instance holding the config data
'user' name of the user who is responsible for this
'call' reference to the call. Needed by all capisuite functions
'userdir' spool_dir of the current_user
"""
say(config, user, call, "bitte-neue-ansage-komplett.la", "beep.la")
tmpfile = os.path.join(userdir, "announcement-tmp.la")
while 1:
call.audio_receive(tmpfile, 60, 3)
say(config, user, call, "neue-ansage-lautet.la")
call.audio_send(tmpfile)
say(config, user, call, "wenn-einverstanden-1.la")
cmd = call.read_DTMF(0, 1)
# todo: allow eg. '9' for cancel and go back to menu
if cmd == "1":
break
else:
say(config, user, call, "bitte-neue-ansage-kurz.la", "beep.la")
userannouncement = os.path.join(userdir,
config.getUser(user, "announcement", "announcement.la"))
os.rename(tmpfile, userannouncement)
fileutils._setProtection(user, userannouncement, mode=0666)
say(config, user, call, "ansage-gespeichert.la")
#
# History:
#
# Old Log (for new changes see ChangeLog):
# Revision 1.13 2003/12/01 20:53:05 gernot
# - confused "hiRes" and "loRes". Thx to Ingo Goeppert <Ingo.Goeppert@gmx.de>
# for the report!
#
# Revision 1.12 2003/10/03 13:42:09 gernot
# - added new options "fax_email_from" and "voice_email_from"
#
# Revision 1.11 2003/08/24 12:47:50 gernot
# - faxIncoming tried to reconnect when it was called after a switch from
# voice to fax mode, which lead to a call abort. Thx to Harald Jansen &
# Andreas Scholz for reporting!
#
# Revision 1.10 2003/07/20 10:30:37 gernot
# - started implementing faxInfo output in sent mails, not working currently
#
# Revision 1.9 2003/06/27 07:51:09 gernot
# - replaced german umlaut in filename "nachricht-gelscht.la", can cause
# problems on Redhat, thx to Herbert Hübner for reporting
#
# Revision 1.8 2003/06/16 10:21:05 gernot
# - define filename in any case (thx to Axel Schneck for reporting and
# analyzing...)
#
# Revision 1.7 2003/05/25 13:38:30 gernot
# - support reception of color fax documents
#
# Revision 1.6 2003/04/10 21:29:51 gernot
# - support empty destination number for incoming calls correctly (austrian
# telecom does this (sic))
# - core now returns "-" instead of "??" for "no number available" (much nicer
# in my eyes)
# - new wave file used in remote inquiry for "unknown number"
#
# Revision 1.5 2003/03/20 09:12:42 gernot
# - error checking for reading of configuration improved, many options got
# optional, others produce senseful error messages now if not found,
# fixes bug# 531, thx to Dieter Pelzel for reporting
#
# Revision 1.4 2003/03/13 11:08:06 gernot
# - fix remote inquiry locking (should fix bug #534, but doesn't - anyway,
# this fix is definitely necessary)
# - stricter permissions of saved files and created dirs, fixes #544
# - add "file://" prefix to the path shown in the mails to the user
#
# Revision 1.3 2003/02/21 13:13:34 gernot
# - removed some debug output (oops...)
#
# Revision 1.2 2003/02/21 11:02:17 gernot
# - removed os.setuid() from incoming script
# -> fixes Bug #527
#
# Revision 1.1.1.1 2003/02/19 08:19:54 gernot
# initial checkin of 0.4
#
# 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
#

View File

@ -1,59 +1,81 @@
import string,os,time
import pcallcontrol
cc=pcallcontrol
import pcallcontrol as cc
import os, time
import commands
def callWaiting(CIP,callingParty,calledParty):
if (calledParty=="23"):
def acceptCall(CIP, callingParty, calledParty):
print "nehme Anruf von",callingParty,"an",calledParty,"an."
cc.connect(16) # 16 = telephony
else:
def rejectCall(CIP, callingParty, calledParty):
print "nehme Anruf von",callingParty,"an",calledParty,"nicht an."
cc.reject(2) # 2 = normal call clearing
def establishInternetConnection():
# establish connection
timeout = time.time()+15 # 15 seconds timeout
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() < timeout:
lines = commands.getoutput("/usr/sbin/cinternet --status")
for l in lines.splitlines():
if l.startswith("status"):
c_status = l[9:].strip()
print 'status:', c_status
time.sleep(1)
# get ip address
save_lang = os.environ["LANG"]
os.environ["LANG"] = ""
lines = commands.getoutput("/sbin/ifconfig ppp0")
os.environ["LANG"] = save_lang
for l in lines: # no need to split into lines
index = l.find("inet addr:")
if index >= 0:
index += 10
rindex = l.find(" ", index)
ip_address = l[index:rindex]
# play ip address
for i in ip_address:
cc.audio_send(i+".la")
callWaitingMap = {
'23': acceptCall,
'default': denyCall,
}
def callWaiting(CIP, callingParty, calledParty):
action = callWaitingMap.get(calledParty, None)
if action:
action(CIP, callingParty, calledParty)
else:
action = callWaitingMap.get('default', None)
if action:
action(CIP, callingParty, calledParty)
callConnectedMap = {
'3008': establishInternetConnection,
}
def callConnected():
try:
cc.enableDTMF()
cc.audio_send("send.la")
code=cc.getDTMF()
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")
action = callWaitingMap.get(calledParty, None)
if action:
action(CIP, callingParty, calledParty)
else:
action = callWaitingMap.get('default', None)
if action:
action(CIP, callingParty, calledParty)
cc.disconnect()
except cc.CallGoneError:
print "schimpf schimpf"
print "schimpf schimpf: call gone"
cc.callWaiting=callWaiting
cc.callConnected=callConnected
cc.callWaiting = callWaiting
cc.callConnected = callConnected

View File

@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.8.3 from Makefile.am.
# Makefile.in generated by automake 1.9.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@ -47,6 +47,12 @@ CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
SOURCES =
DIST_SOURCES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
am__installdirs = "$(DESTDIR)$(pkgdatadir)"
dist_pkgdataDATA_INSTALL = $(INSTALL_DATA)
DATA = $(dist_pkgdata_DATA)
@ -113,6 +119,8 @@ am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
datadir = @datadir@
@ -195,7 +203,7 @@ install-dist_pkgdataDATA: $(dist_pkgdata_DATA)
test -z "$(pkgdatadir)" || $(mkdir_p) "$(DESTDIR)$(pkgdatadir)"
@list='$(dist_pkgdata_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " $(dist_pkgdataDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(pkgdatadir)/$$f'"; \
$(dist_pkgdataDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(pkgdatadir)/$$f"; \
done
@ -203,7 +211,7 @@ install-dist_pkgdataDATA: $(dist_pkgdata_DATA)
uninstall-dist_pkgdataDATA:
@$(NORMAL_UNINSTALL)
@list='$(dist_pkgdata_DATA)'; for p in $$list; do \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(pkgdatadir)/$$f'"; \
rm -f "$(DESTDIR)$(pkgdatadir)/$$f"; \
done
@ -267,7 +275,7 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f $(CONFIG_CLEAN_FILES)
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@ -328,7 +336,7 @@ uninstall-am: uninstall-dist_pkgdataDATA uninstall-info-am
installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
pdf-am ps ps-am uninstall uninstall-am \
uninstall-dist_pkgdataDATA uninstall-info-am
uninstall-dist_pkgdataDATA uninstall-hook uninstall-info-am
uninstall-hook:

14
scripts/waves/SConscript Normal file
View File

@ -0,0 +1,14 @@
# -*- python -*-
import os, fnmatch
Import('env')
def findFiles(pattern, subdir=''):
files = []
for file in os.listdir(os.path.join(Dir('.').srcnode().abspath, subdir)):
if fnmatch.fnmatch(file, pattern):
files.append(os.path.join(subdir, file))
return files
Alias('install', env.Install('$pkgdatadir/waves', 'README' ))
Alias('install', env.Install('$pkgdatadir/waves', findFiles('*.la')))

View File

@ -4,7 +4,7 @@ sbin_PROGRAMS = capisuite
capisuite_LDADD=application/libccapplication.a modules/libccmodules.a \
backend/libccbackend.a
capisuite_SOURCES=main.cpp
SUBDIRS = application backend modules
SUBDIRS = application backend modules capisuite-py
pkgsysconf_DATA = capisuite.conf
EXTRA_DIST = capisuite.conf.in

View File

@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.8.3 from Makefile.am.
# Makefile.in generated by automake 1.9.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@ -58,7 +58,6 @@ capisuite_DEPENDENCIES = application/libccapplication.a \
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/main.Po
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
CXXLD = $(CXX)
@ -72,6 +71,12 @@ RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
install-recursive installcheck-recursive installdirs-recursive \
pdf-recursive ps-recursive uninstall-info-recursive \
uninstall-recursive
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
pkgsysconfDATA_INSTALL = $(INSTALL_DATA)
DATA = $(pkgsysconf_DATA)
ETAGS = etags
@ -140,6 +145,8 @@ am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
datadir = @datadir@
@ -179,7 +186,7 @@ capisuite_LDADD = application/libccapplication.a modules/libccmodules.a \
backend/libccbackend.a
capisuite_SOURCES = main.cpp
SUBDIRS = application backend modules
SUBDIRS = application backend modules capisuite-py
pkgsysconf_DATA = capisuite.conf
EXTRA_DIST = capisuite.conf.in
all: all-recursive
@ -254,16 +261,14 @@ distclean-compile:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
.cpp.obj:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
uninstall-info-am:
install-pkgsysconfDATA: $(pkgsysconf_DATA)
@ -271,7 +276,7 @@ install-pkgsysconfDATA: $(pkgsysconf_DATA)
test -z "$(pkgsysconfdir)" || $(mkdir_p) "$(DESTDIR)$(pkgsysconfdir)"
@list='$(pkgsysconf_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " $(pkgsysconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(pkgsysconfdir)/$$f'"; \
$(pkgsysconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(pkgsysconfdir)/$$f"; \
done
@ -279,7 +284,7 @@ install-pkgsysconfDATA: $(pkgsysconf_DATA)
uninstall-pkgsysconfDATA:
@$(NORMAL_UNINSTALL)
@list='$(pkgsysconf_DATA)'; for p in $$list; do \
f="`echo $$p | sed -e 's|^.*/||'`"; \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(pkgsysconfdir)/$$f'"; \
rm -f "$(DESTDIR)$(pkgsysconfdir)/$$f"; \
done
@ -357,14 +362,16 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
if (etags --etags-include --version) >/dev/null 2>&1; then \
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
include_option=--etags-include; \
empty_fix=.; \
else \
include_option=--include; \
empty_fix=; \
fi; \
list='$(SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test -f $$subdir/TAGS && \
test ! -f $$subdir/TAGS || \
tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
fi; \
done; \
@ -374,9 +381,11 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$tags$$unique" \
|| $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique; \
fi
ctags: CTAGS
CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
@ -427,15 +436,17 @@ distdir: $(DISTFILES)
|| exit 1; \
fi; \
done
list='$(SUBDIRS)'; for subdir in $$list; do \
list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test -d "$(distdir)/$$subdir" \
|| mkdir "$(distdir)/$$subdir" \
|| $(mkdir_p) "$(distdir)/$$subdir" \
|| exit 1; \
distdir=`$(am__cd) $(distdir) && pwd`; \
top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
(cd $$subdir && \
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="../$(top_distdir)" \
distdir="../$(distdir)/$$subdir" \
top_distdir="$$top_distdir" \
distdir="$$distdir/$$subdir" \
distdir) \
|| exit 1; \
fi; \
@ -467,7 +478,7 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f $(CONFIG_CLEAN_FILES)
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"

23
src/SConscript Normal file
View File

@ -0,0 +1,23 @@
# -*- python -*-
Import('env')
env.Append(capisuite_sources = Dir('.'))
SConscript('capisuite-py/SConscript')
libmodule = SConscript('modules/SConscript')
libappl = SConscript('application/SConscript')
libback = SConscript('backend/SConscript')
capisuite = env.Program('capisuite',
['main.cpp', libappl, libmodule, libback, ])
env.AddPostAction(capisuite, 'strip $TARGET')
capisuite_conf = env.FileSubst('capisuite.conf', 'capisuite.conf.in')
install_exec = env.Install('$sbindir', capisuite)
Alias('install-exec', install_exec)
Alias('install',
env.Install('$pkgsysconfdir', capisuite_conf),
install_exec,
)

View File

@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.8.3 from Makefile.am.
# Makefile.in generated by automake 1.9.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@ -46,9 +46,9 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
LIBRARIES = $(noinst_LIBRARIES)
AR = ar
ARFLAGS = cru
LIBRARIES = $(noinst_LIBRARIES)
libccapplication_a_AR = $(AR) $(ARFLAGS)
libccapplication_a_LIBADD =
am_libccapplication_a_OBJECTS = capisuite.$(OBJEXT) \
@ -58,11 +58,6 @@ libccapplication_a_OBJECTS = $(am_libccapplication_a_OBJECTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/capisuite.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/capisuitemodule.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/idlescript.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/incomingscript.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/pythonscript.Po
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
CXXLD = $(CXX)
@ -139,6 +134,8 @@ am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
datadir = @datadir@
@ -235,16 +232,14 @@ distclean-compile:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
.cpp.obj:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
uninstall-info-am:
@ -268,9 +263,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$tags$$unique" \
|| $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique; \
fi
ctags: CTAGS
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
@ -344,7 +341,7 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f $(CONFIG_CLEAN_FILES)
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"

View File

@ -0,0 +1,9 @@
# -*- python -*-
Import('env')
libappl = env.StaticLibrary('ccapplication', source = Split("""
capisuite.cpp capisuitemodule.cpp pythonscript.cpp
idlescript.cpp incomingscript.cpp
"""))
Return('libappl')

View File

@ -89,7 +89,7 @@ bool
convertConnRef(PyObject *conn_ref, Connection** conn)
{
if (!PyCObject_Check(conn_ref)) {
PyErr_SetString(PyExc_TypeError,"First parameter must be the call reference.");
PyErr_SetString(PyExc_TypeError,"Invalid call reference given.");
return 0;
}
@ -112,7 +112,7 @@ bool
convertCapiRef(PyObject *capi_ref, Capi** capi)
{
if (!PyCObject_Check(capi_ref)) {
PyErr_SetString(PyExc_TypeError,"First parameter must be the Capi reference.");
PyErr_SetString(PyExc_TypeError,"Invalid Capi reference given.");
return 0;
}
@ -966,7 +966,7 @@ capisuitemodule_init () throw (ApplicationError)
{
PyObject *mod,*d;
try {
if ( ! ( mod=Py_InitModule3("capisuite", PCallControlMethods, "Python module for controlling CapiSuite") ) ) // m=borrowed ref
if ( ! ( mod=Py_InitModule3("_capisuite", PCallControlMethods, "Python module for controlling CapiSuite") ) ) // m=borrowed ref
throw ApplicationError("unable to init python module capisuite (InitModule failed)","capisuite_init()");
if ( ! ( d=PyModule_GetDict(mod) ) ) // d=borrowed ref

View File

@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.8.3 from Makefile.am.
# Makefile.in generated by automake 1.9.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@ -46,9 +46,9 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
LIBRARIES = $(noinst_LIBRARIES)
AR = ar
ARFLAGS = cru
LIBRARIES = $(noinst_LIBRARIES)
libccbackend_a_AR = $(AR) $(ARFLAGS)
libccbackend_a_LIBADD =
am_libccbackend_a_OBJECTS = capi.$(OBJEXT) connection.$(OBJEXT)
@ -56,7 +56,6 @@ libccbackend_a_OBJECTS = $(am_libccbackend_a_OBJECTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/capi.Po ./$(DEPDIR)/connection.Po
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
CXXLD = $(CXX)
@ -133,6 +132,8 @@ am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
datadir = @datadir@
@ -225,16 +226,14 @@ distclean-compile:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
.cpp.obj:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
uninstall-info-am:
@ -258,9 +257,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$tags$$unique" \
|| $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique; \
fi
ctags: CTAGS
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
@ -334,7 +335,7 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f $(CONFIG_CLEAN_FILES)
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"

8
src/backend/SConscript Normal file
View File

@ -0,0 +1,8 @@
# -*- python -*-
Import('env')
libback = env.StaticLibrary('ccbackend', source = Split("""
capi.cpp connection.cpp
"""))
Return('libback')

View File

@ -0,0 +1,12 @@
pkgsysconfdir = @sysconfdir@/capisuite
pkgpython_PYTHON = __init__.py config.py consts.py core.py exceptions.py \
fax.py fileutils.py voice.py
EXTRA_DIST = config.py.in
config.py: config.py.in
rm -f $@
sed -e 's,@\pkgsysconfdir@,$(pkgsysconfdir),g' $< >$@
all-local: config.py

View File

@ -0,0 +1,347 @@
# Makefile.in generated by automake 1.9.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = ../..
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
INSTALL = @INSTALL@
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
subdir = src/capisuite-py
DIST_COMMON = $(pkgpython_PYTHON) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
$(top_srcdir)/configure.in
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
SOURCES =
DIST_SOURCES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
am__installdirs = "$(DESTDIR)$(pkgpythondir)"
pkgpythonPYTHON_INSTALL = $(INSTALL_DATA)
py_compile = $(top_srcdir)/py-compile
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMDEP_FALSE = @AMDEP_FALSE@
AMDEP_TRUE = @AMDEP_TRUE@
AMTAR = @AMTAR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXCPP = @CXXCPP@
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PYTHON = @PYTHON@
PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
PYTHON_PLATFORM = @PYTHON_PLATFORM@
PYTHON_PREFIX = @PYTHON_PREFIX@
PYTHON_VERSION = @PYTHON_VERSION@
RANLIB = @RANLIB@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
ac_ct_RANLIB = @ac_ct_RANLIB@
ac_ct_STRIP = @ac_ct_STRIP@
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
datadir = @datadir@
docdir = @docdir@
doxygen = @doxygen@
exec_prefix = @exec_prefix@
host_alias = @host_alias@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pkgpyexecdir = @pkgpyexecdir@
pkgpythondir = @pkgpythondir@
prefix = @prefix@
program_transform_name = @program_transform_name@
pyexecdir = @pyexecdir@
python_configdir = @python_configdir@
python_execprefix = @python_execprefix@
python_includespec = @python_includespec@
python_linkforshared = @python_linkforshared@
python_moduledir = @python_moduledir@
python_moduleexecdir = @python_moduleexecdir@
python_prefix = @python_prefix@
python_version = @python_version@
pythondir = @pythondir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
pkgsysconfdir = @sysconfdir@/capisuite
pkgpython_PYTHON = __init__.py config.py consts.py core.py exceptions.py \
fax.py fileutils.py voice.py
EXTRA_DIST = config.py.in
all: all-am
.SUFFIXES:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
&& exit 0; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/capisuite-py/Makefile'; \
cd $(top_srcdir) && \
$(AUTOMAKE) --gnu src/capisuite-py/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
uninstall-info-am:
install-pkgpythonPYTHON: $(pkgpython_PYTHON)
@$(NORMAL_INSTALL)
test -z "$(pkgpythondir)" || $(mkdir_p) "$(DESTDIR)$(pkgpythondir)"
@list='$(pkgpython_PYTHON)'; dlist=''; for p in $$list; do\
if test -f "$$p"; then b=; else b="$(srcdir)/"; fi; \
if test -f $$b$$p; then \
f=$(am__strip_dir) \
dlist="$$dlist $$f"; \
echo " $(pkgpythonPYTHON_INSTALL) '$$b$$p' '$(DESTDIR)$(pkgpythondir)/$$f'"; \
$(pkgpythonPYTHON_INSTALL) "$$b$$p" "$(DESTDIR)$(pkgpythondir)/$$f"; \
else :; fi; \
done; \
test -z "$$dlist" || \
PYTHON=$(PYTHON) $(py_compile) --basedir "$(DESTDIR)$(pkgpythondir)" $$dlist
uninstall-pkgpythonPYTHON:
@$(NORMAL_UNINSTALL)
@list='$(pkgpython_PYTHON)'; dlist=''; for p in $$list; do\
f=$(am__strip_dir) \
rm -f "$(DESTDIR)$(pkgpythondir)/$$f"; \
rm -f "$(DESTDIR)$(pkgpythondir)/$${f}c"; \
rm -f "$(DESTDIR)$(pkgpythondir)/$${f}o"; \
done
tags: TAGS
TAGS:
ctags: CTAGS
CTAGS:
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
case $$file in \
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
esac; \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
dir="/$$dir"; \
$(mkdir_p) "$(distdir)$$dir"; \
else \
dir=''; \
fi; \
if test -d $$d/$$file; then \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
fi; \
cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
else \
test -f $(distdir)/$$file \
|| cp -p $$d/$$file $(distdir)/$$file \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile all-local
installdirs:
for dir in "$(DESTDIR)$(pkgpythondir)"; do \
test -z "$$dir" || $(mkdir_p) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
`test -z '$(STRIP)' || \
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic mostlyclean-am
distclean: distclean-am
-rm -f Makefile
distclean-am: clean-am distclean-generic
dvi: dvi-am
dvi-am:
html: html-am
info: info-am
info-am:
install-data-am: install-pkgpythonPYTHON
install-exec-am:
install-info: install-info-am
install-man:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-generic
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-info-am uninstall-pkgpythonPYTHON
.PHONY: all all-am all-local check check-am clean clean-generic \
distclean distclean-generic distdir dvi dvi-am html html-am \
info info-am install install-am install-data install-data-am \
install-exec install-exec-am install-info install-info-am \
install-man install-pkgpythonPYTHON install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
pdf-am ps ps-am uninstall uninstall-am uninstall-info-am \
uninstall-pkgpythonPYTHON
config.py: config.py.in
rm -f $@
sed -e 's,@\pkgsysconfdir@,$(pkgsysconfdir),g' $< >$@
all-local: config.py
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

View File

@ -0,0 +1,27 @@
# -*- python -*-
Import('env')
def py_compile(target, source, env):
"""compile python modules for .../python2.x/site-packages/capisuite"""
# Note: this differs from #/scripts/SConscript.py_compile in 'dfile'
import py_compile, os.path
py_compile.compile(source[0].abspath,
#cfile=target[0].abspath,
dfile = os.path.join(env.subst('$pkgpython_moduledir'),
env.subst('$SOURCE.file')),
)
# substitute "pgksysconfdir"
env.FileSubst('config.py', 'config.py.in')
modules = []
for mod in Split('__init__ config consts fax fileutils voice exceptions '
'core'):
modules.append(mod+'.py')
modules.append(env.Command(mod + '.pyc', mod+'.py', py_compile))
install_pylib = env.Install('$pkgpython_moduledir', modules)
Alias('install-pylib',install_pylib)
Alias('install', install_pylib)

View File

@ -0,0 +1,24 @@
"""capisuite
The main package of CapiSuite. This package provides the interface between
the user programmable Python scripts for handling incoming and outgoing calls
and the CapiSuite core which makes the ISDN hardware available.
This package consists of several files containing classes for certain aspects:
- core: provides the basic access to all functions of the CapiSuite core
- fax: contains all routines necessary for fax connection handling
- voice: contains all routines necessary for voice connection handling
- config: allows to read and write configuration and job description files
- fileutils: contains general file handling routines typically needed
by CapiSuite scripts
- consts: some constant definitions
- exceptions: collection of exception classes used by the classes and functions
in the other files
"""
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright (c) 2004 by Hartmut Goebel"
__version__ = "$Revision: 0.0 $"
__credits__ = "This is part of www.capisuite.de; thanks to Gernot Hiller"

View File

@ -0,0 +1,216 @@
# -*- mode: python -*-
"""capisuite.config
Handling of configuration and job description files.
This file contains CSConfigParser, a configuration parser derived from the
default Python parsers, but adapted to the needs of CapiSuite.
Additionally, the class JobDescription is defined, which allows to read and
write special text files used to store meta data about incoming and outgoing
voice and fax jobs/calls.
"""
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright (c) 2004 by Hartmut Goebel"
__version__ = "$Revision: 0.0 $"
__credits__ = "This is part of www.capisuite.de; thanks to Gernot Hiller"
import os.path, types, string
# the name of the config file read by the scripts; see there for
# options and descriptions
pkgsysconfdir = "@pkgsysconfdir@"
configfile_fax = os.path.join(pkgsysconfdir, "fax.conf")
configfile_voice = os.path.join(pkgsysconfdir, "answering_machine.conf")
__all__ = ['configfile_fax', 'configfile_voice',
'CSConfigParser', 'JobDescription',
'readGlobalConfig', 'readDescription', 'createDescriptionFor',
'NoOptionError', 'NoGlobalSectionError']
# try to use RawConfigParser if available
try:
from ConfigParser import RawConfigParser as ConfigParser
except:
from ConfigParser import ConfigParser as ConfigParser
# capisuite stuff
import consts
#from exceptions import NoGlobalSectionError
#--- spezialied ConfigParser --#
class CSConfigParser(ConfigParser):
"""
Specialized version of Python ConfigParser:
- if no filenames are given, read the default config files
- values are automatically quoted on setting (if required) und
unquoted on reading
"""
def read(self, filenames):
"""Read configuration files given as filenames. If no names are given,
the default fax and voice files are read.
"""
if not filenames:
ConfigParser.read(self, configfile_fax)
ConfigParser.read(self, configfile_voice)
else:
ConfigParser.read(self, filenames)
def get(self, section, option):
"""Get the value of the option from the given section (w/o quot. marks)."""
value = ConfigParser.get(self, section, option)
if len(value) > 1 and value[0] == '"':
value = value[1:-1]
self.set(section, option, value)
return value
def getList(self, section, option):
"""Return the value of the option from the given section as a list.
Commas are used as separators and leading and trailing whitespaces are
removed from all items."""
return map(string.strip, self.get(section, option).split(','))
def getUser(self, user, option, default=None, fail=0):
"""Get an option from the user or global section.
The option is searched in the user's section and if not found
in the global section.
'config' the ConfigParser object containing the values
'user' user section to use, if empty only global section is read
'option' the name of the option to search for
Returns the value for this option or None if it's not found
"""
if self.has_option(user, option):
return self.get(user, option)
elif self.has_option('GLOBAL', option):
return self.get('GLOBAL', option)
elif fail:
raise NoOptionError(user, option)
else:
return default
def listUsers(self):
ul = [ u
for u in self.sections()
if u not in consts.__known_sections__ ]
return ul
def items(self, section):
"""
Return a dictionary (name, value) for each option in the section.
NB: This differs from ConfigParser.items() which returns a list of
tuples!
"""
items = {}
for key, value in ConfigParser.items(self, section):
if len(value) > 1 and value[0] == '"':
value = value[1:-1]
items[key] = value
return items
def set(self, section, option, value):
if isinstance(value, types.StringTypes):
if not value or value[0].isspace() or value[-1].isspace():
value = '"%s"' % value
ConfigParser.set(self, section, option, value)
class JobDescription:
def __init__(self, filename=None, defaults=None):
self._config = config = CSConfigParser(defaults)
config.add_section('GLOBAL')
if filename:
config.read(filename)
def add_section(self, section):
if 1: # PyChecker should _not_ take this a an abstract method
raise NotImplementedError
remove_section = add_section
def options(self): return self._config.options('GLOBAL')
def items(self): return self._config.items('GLOBAL')
def get(self, option): return self._config.get('GLOBAL', option)
def getint(self, option): return self._config.getint('GLOBAL', option)
def getfloat(self, option): return self._config.getfloat('GLOBAL', option)
def getboolean(self, option): return self._config.getboolean('GLOBAL', option)
def has_option(self, option): return self._config.has_option('GLOBAL', option)
def set(self, option, value): return self._config.set('GLOBAL', option, value)
def remove_option(self, option): return self._config.remove_option('GLOBAL',option)
def read(self, filenames): return self._config.read(filenames)
def readfp(self, fp, filename=None): return self._config.read(fp, filename)
def write(self, fp):
print >> fp, "# Description file for", self.get('filename')
print >> fp, "# This if for internal use of CapiSuite."
print >> fp, "# Only change if you know what you do!"
self._config.write(fp)
def x__getattr__(self, attr):
return getattr(self._config, attr)
def x__setattr__(self, attr, value):
setattr(self._config, attr, value)
###--- utility functions ---###
def readGlobalConfig(file=None):
"""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.
Returns the constructed CSConfigParser object
"""
config = CSConfigParser()
config.read(file)
if not config.has_section('GLOBAL'):
raise NoGlobalSectionError()
return config
def readDescription(jobfilename):
"""read (job) description file for received fax or voice
This function reads an INI-style description file which has prior
been written by writeDesc() .
jobfilename the job' filename (with extension .txt)
Returns the filename the data filename (with extension) and
content the description as a dictionary
"""
control = JobDescription()
control.read(jobfilename)
filename = control.get('filename')
description = control.items()
return filename, description
def createDescriptionFor(filename, **description):
"""write (job) 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 the CSConfigParser
instance. The data file name is used, the extension stripped and
replaced by .txt
filename the data filename (with extension),
content the description as a dictionary
"""
assert isinstance(description, dict)
descrname = os.path.splitext(filename)[0] + '.txt'
control = JobDescription()
control.set('filename', filename)
for key, value in description.items():
control.set(key, value)
descr = open(descrname, 'w')
control.write(descr)
descr.close()
return descrname

View File

@ -0,0 +1,38 @@
"""capisuite.consts
Constant defintions for the capisuite Python package.
"""
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright (c) 2004 by Hartmut Goebel"
__version__ = "$Revision: 0.0 $"
__credits__ = "This part of www.capisuite.de; thanks to Gernot Hiller"
SEND_Q = 'sendq'
RECEIVED_Q = 'received'
__known_sections__ = ('GLOBAL',
'Mail Fax Sent',
'Mail Fax Failed',
'Mail Fax Received',
'Mail Voice Received')
# capi return codes:
# 34D8 = Incompatible destination
# returncodes from call_voice and call_faxG3
CONNECTION_ESTABLISHED = 0
CONNECTION_TIMEOUT_EXCEEDED = 1
CONNECTION_FAILED_UNKNONW_REASON = 2
"""
reject causes:
1 = ignore call
2 = normal call clearing
3 = user busy
7 = incompatible destination
8 = destination out of order
0x34A9 = temporary failure
"""

433
src/capisuite-py/core.py Normal file
View File

@ -0,0 +1,433 @@
"""capisuite.core
This module exposes the built-in core of capisuite.
"""
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright (c) 2004 by Hartmut Goebel"
__version__ = "$Revision: 0.0 $"
__credits__ = "This part of www.capisuite.de; thanks to Gernot Hiller"
# _capisuite may only be imported when running within capisuite
try:
# import all capisuite symbols in the namespace "_capisuite.symbol"
import _capisuite
# now add symbols directly used by the scripts to our namespace
from _capisuite import log,error,SERVICE_VOICE,SERVICE_FAXG3,CallGoneError
except ImportError:
pass
#########
###
### ATTENTION: This interface is not yet stable. You may expect
### changes until capisuite 0.5 is released!
###
#########
class Capi:
def __init__(self, handle):
"""
handle: a capi handle as received from _capisuite (given to the idle
function as parameter)
"""
self._handle = handle
def call_voice(self, controller, call_from, call_to,
timeout, clir=0):
"""
Initiate an outgoing call with service voice and wait for
successful connection.
This will initiate an outgoing call and choose voice as
service, so you can use the audio commands (like audio_receive()
and audio_send()) with this connection. After this command has
finished, the call is connected successfully or the given
timeout has exceeded. The timeout is measured beginning at the
moment when the call is signalled (it's "ringing") to the
called party.
Parameters:
controller: ISDN controller ID to use
call_from: own number to use (string)
call_to: the number to call (string)
timeout: timeout in seconds to wait for connection establishment
clir: disable sending of own number (default=0, send number)
On success returns a call object; on failure returns an
error_code.
"""
call, result = _capisuite.call_voice(self.handle, controller,
call_from, call_to,
timeout, clir)
if result:
return result
return Call(call, SERVICE_VOICE, call_from, call_to)
def call_faxG3(self, controller, call_from, call_to,
timeout, stationID, headline, clir=0):
"""
Initiate an outgoing call with service faxG3 and wait for
successful connection.
This will initiate an outgoing call and choose fax group 3 as
service, so you can use the fax commands (like fax_send() and
fax_receive()) with this connection. After this command has
finished, the call is connected successfully or the given
timeout has exceeded. The timeout is measured beginning at the
moment when the call is signalled (it's "ringing") to the
called party.
Parameters:
controller: ISDN controller ID to use
call_from: own number to use (string)
call_to: the number to call (string)
timeout: timeout in seconds to wait for connection establishment
faxStationID: fax station ID (string)
faxHeadline: fax headline to print on every page (string)
clir: disable sending of own number (default=0, send number)
On success returns a call object; on failure returns an
error_code.
"""
call, result = _capisuite.call_faxG3(self.handle, controller,
call_from, call_to,
timeout, stationID, headline)
if result:
return result
return Call(call, SERVICE_FAXG3, call_from, call_to)
class Call:
def __init__(self, handle, service, call_from, call_to):
"""
handle: a call handle as received from _capisuite
NB: A Call instance is never True to ease testing results from
Capi.call_...()
"""
self._handle = handle
self.service = service
self.from_nr = call_from
self.to_nr = call_to
###--- python stuff --###
def __nonzero__(self):
# 'if Call()' must never be true to allow easier results from
# Capi.call_...()
return 0
def __str__(self):
return str(self._handle)
def __repr__(self):
# todo: add service, call_from, call_to
return repr(self._handle)
###--- general --###
def disconnect(self):
"""
Disconnect connection.
This will cause an immediate disconnection. It should be
always the last command in every flow of a script.
Returns a tuple of two result values. The first is the
disconnect cause of the physical connection, the second the
disconnect cause of the logical connection. See CAPI spec for
the logical causes and ETS 300 102-01 for the physical causes.
"""
result = _capisuite.disconnect(self._handle)
return result
def reject(self, rejectCause):
"""
Reject an incoming call.
If you don't want to accept an incoming call for any reason
(e.g. if it has a service or comes from a number you don't
want to accept), use this command. There are several reasons
you can give when rejecting a call. Some important ones are:
rejectCause: cause to signal when rejecting call. This may be one of
1 = ignore call
2 = normal call clearing
3 = user busy
7 = incompatible destination
8 = destination out of order
0x34A9 = temporary failure
"""
_capisuite.reject(self._handle, rejectCause)
def log(self, message, level):
"""
Log a connection dependent message.
This function writes a message to the CapiSuite log. As all messages
written with it are prefixed with the current call reference, you
should use it for connection-dependant messages (e.g. information about
handling *this* call).
If you want to log messages of general nature not associated with a
certain call (e.g. problem in reading configuration files), please use
core.log instead.
message: the log message to be written
level: parameter for CapiSuite log_level used (0=vital .. 3=debug info)
"""
_capisuite.log(message, level, self._handle)
###--- DTMF support --###
def enable_DTMF(self):
"""
Enable recognition of DTMF tones.
"""
_capisuite.enable_DTMF(self._handle)
def disable_DTMF(self):
"""
Disable recognition of DTMF tones.
"""
_capisuite.disable_DTMF(self._handle)
def read_DTMF(self, timeout, min_digits=0, max_digits=0):
"""
Read the received DTMF tones or wait for a certain amount of
them.
This function allows to just read in the DTMF tones which were
already received. But it also supports to wait for a certain
amount of DTMF tones if you want the user to always input some
digits at a certain step in your script.
You can specify how much DTMF tones you want in several ways -
see the parameter description. To just see if something was
entered before, use capisuite.read_DTMF(0). If you want to get
at least 1 and mostly 4 digits and want to wait 5 seconds for
additional digits, you'll use capisuite.read_DTMF(5,1,4).
Valid DTMF characters are '0'...'9','A'...'D' and two special
fax tones: 'X' (CNG), 'Y' (CED)
timeout: timeout in seconds after which reading is terminated;
only applied after min_digits have been read! (-1 =
infinite)
min_digits: minimum number of digits which must be read in ANY
case, i.e. timout doesn't count here (default: 0)
max_digits: maximum number of digits to read; aborts
immediately enough digits are read) (default:
0=infinite, i.e. wait until timeout is reached)
Returns a string containing the characters read.
"""
# todo: descibe what A...D means and where '#' and '*' go
return _capisuite.read_DTMF(self._handle, timeout,
min_digits, max_digits)
###--- voice calls ---###
def connect_voice (self, delay=0):
"""
Accept an incoming call and connect with voice service.
This will accept an incoming call and choose voice as service,
so you can use the audio commands (like audio_receive() and
audio_send()) with this connection. After this command has
finished, the call is connected successfully.
It's also possible to accept a call with some delay. This is
useful for an answering machine if you want to fetch a call
with your phone before your computer answers it.
delay: delay in seconds _before_ connection will be established
(default: 0=immediate connect)
"""
_capisuite.connect_voice(self._handle, delay)
def audio_receive(self, filename, timeout, silence_timeout=0,
exit_DTMF=0):
"""
Receive an audio file in a speech mode connection.
This functions receives an audio file. It can recognize
silence in the signal and timeout after a given period of
silence, after a general timeout or after the reception of a
DTMF signal.
If the recording was finished because of silence_timeout, the
silence will be truncated away.
If DTMF abort is enabled, the command will also abort
immediately if DTMF was received before it is called. This
allows to abort subsequent audio receive and send commands
with one DTMF signal w/o the need to check for received DTMF
after each command.
The connction must be in audio mode (by connect_voice()),
otherwise an exception will be caused.
The created file will be saved in bit-reversed A-Law format, 8
kHz mono. Use sox to convert it to a normal wav file.
filename: where to save the received message.
timeout: receive length in seconds (-1 = infinite).
silence_timeout: abort after x seconds of silence (default: no timeout)
exit_DTMF: abort sending when a DTMF signal is received (default: 0)
Returns duration of receiving in seconds.
"""
return _capisuite.audio_receive(self._handle, filename, timeout,
silence_timeout, exit_DTMF)
def audio_send(self, filename, exit_DTMF=0):
"""
Send an audio file in a speech mode connection.
This function sends an audio file, which must be in
bit-inversed A-Law format. Thus files can be created with eg.
sox using the suffix ".la". It supports abortion if DTMF
signal is received.
If DTMF abort is enabled, the command will also abort
immediately if DTMF was received before it is called. That
allows you to abort subsequent audio receive and send commands
with one DTMF signal w/o the need to check for received DTMF
after each command.
The connction must be in audio mode (use connect_voice()),
otherwise an exception will be caused.
filename: file to send
exit_DTMF: abort sending when a DTMF signal is received (default: 0)
Returns duration of send in seconds.
"""
return _capisuite.audio_send(self._handle, filename, exit_DTMF)
def switch_to_faxG3(self, faxStationID, faxHeadline):
"""
Switch a connection from voice mode to fax mode.
This will switch from voice mode to fax group 3 after you have
connected, so you can use the fax commands afterwards.
Attention: Not all ISDN cards or CAPI driver support this
command.
faxStationID: the station ID to use (string)
faxHeadline: the fax headline to use (string)
Returns a FaxInfo instance.
"""
faxInfo = _capisuite.switch_to_faxG3(self._handle,
faxStationID, faxHeadline)
self.service = SERVICE_FAXG3
if not faxInfo:
return FaxInfo()
return FaxInfo(*faxInfo)
###--- fax calls --###
def connect_faxG3(self, faxStationID, faxHeadline, delay=0):
"""
Accept an incoming call and connect with fax (analog, group 3) service.
This will accept an incoming call and choose fax group 3 as
service, so you can use the fax commands (like fax_receive())
with this connection. After this command has finished, the
call is connected successfully.
It's also possible to accept a call with some delay. This is
useful if eg. you want to have to fetch a call with your phone
before your computer answers it.
faxStationID: the station ID to use (string)
faxHeadline: the fax headline to use (string)
delay: delay in seconds _before_ connection will be established
(default: 0=immediate connect)
Returns a FaxInfo instance.
"""
faxInfo = _capisuite.connect_faxG3(self._handle, faxStationID,
faxHeadline, delay)
if not faxInfo:
return FaxInfo()
return FaxInfo(*faxInfo)
def fax_send(self, faxfilename):
"""
Send a fax in a fax mode connection.
This command sends an analog fax (fax group 3). It starts the
send and waits for the end of the connection. So it should be
the last command before disconnect().
The connction must be in fax mode (use capi.call_faxG3() or
call.switch_to_faxG3()), otherwise an exception will be caused.
The file to be sent must be in the Structured Fax File (SFF)
format.
faxfilename: file to send
"""
capisuite.fax_send(self._handle, faxfilename)
def fax_receive(self, filename):
"""
Receive a fax in a fax mode connection.
This command receives an analog fax (fax group 3). It starts
the reception and waits for the end of the connection. So it
should be the last command before disconnect().
The connction must be in fax mode (use capi.call_faxG3() or
call.switch_to_faxG3()), otherwise an exception will be caused.
The created file will be saved in the Structured Fax File
(SFF) format.
filename: where to save the received fax.
"""
_capisuite.fax_receive(self._handle, filename)
class FaxInfo:
def __init__(self, stationID='', rate=0, hiRes=0, format=0, numPages=0):
self.stationID = stationID
self.bitRate = rate
self.hiRes = hiRes
self.resolution = hiRes and "hiRes" or "loRes"
# cff: color fax; sff: normal black-and-white fax
self.format = format and 'cff' or 'sff'
self.color = format and 'color' or 'b&w'
self.numPages = numPages
def as_dict(self):
d = {}
for a in ('stationID', 'bitRate', 'resolution',
'hiRes', 'format', 'color', 'numPages'):
d[a] = getattr(self, a)
return d
# implemented in _capisuite:
#
#def error(...):
# pass
#def log(...):
# pass

View File

@ -0,0 +1,34 @@
"""
Exceptions hierarchy for capisuite
Exception
+ MissingConfigEntry
+ FaxError
+ JobError
+ InvalidJob
+ JobLockedError
"""
class Error(Exception): pass
class FaxError(Exception): pass
class VoiceError(Exception): pass
class JobError(Error):
# todo: distinguish fax/voice
def __init__(self, jobnum, jobfile):
Error.__init__(self)
self.jobnum = jobnum
self.jobfile = jobfile
class JobLockedError(JobError): pass
class InvalidJob(JobError): pass
from ConfigParser import NoOptionError, NoSectionError, Error
class NoGlobalSectionError(NoSectionError):
"""Raised when the GLOBAL section is missing
in a configuration file."""
def __init__(self):
Error.__init__(self, "Invalid config file: section GLOBAL missing")
self.section = 'GLOBAL'

252
src/capisuite-py/fax.py Normal file
View File

@ -0,0 +1,252 @@
"""capisuite.fax
Module for fax interfacing of capisuite.
Most functions deal about creating and manipulationg job control
files. To actually send the fax out, call sendfax(). This is normaly
done by the idle-script.
"""
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright (c) 2004 by Hartmut Goebel"
__version__ = "$Revision: 0.0 $"
__credits__ = "This part of www.capisuite.de; thanks to Gernot Hiller"
import os, os.path, time, re, errno
from types import ListType, TupleType
# capisuite stuff
import fileutils
from capisuite.config import JobDescription, createDescriptionFor
from capisuite.exceptions import InvalidJob, JobLockedError
from capisuite.consts import *
_job_pattern = re.compile("fax-([0-9]+)\.txt")
###---- Utility functions ---###
def _userQ(config, user, Q):
userdir= config.get('GLOBAL', "fax_user_dir")
# todo: enable this, iff using config.getUser()
#if not userdir:
# raise NoOptionError('', 'fax_user_dir')
return os.path.abspath(os.path.join(userdir, user, Q))
###---- Job handling ---###
def _createSendJob(user, filename, **controlinfo):
"""
Create a control file for a fax to be send. The control file is
written to the same directory as the filename.
NB: This function does not care about the control information to
be written to the controlfile. This is to keep the definition of
what is required for sending the fax out of here. It's up to the
application layer to ensure correct parameters.
"""
controlfile = createDescriptionFor(filename, **controlinfo)
fileutils._setProtection(user, 0600, filename, controlfile)
def createReceivedJob(user, filename, call_from, call_to, causes, **kwargs):
"""
Create a file description for a received fax. The description file
is written to the same directory as the filename.
"""
controlinfo = {
'call_from': call_from,
'call_to': call_to,
'time': time.ctime(),
'cause': "0x%x/0x%x" % causes
}
controlinfo.update(kwargs)
controlfile = createDescriptionFor(filename, **controlinfo)
fileutils._setProtection(user, 0600, filename, controlfile)
def enqueueJob(config, user, infiles, converter, **controlinfo):
# todo: enable color-suffixes
"""
Enqueue a file into the fax send queue.
This sets the spool-filename and calls 'converter(infile,
spoolfile)' to convert the infile into a faxfile. The later has to
be written tp 'spoolfile'. If the conversion succeeds, the
appropriate job controlfile is written .
NB: This function does not care about the control information to
be written to the controlfile. This is to keep the definition of
what is required for sending the fax out of here. It's up to the
application layer to ensure correct parameters.
@param user
@param infile
@param converter a function for converting the infile into a fax file
(will be called covnerter(infile, faxfile)
@controlinfo the fax job description
"""
assert isinstance(infiles, (ListType, TupleType))
# ensure some required entries (which may have defaults) exist
if not controlinfo.has_key('tries'):
controlinfo['tries'] = 0
if not controlinfo.has_key('starttime'):
controlinfo['starttime'] = time.ctime(time.time())
sendQ = _userQ(config, user, SEND_Q)
jobnum, faxname = fileutils.uniqueName(sendQ, "fax", "sff")
converter(infiles, faxname)
_createSendJob(user, faxname, **controlinfo)
return jobnum
def moveJob(controlfile, newdir, user=None):
# todo: important: update 'filenmae' in job description!
control = JobDescription(controlfile)
filename = control.get('filename')
cname = os.path.split(controlfile)[1]
fname = os.path.split(filename)[1]
if not user:
cname = os.path.join(newdir, cname)
fname = os.path.join(newdir, fname)
else:
cname = os.path.join(newdir, "%s-%s" % (user, cname))
fname = os.path.join(newdir, "%s-%s" % (user, fname))
# move controlfile to keep owner and protection!
os.rename(controlfile, cname)
os.rename(filename, fname)
# update controlfile
control.set('filename', fname)
control.write(open(cname, 'w'))
return control
def abortJob(controlfile):
"""
Abort a fax job defined by it's controlfile.
This will remove the job from respective queue and delete both the
controlfile and the file defined in the controlfile.
"""
# todo: security: May this be missused for deleting other users files?
# todo: security: should ensure controlfile and filename are in the same
# directory
lockname = fileutils.lockname(controlfile)
if not os.access(controlfile, os.W_OK):
raise InvalidJob(None, controlfile)
try:
lock = fileutils._getLock(lockname, blocking=0)
except IOError, err:
if err.errno in (errno.EACCES, errno.EAGAIN):
raise JobLockedError(None, controlfile)
else:
raise
else:
try:
control = JobDescription(controlfile)
filename = control.get('filename')
os.unlink(filename)
os.unlink(controlfile)
finally:
fileutils._releaseLock(lock)
def abortUserJob(config, user, jobnum):
"""
Abort a fax send job defined by username and job number.
This will remove the job from respective queue and delete both the
controlfile and the file defined in the controlfile.
"""
sendQ = _userQ(config, user, SEND_Q)
controlfile = fileutils.controlname(os.path.join(sendQ,
"fax-%03i" % jobnum))
abortJob(controlfile)
###---- Queue handling ---###
def getQueueFiles(config, user):
"""
Generate a list of all fax jobs in the send queue.
Result is a list of (job-number, controlfile) tuples, where
'controlfile' is an absolut paht.
"""
sendQ = _userQ(config, user, SEND_Q)
for filename in os.listdir(sendQ):
m = _job_pattern.match(filename)
if m:
yield (int(m.group(1)), # job number
os.path.join(sendQ, filename))
def getQueue(config, user):
"""
Generate a list of all fax entries in the send queue.
Result is a list of (job-number, description) tuples, where
'description' is the content of the job's controlfile as a
dictionary.
"""
for num, controlfile in getQueueFiles(config, user):
jobDesc = JobDescription(controlfile).items()
yield (num, jobDesc)
def getQueueDetails(config, user):
"""
Return a list of interesting details about the fax jobs in the
send queue.
Result is a list of tuples of (job-num, addresse/dialstring,
tries, starttime, subject), where starttime it the time when the
next try for sending is undertaken and addresse/dialstring is the
adresses (if not empty) or the dialstring otherwise).
"""
jobs = []
for num, jobDesc in getQueue(config, user):
jobs.append( (
num,
jobDesc['addressee'] or jobDesc['dialstring'],
jobDesc['tries'],
jobDesc['starttime'],
jobDesc['subject'],
))
return jobs
###--- Send/Receive Fax ---###
def sendfax(config, user, capi, faxfile,
outgoing_num, dialstring, stationID=None, headline=None):
"""
Send a fax out via the capi.
"""
import capisuite.core as core
controller = config.getint('GLOBAL', "send_controller")
timeout = int(config.getUser(user, "outgoing_timeout"))
# get defaults for stationID and headline from config
if not stationID:
stationID = config.getUser(user, "fax_stationID")
if not stationID:
core.error("Warning: fax_stationID for user %s not set" %user)
if not headline:
headline = config.getUser(user, "fax_headline")
try:
call = core.call_faxG3(
capi, controller, outgoing_num,
dialstring, timeout, stationID, headline)
if call:
# an errror occured
return call
call.fax_send(faxfile)
return call.disconnect()
except core.CallGoneError:
return call.disconnect()

View File

@ -0,0 +1,158 @@
"""capisuite.fileutils
File handling utility function.
"""
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright (c) 2004 by Hartmut Goebel"
__version__ = "$Revision: 0.0 $"
__credits__ = "This part of www.capisuite.de; some ideas taken from Gernot Hiller"
import fcntl, os, re, errno
from types import IntType
class UnknownUserError(KeyError): pass
class LockTakenError(Exception): pass
def lockname(path):
return "%s.lock" % os.path.splitext(path)[0]
def controlname(path):
return "%s.txt" % os.path.splitext(path)[0]
def _getLock(lockname_=None, forfile=None, blocking=0):
if forfile:
lockname_ = lockname(forfile)
elif not lockname_:
raise ValueError, lockname_
lockfile = open(lockname_, "w")
try:
if blocking:
fcntl.lockf(lockfile, fcntl.LOCK_EX)
else:
fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError, err: # can't get the lock
if err.errno in (errno.EACCES, errno.EAGAIN):
lockfile.close()
raise LockTakenError
else:
raise
return (lockname_, lockfile)
def _releaseLock((lockname, lockfile)):
fcntl.lockf(lockfile, fcntl.LOCK_UN)
lockfile.close()
try:
os.unlink(lockname)
except OSError, err:
# as we don't hold the lock any more, the other thread can be quicker
# in deleting than we; this doesn't harm, so ignore it
if (err.errno!=2):
raise
def _setProtection(user, mode=0600, *files):
import pwd
try:
userdata = pwd.getpwnam(user)
except KeyError:
raise UnknownUserError(user)
print files
if os.getuid() == 0:
# running as root, change ownership
for f in files:
os.chmod(f, mode)
os.chown(f, userdata.pw_uid, userdata.pw_gid)
else:
for f in files:
os.chmod(f, mode)
os.chown(f, os.getuid(), userdata.pw_gid)
def _mkuserdir(user, parrentdir, *dirs):
import pwd
try:
userdata = pwd.getpwnam(user)
except KeyError:
raise UnknownUserError(user)
path = parrentdir
for d in dirs:
path = os.path.join(path, d)
# todo: use os.path.exists?
if not os.access(path, os.F_OK):
os.mkdir(path, 0700)
os.chown(path, userdata.pw_uid, userdata.pw_gid)
return path
###--- counter files ---###
def readCounter(default=0, *fileparts):
assert isinstance(default, IntType)
filename = os.path.join(*fileparts)
if os.path.exists(filename):
lastfile = open(filename, "r")
count = int(lastfile.readline())
lastfile.close()
return count
else:
return default
def writeCounter(count, *fileparts):
assert isinstance(count, IntType)
filename = os.path.join(*fileparts)
lastfile = open(filename, "w")
print >> lastfile, count
lastfile.close()
###--- ... ---###
def __makeCountedFilePattern(basename, suffix):
# todo: group should be digits only
return re.compile("%s-([0-9]+)\.%s" % (re.escape(basename),
re.escape(suffix)))
# @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 job number, new file name
def uniqueName(directory, basename, suffix):
nextfile = "%s-nextnr" % os.path.join(directory, basename)
lock = _getLock(os.path.join(directory,"cs_lock"), blocking=1)
try:
nextnum = readCounter(-10, nextfile)
if nextnum < 0:
# search for next free sequence number
pattern = __makeCountedFilePattern(basename, suffix)
numbers = [0]
for f in os.listdir(directory):
m = pattern.match(f)
if m:
numbers.append(int(m.group(1)))
# take number of last file and increase it by one
nextnum = max(numbers)+1
writeCounter(nextnum+1, nextfile)
except:
_releaseLock(lock)
raise
else:
_releaseLock(lock)
newname = "%s-%03i.%s" % (os.path.join(directory,basename),
nextnum, suffix)
return nextnum, newname

View File

@ -0,0 +1,216 @@
# -*- python -*-
# .pycheckrc file created by PyChecker v0.8.13 @ Wed Feb 25 14:03:29 2004
#
# It should be placed in your home directory (value of $HOME).
# If $HOME is not set, it will look in the current directory.
#
# unused imports
importUsed = 1
# unused imports from __init__.py
packageImportUsed = 1
# module imports itself
reimportSelf = 1
# reimporting a module
moduleImportErrors = 1
# module does import and from ... import
mixImport = 1
# unused local variables, except tuples
localVariablesUsed = 1
# all unused local variables, including tuples
unusedLocalTuple = 0
# all unused class data members
membersUsed = 0
# all unused module variables
allVariablesUsed = 0
# unused private module variables
privateVariableUsed = 1
# report each occurrence of global warnings
reportAllGlobals = 0
# functions called with named arguments (like keywords)
namedArgs = 0
# Attributes (members) must be defined in __init__()
onlyCheckInitForMembers = 0
# Subclass.__init__() not defined
initDefinedInSubclass = 0
# Baseclass.__init__() not called
baseClassInitted = 1
# Subclass needs to override methods that only throw exceptions
abstractClasses = 1
# Return None from __init__()
returnNoneFromInit = 1
# unreachable code
unreachableCode = 0
# a constant is used in a conditional statement
constantConditions = 1
# 1 is used in a conditional statement (if 1: or while 1:)
constant1 = 0
# check if iterating over a string
stringIteration = 1
# Calling data members as functions
callingAttribute = 0
# class attribute does not exist
classAttrExists = 1
# First argument to methods
methodArgName = 'self'
# First argument to classmethods
classmethodArgNames = ['cls', 'klass']
# unused method/function arguments
argumentsUsed = 1
# unused method/function variable arguments
varArgumentsUsed = 1
# ignore if self is unused in methods
ignoreSelfUnused = 0
# check if overridden methods have the same signature
checkOverridenMethods = 1
# check if __special__ methods exist and have the correct signature
checkSpecialMethods = 1
# check if function/class/method names are reused
redefiningFunction = 1
# check if using unary positive (+) which is usually meaningless
unaryPositive = 1
# check if modify (call method) on a parameter that has a default value
modifyDefaultValue = 1
# check if variables are set to different types
inconsistentTypes = 0
# check if unpacking a non-sequence
unpackNonSequence = 1
# check if unpacking sequence with the wrong length
unpackLength = 1
# check if raising or catching bad exceptions
badExceptions = 1
# check if statement appears to have no effect
noEffect = 1
# check if using (expr % 1), it has no effect on integers and strings
modulo1 = 1
# check if using (expr is const-literal), doesn't always work on integers and strings
isLiteral = 1
# check consistent return values
checkReturnValues = 1
# check if using implict and explicit return values
checkImplicitReturns = 1
# check that attributes of objects exist
checkObjectAttrs = 1
# various warnings about incorrect usage of __slots__
slots = 1
# using properties with classic classes
classicProperties = 1
# check if __slots__ is empty
emptySlots = 1
# check if using integer division
intDivide = 1
# check if local variable shadows a global
shadows = 1
# check if a variable shadows a builtin
shadowBuiltins = 1
# check if input() is used
usesInput = 1
# check if the exec statement is used
usesExec = 0
# ignore warnings from files under standard library
ignoreStandardLibrary = 0
# ignore warnings from the list of modules
blacklist = ['Tkinter', 'wxPython', 'gtk', 'GTK', 'GDK']
# ignore global variables not used if name is one of these values
variablesToIgnore = ['__version__', '__warningregistry__', '__all__',
'__credits__', '__test__', '__author__',
'__email__', '__revision__', '__copyright__']
# ignore unused locals/arguments if name is one of these values
unusedNames = ['_', 'empty', 'unused', 'dummy']
# ignore use of deprecated modules/functions
deprecated = 1
# maximum lines in a function
maxLines = 200
# maximum branches in a function
maxBranches = 50
# maximum returns in a function
maxReturns = 10
# maximum # of arguments to a function
maxArgs = 10
# maximum # of locals in a function
maxLocals = 40
# maximum # of identifier references (Law of Demeter)
maxReferences = 5
# no module doc strings
noDocModule = 0
# no class doc strings
noDocClass = 0
# no function/method doc strings
noDocFunc = 0
# print internal checker parse structures
printParse = 0
# turn on debugging for checker
debug = 0
# { 'module1': 'no-namedargs maxlines=0',
# 'module2.my_func': 'argsused',
# 'module3.my_class': 'no-initreturn', }
suppressions = {
'config.NoGlobalSectionError.__init__': 'no-callinit',
}

176
src/capisuite-py/voice.py Normal file
View File

@ -0,0 +1,176 @@
"""capisuite.voice
Module for voice interfacing and interactions of capisuite.
"""
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright (c) 2004 by Hartmut Goebel"
__version__ = "$Revision: 0.0 $"
__credits__ = "This part of www.capisuite.de; thanks to Gernot Hiller"
import os, re, errno
# capisuite stuff
import fileutils
from capisuite.config import JobDescription, createDescriptionFor
from capisuite.exceptions import InvalidJob, JobLockedError
#from capisuite.consts import *
_job_pattern = re.compile("voice-([0-9]+)\.txt")
_la_pattern = re.compile("voice-([0-9]+)\.la")
def _userQ(config, user, Q):
userdir= config.get('GLOBAL', "voice_user_dir")
# todo: enable this, iff using config.getUser()
#if not userdir:
# raise NoOptionError('', 'fax_user_dir')
return os.path.abspath(os.path.join(userdir, user, Q))
def getAudio(config, user, filename):
"""
Search for an audio file first in user_dir, than in audio_dir
'config' is the ConfigParser object containing the configuration,
'user' the name of the user, 'filename' the filename of the wave
file.
Returns the found file with full path
"""
userdir = config.get('GLOBAL', "voice_user_dir")
userdir = os.path.join(userdir, user)
if config.has_option('GLOBAL', "user_audio_files") and \
config.getboolean('GLOBAL', "user_audio_files") and \
os.access(os.path.join(userdir, filename),os.R_OK):
return os.path.join(userdir, filename)
else:
systemdir = config.get('GLOBAL', "audio_dir")
return os.path.join(systemdir, filename)
def getNumberFiles(number, gender="-"):
number = str(number)
if number == "-" or number == "??":
# "??" is needed for backward compatibility to versions <= 0.4.1a
yield 'unbekannt'
elif gender != "-" and number in ("1", "01"):
if gender in ("n", "m"):
yield 'ein'
else:
yield 'eine'
elif len(number) == 2 and number[0] != "0":
digit10, digit01 = number
if digit10 == "1" or digit01 == "0":
# for 10, 11...19, 20, 30, ... we have seperate voice files
yield number
else:
if digit01 == "1":
yield 'ein'
else:
yield digit01
yield 'und'
yield '%s0' % digit10
else:
for digit in list(number):
yield digit
# @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.
# An input of "-" produces the word "unbekannt" (unknown)
#
# @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
# @param gender if the number is used in connection with a singular noun ("f" --> "eine Nachricht")
def sayNumber(config, user, call, number, gender="-"):
for f in getNumberFiles(number, gender=gender):
say(config, user, call, "%s.la" % f)
def say(config, user, call, *files):
for f in files:
call.audio_send(getAudio(config, user, f),1)
###---- Queue handling ---###
def getInquiryCounter(config, user):
return fileutils.readCounter(-1,
_userQ(config, user, "received"),
"last_inquiry")
def setInquiryCounter(config, user, count):
return fileutils.writeCounter(count,
_userQ(config, user, "received"),
"last_inquiry")
def getQueueFiles(config, user):
"""
Generated a list of all Queue files, where each entry consists of
a tuple (job-number, filename).
filename is an absolute path
"""
receivedQ = _userQ(config, user, "received")
for filename in os.listdir(receivedQ):
m = _job_pattern.match(filename)
if m:
yield (m.group(1), # job number
filename)
def abortJob(controlfile):
"""
Abort a fax job defined by it's controlfile.
This will remove the job from respective queue and delete both the
controlfile and the file defined in the controlfile.
"""
# todo: security: May this be missused for deleting other users files?
# todo: security: should ensure controlfile and filename are in the same
# directory
lockname = fileutils.lockname(controlfile)
if not os.access(controlfile, os.W_OK):
raise InvalidJob(None, controlfile)
try:
lock = fileutils._getLock(lockname, blocking=0)
except IOError, err:
if err.errno in (errno.EACCES, errno.EAGAIN):
raise JobLockedError(None, controlfile)
else:
raise
else:
try:
control = JobDescription(controlfile)
filename = control.get('filename')
os.unlink(filename)
os.unlink(controlfile)
finally:
fileutils._releaseLock(lock)
def createReceivedJob(user, filename, call_from, call_to, causes):
"""
Create a file description for a received fax. The description file
is written to the same directory as the filename.
"""
import time
control = {
'call_from': call_from,
'call_to': call_to,
'date': time.ctime(),
'cause': "0x%x/0x%x" % causes,
# we return this dict, thus set the filename here, too
'filename': filename,
}
controlfile = createDescriptionFor(**control)
fileutils._setProtection(user, 0600, filename, controlfile)
return control

View File

@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.8.3 from Makefile.am.
# Makefile.in generated by automake 1.9.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@ -46,9 +46,9 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
LIBRARIES = $(noinst_LIBRARIES)
AR = ar
ARFLAGS = cru
LIBRARIES = $(noinst_LIBRARIES)
libccmodules_a_AR = $(AR) $(ARFLAGS)
libccmodules_a_LIBADD =
am_libccmodules_a_OBJECTS = audiosend.$(OBJEXT) callmodule.$(OBJEXT) \
@ -60,14 +60,6 @@ libccmodules_a_OBJECTS = $(am_libccmodules_a_OBJECTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/audioreceive.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/audiosend.Po ./$(DEPDIR)/callmodule.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/calloutgoing.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/connectmodule.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/disconnectmodule.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/faxreceive.Po ./$(DEPDIR)/faxsend.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/readDTMF.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/switch2faxG3.Po
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
CXXLD = $(CXX)
@ -144,6 +136,8 @@ am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
datadir = @datadir@
@ -247,16 +241,14 @@ distclean-compile:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
.cpp.obj:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
uninstall-info-am:
@ -280,9 +272,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$tags$$unique" \
|| $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique; \
fi
ctags: CTAGS
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
@ -356,7 +350,7 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f $(CONFIG_CLEAN_FILES)
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"

11
src/modules/SConscript Normal file
View File

@ -0,0 +1,11 @@
# -*- python -*-
Import('env')
libmodules = env.StaticLibrary('ccmodules', source = Split("""
audiosend.cpp callmodule.cpp audioreceive.cpp faxreceive.cpp
connectmodule.cpp switch2faxG3.cpp readDTMF.cpp calloutgoing.cpp
disconnectmodule.cpp faxsend.cpp
"""))
Return('libmodules')