- 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:
parent
b75a6e0f54
commit
a22481c4fc
1
AUTHORS
1
AUTHORS
|
@ -1 +1,2 @@
|
|||
Gernot Hillier <gernot@hillier.de>
|
||||
Hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
|
|
113
ChangeLog
113
ChangeLog
|
@ -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.
|
||||
|
|
58
Makefile.in
58
Makefile.in
|
@ -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
25
NEWS
|
@ -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
|
||||
|
|
|
@ -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'),
|
||||
)
|
|
@ -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
|
||||
"""
|
|
@ -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)
|
|
@ -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
15
TODO
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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'),
|
||||
)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'),
|
||||
)
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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 ###################################
|
||||
###############################################################################
|
||||
|
|
367
scripts/idle.py
367
scripts/idle.py
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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')))
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
)
|
|
@ -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"
|
||||
|
|
|
@ -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')
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# -*- python -*-
|
||||
|
||||
Import('env')
|
||||
libback = env.StaticLibrary('ccbackend', source = Split("""
|
||||
capi.cpp connection.cpp
|
||||
"""))
|
||||
|
||||
Return('libback')
|
|
@ -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
|
|
@ -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:
|
|
@ -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)
|
|
@ -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"
|
|
@ -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
|
|
@ -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
|
||||
"""
|
||||
|
|
@ -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
|
|
@ -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'
|
|
@ -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()
|
|
@ -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
|
|
@ -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',
|
||||
}
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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')
|
Loading…
Reference in New Issue