Incoming adv: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
| (12 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
[http://www.neobiker.de/ftp/pub/isdn/ Download ISDN Files] | |||
File: '''incoming_adv.py''' | File: '''incoming_adv.py''' | ||
<pre> | <pre> | ||
| Zeile 4: | Zeile 6: | ||
# -------------------------------------------------------- | # -------------------------------------------------------- | ||
# copyright : (c) 2007 by Neobiker | # copyright : (c) 2007 by Neobiker | ||
# Version : $Revision: 1.2 $ | |||
# Date : $Date: 2009/04/22 18:14:20 $ | |||
# | # | ||
# original script: | # original script : incoming.py | ||
# copyright : (C) 2002 by Gernot Hillier | # copyright : (C) 2002 by Gernot Hillier | ||
# email : gernot@hillier.de | # email : gernot@hillier.de | ||
# | # | ||
# This program is free software; you can redistribute it and/or modify | # This program is free software; you can redistribute it and/or modify | ||
| Zeile 17: | Zeile 20: | ||
# general imports | # general imports | ||
import datetime,time,os,re,string,pwd | import locale,datetime,time,os,re,string,pwd,commands | ||
# CapiSuite imports | # CapiSuite imports | ||
import capisuite,cs_helpers | import capisuite,cs_helpers | ||
# @ | # phonebook file (neobiker's _adv) | ||
configfile_phonebook="/etc/capisuite/phonebook.conf" | |||
configfile_fax="/etc/capisuite/fax.conf" | |||
configfile_voice="/etc/capisuite/answering_machine.conf" | |||
# read_Configfiles () | |||
# 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 read_Configfiles(file=""): | |||
import ConfigParser | |||
config=ConfigParser.ConfigParser() | |||
if (file==""): | |||
config.readfp(open(configfile_fax)) | |||
config.readfp(open(configfile_voice)) | |||
try: | |||
config.readfp(open(configfile_phonebook)) | |||
except: | |||
pass | |||
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 | |||
# read_phonebook () | |||
# check if number is listed in phonebook | |||
# | |||
# read_phonebook (number): | |||
# number: tel number | |||
# | |||
# return: name | |||
def read_phonebook (number): | |||
"""read_phonebook () read name of number if listed in phonebook""" | |||
try: | |||
fd=open(configfile_phonebook) | |||
except: | |||
return number | |||
for line in fd: | |||
line=line.strip() | |||
if (not line) | line.startswith("#") | (not '=' in line): | |||
continue | |||
match=re.match(r'(.*?)\s*=\s*(.*)$', line) | |||
name=match.group(1) | |||
details=re.sub('[\s+()-]','',match.group(2)) | |||
if re.sub('[\s+()-]','',number) in details.split(','): | |||
fd.close() | |||
return name | |||
fd.close() | |||
return number | |||
# ---------------------------------------- | |||
# get_fax_pages () | |||
# get number of pages of the fax file | |||
# | |||
# get_fax_pages (fax): | |||
# fax: sff fax file | |||
# | |||
# return: pages | |||
def get_fax_pages (fax, call): | |||
"""get_fax_pages () get fax pages""" | |||
pages = "" | |||
cmd = "sfftobmp -t " + fax + " -o /dev/null 2>&1 | grep 'page(s)'" | |||
try: | |||
pages = commands.getoutput(cmd).split()[5] | |||
except: | |||
capisuite.log("Error: get_fax_pages() failed",1,call) | |||
capisuite.log("Error: "+cmd+" failed",1,call) | |||
return pages | |||
# ---------------------------------------- | |||
# check_number_online () | |||
# online reverse lookup | |||
# | |||
# check_number_online (number): | |||
# number: tel number | |||
# | |||
# return: name, details | |||
def check_number_online (number,call): | |||
"""check_number_online () check if number if listed in online phonebook""" | |||
name = details = "" | |||
cmd = "tbident.sh " + number + " 2>/dev/null" | |||
try: | |||
number, name, details = commands.getoutput(cmd).split("|") | |||
except: | |||
capisuite.log("Error: check_number_online() failed",1,call) | |||
capisuite.log("Error: "+cmd+" failed",1,call) | |||
return name, details | |||
# ---------------------------------------- | |||
# check_date () | |||
# check if user program date fits actual date | |||
# | |||
# check_date (pdate, adate): | |||
# pdate: program date | |||
# adate: actual date | |||
# | # | ||
# | # return: True/False if 'pdate' fits 'adate' | ||
def check_date (pdate,adate): | def check_date (pdate, adate): | ||
"""check_date() check if actual date fits user program date""" | """check_date() check if actual date fits user program date""" | ||
if pdate == '*': | |||
# a '*' fits always... | |||
# | if pdate == '*': return True | ||
# initialise day.month.year from actual date if field is undefined | |||
# update pdate fields day.month.year as defined in user program | |||
idate=pdate.split('.') | idate=pdate.split('.') | ||
pdate=adate[:] | pdate=adate[:] | ||
for i in range(len(idate)): | for i in range(len(idate)): | ||
if idate[i] and idate[i] != '*': pdate[i] = int(idate[i]) | if idate[i] and idate[i] != '*': | ||
pdate[i] = int(idate[i]) | |||
return pdate == adate; | |||
# | # ---------------------------------------- | ||
# check_date_section () | |||
# check if date is listed in date section | |||
# | |||
# check_date_section (config, dsect, adate): | |||
# config: config file | |||
# dsect: date section list of dates | |||
# adate: actual date | |||
# | # | ||
# | # return: True/False if 'adate' is listed/not listed | ||
def check_date_section (dsect,adate | def check_date_section (config, dsect, adate): | ||
"""check_date_section () check if date is listed in date section""" | """check_date_section () check if date is listed in date section""" | ||
# check all entries in date section | |||
# every entry (item) can be a list of dates | |||
for i in range(len(config.items(dsect))): | for i in range(len(config.items(dsect))): | ||
for pdate in config.items(dsect)[i][1].split(','): | for pdate in config.items(dsect)[i][1].split(','): | ||
if check_date(pdate,adate): | if check_date(pdate, adate): | ||
return True | return True | ||
return False | return False | ||
# | # ---------------------------------------- | ||
# prog_time () | |||
# check if actual time fits user program time interval | |||
# | # | ||
# | # prog_time (ptime, atime): | ||
# | # ptime: program time intervall | ||
# atime: actual time | |||
# | |||
# return: True/False if 'ptime' fit's actual time | |||
def prog_time (ptime,atime): | def prog_time (ptime, atime): | ||
"""prog_time() check if actual time fits user program time interval""" | """prog_time() check if actual time fits user program time interval""" | ||
if ptime == '*': | |||
# a '*' fits always... | |||
if ptime == '*': return True | |||
# From: time intervall start | # From: time intervall start | ||
f0=ptime.split('-')[0] + ':00' | # From: minutes are optional, set default to :00 | ||
f=[int(f0.split(':')[0]), int(f0.split(':')[1])] | f0 = ptime.split('-')[0] + ':00' | ||
f = [int(f0.split(':')[0]), int(f0.split(':')[1])] | |||
# To: time intervall end | # To: time intervall end | ||
t0=ptime.split('-')[1] + ':00' | # To: minutes are optional, set default to :00 | ||
t=[int(t0.split(':')[0]), int(t0.split(':')[1])] | t0 = ptime.split('-')[1] + ':00' | ||
# actual time | t = [int(t0.split(':')[0]), int(t0.split(':')[1])] | ||
# test actual time interval: From < PTime < To? | |||
# 1.st test: f < t (means: t < 23:59) | # 1.st test: f < t (means: t < 23:59) | ||
# 2.nd test: f > t (means: t >= 00:00 -> on next day!) | # 2.nd test: f > t (means: t >= 00:00 -> on next day!) | ||
| Zeile 79: | Zeile 209: | ||
return not (t <= atime <= f) | return not (t <= atime <= f) | ||
# | # ---------------------------------------- | ||
# prog_wday() | |||
# check if weekday is listed in user program | |||
# | |||
# prog_wday (pwday, wday): | |||
# pwday program weekday | |||
# wday actual weekday | |||
# | # | ||
# | # return: True/False if 'wday' is listed/not listed | ||
def prog_wday (pwday,wday): | def prog_wday (pwday, wday): | ||
"""prog_wday() check if weekday is listed in user program""" | """prog_wday() check if weekday is listed in user program""" | ||
if pwday == '*': | |||
# a '*' fits always... | |||
if pwday == '*': return True | |||
wd={'MO': 0, 'DI': 1, 'MI': 2, 'DO': 3, 'FR': 4, 'SA': 5, 'SO': 6, \ | wd={'MO': 0, 'DI': 1, 'MI': 2, 'DO': 3, 'FR': 4, 'SA': 5, 'SO': 6, \ | ||
'TU': 1, 'WE': 2, 'TH': 3, 'SU': 6} | 'TU': 1, 'WE': 2, 'TH': 3, 'SU': 6} | ||
# | return wday == wd[pwday.upper()] | ||
# ---------------------------------------- | |||
# check_caller_section () | |||
# check if call_from is listed in user caller-section | |||
# | |||
# check_caller_section (config, csect, call_from): | |||
# config: config file | |||
# csect: caller-section | |||
# call_from: caller number | |||
# | # | ||
# | # return: True/False if 'call_from' is listed/not listed | ||
def check_caller_section ( | def check_caller_section (config, csect, call_from): | ||
"""check_caller_section () check if call_from is listed in callers section""" | """check_caller_section () check if call_from is listed in callers section""" | ||
# check all entries in caller section | |||
# every entry (item) can be a list of numbers | |||
# standardise numbers (delete blanks etc.) | |||
for i in range(len(config.items(csect))): | for i in range(len(config.items(csect))): | ||
for j in config.items(csect)[i][1].split(','): | for j in config.items(csect)[i][1].split(','): | ||
| Zeile 104: | Zeile 253: | ||
return False | return False | ||
# | # ---------------------------------------- | ||
# check_caller() | |||
# check if 'call_from' is listed in any callers list/section | |||
# | # | ||
# | # check_caller (config, callers, call_from): | ||
# config: config file | |||
# callers: list of numbers or sections | |||
# call_from: caller number | |||
# | |||
# return: True/False if 'call_from' is listed/not listed | |||
def check_caller (config, callers, call_from): | |||
"""check_caller () check if caller_from matches group entries/section""" | |||
# a '*' fits always... | |||
if callers == '*': return True | |||
# check all entries in group entry (caller numbers) | |||
# every entry (item) can be a separate section | |||
# correct format of numbers (delete blanks etc.) | |||
for i in range(len(callers.split(','))): | for i in range(len(callers.split(','))): | ||
pcaller=re.sub('[\s+()-]','',callers.split(',')[i]) | pcaller = re.sub('[\s+()-]', '', callers.split(',')[i]) | ||
if pcaller in config.sections(): | if pcaller in config.sections(): | ||
if check_caller_section( | if check_caller_section(config, pcaller, call_from): | ||
return True | return True | ||
elif call_from == pcaller: | elif call_from == pcaller: | ||
| Zeile 121: | Zeile 282: | ||
return False | return False | ||
# | # ---------------------------------------- | ||
# prog_active() | |||
# check if given user program is active | |||
# | # | ||
# Check if Date/Time/Weekday fits actual date/time | # Check if Date/Time/Weekday fits actual date/time | ||
# to see if user program is activ | # to see if user program is activ | ||
# | # | ||
# | # prog_active (config, dates, times, wdays): | ||
# | # config: config file | ||
# | # dates: program dates | ||
# | # times: program time intervals | ||
# wdays: program week days | |||
# | |||
# return: True/False if programm is active/not active | |||
def prog_active (dates, times, wdays | def prog_active (config, dates, times, wdays): | ||
"""prog_active() check if given user program is active""" | """prog_active() check if given user program is active""" | ||
# | |||
# get actual date / time | |||
d = datetime.datetime.now() | |||
# check the dates in user program | # check the dates in user program | ||
for i in range(len(dates.split(','))): | for i in range(len(dates.split(','))): | ||
pdate=dates.split(',')[i] | pdate = dates.split(',')[i] | ||
if pdate in config.sections(): | if pdate in config.sections(): | ||
if check_date_section(pdate,[d.day, d.month, d.year] | if check_date_section(config, pdate, [d.day, d.month, d.year]): | ||
break | break | ||
elif check_date(pdate,[d.day, d.month, d.year]): | elif check_date(pdate, [d.day, d.month, d.year]): | ||
break | break | ||
# date didn't fit | # date didn't fit | ||
else: | |||
return False | |||
# check the times in user program | # check the times in user program | ||
for i in range(len(times.split(','))): | for i in range(len(times.split(','))): | ||
ptime=times.split(',')[i] | ptime = times.split(',')[i] | ||
if prog_time(ptime,[d.hour, d.minute]): | if prog_time(ptime, [d.hour, d.minute]): | ||
break | break | ||
# time | # time intervals didn't fit | ||
else: | |||
return False | |||
# check the weekdays in user program | # check the weekdays in user program | ||
for i in range(len(wdays.split(','))): | for i in range(len(wdays.split(','))): | ||
pwday=wdays.split(',')[i] | pwday = wdays.split(',')[i] | ||
if prog_wday(pwday,d.weekday()): | if prog_wday(pwday, d.weekday()): | ||
break | |||
# weekdays didn't fit | # weekdays didn't fit | ||
return False | else: | ||
return False | |||
# all tests OK til now | |||
return True | |||
# | # ---------------------------------------- | ||
# read_prog() | |||
# read prog[dates, times, weekdays, message, delay, length, callers] | |||
# | # | ||
# It will search for a valid user program (prog#) in the user section | # It will search for a valid user program ('prog#') in the user section | ||
# and reads | # and reads coresponding definitions for | ||
# - delay the delay of vbox activation | # - delay the delay of vbox activation | ||
# - message the specific message to play | # - message the specific message to play | ||
# - length the max. record length | |||
# | |||
# read_prog (config, section, call_from) | |||
# config: config file | |||
# section: section in config file to read | |||
# call_from: caller number | |||
# | # | ||
# | # return: True/False if valid programm found or not | ||
# @user_prog['delay': xx, 'message': yy, 'length': zz] | |||
# @user_prog['delay': xx, 'message': yy] | |||
def read_prog ( | def read_prog (config, section, call_from): | ||
"""read_prog(section) read user specific program for vbox""" | """read_prog(section) read user specific program for vbox""" | ||
# | user_prog = {} | ||
i = 1 | |||
prog = "prog" + str(i) | |||
while config.has_option(section, prog): | |||
# progX = [dates, times, weekdays, message, delay, length, callers] | |||
p = config.get(section, prog).split() | |||
if prog_active(config, p[0], p[1], p[2]): | |||
if check_caller(config, re.sub('[\s+()-]', '', p[6]), call_from): | |||
user_prog['message'] = p[3] | |||
user_prog['delay'] = p[4] | |||
user_prog['length'] = p[5] | |||
return user_prog | |||
i += 1 | |||
prog = "prog" + str(i) | |||
# empty user_prog = False | |||
return user_prog | |||
# ------------------------------------------------------------------- | |||
# 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 | # 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. | # which user. The real call handling is done in faxIncoming and voiceIncoming. | ||
# | # | ||
# | # callIncoming(call,service,call_from,call_to) | ||
# | # 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 | |||
def callIncoming(call,service,call_from,call_to): | def callIncoming(call,service,call_from,call_to): | ||
# read sections in config file | # read sections in config file | ||
try: | try: | ||
config= | locale.setlocale(locale.LC_ALL, 'de_DE') | ||
config=read_Configfiles() | |||
userlist=config.sections() | userlist=config.sections() | ||
userlist.remove('GLOBAL') | userlist.remove('GLOBAL') | ||
# search for call_to in the user sections | # search for call_to in the user sections | ||
curr_user="" | curr_user="" | ||
| Zeile 256: | Zeile 422: | ||
capisuite.reject(call,0x34A9) | capisuite.reject(call,0x34A9) | ||
return | return | ||
# answer the call with the right service | # answer the call with the right service | ||
if (curr_user==""): | if (curr_user==""): | ||
| Zeile 261: | Zeile 428: | ||
capisuite.reject(call,1) | capisuite.reject(call,1) | ||
return | return | ||
# XXX: neobiker's _adv | |||
try: | |||
phonebook=cs_helpers.readConfig(configfile_phonebook) | |||
except IOError,e: | |||
capisuite.error("Warning: Error occured during phonebook file reading: "+e) | |||
try: | try: | ||
if (curr_service==capisuite.SERVICE_VOICE): | if (curr_service==capisuite.SERVICE_VOICE): | ||
# XXX: neobiker's _adv | |||
user_prog = read_prog(config, curr_user, call_from) | |||
if | if user_prog: | ||
delay=user_prog['delay'] | delay = user_prog['delay'] | ||
else: | |||
delay=cs_helpers.getOption(config,curr_user,"voice_delay") | |||
caller_name = read_phonebook(call_from) | |||
called_name = read_phonebook(call_to) | |||
if (delay==None): | if (delay==None): | ||
capisuite.error("voice_delay not found for user "+curr_user+"! -> rejecting call") | capisuite.error("voice_delay not found for user "+curr_user+"! -> rejecting call") | ||
capisuite.reject(call,0x34A9) | capisuite.reject(call,0x34A9) | ||
return | return | ||
capisuite.log("call from "+ | capisuite.log("call from "+caller_name+" to "+called_name+" for "+curr_user+" connecting with voice",1,call) | ||
capisuite.connect_voice(call,int(delay)) | capisuite.connect_voice(call,int(delay)) | ||
voiceIncoming(call,call_from,call_to,curr_user,config) | voiceIncoming(call,call_from,caller_name,call_to,called_name,curr_user,config) | ||
elif (curr_service==capisuite.SERVICE_FAXG3): | elif (curr_service==capisuite.SERVICE_FAXG3): | ||
faxIncoming(call,call_from,call_to,curr_user,config,0) | caller_name = read_phonebook(call_from) | ||
called_name = read_phonebook(call_to) | |||
faxIncoming(call,call_from,caller_name,call_to,called_name,curr_user,config,0) | |||
except capisuite.CallGoneError: # catch exceptions from connect_* | except capisuite.CallGoneError: # catch exceptions from connect_* | ||
(cause,causeB3)=capisuite.disconnect(call) | (cause,causeB3)=capisuite.disconnect(call) | ||
capisuite.log("connection lost with cause 0x%x,0x%x" % (cause,causeB3),1,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 | # @brief called by callIncoming when an incoming fax call is received | ||
# | # ------------------------------------------------------------------- | ||
# @param call reference to the call. Needed by all capisuite functions | # @param call reference to the call. Needed by all capisuite functions | ||
# @param call_from string containing the number of the calling party | # @param call_from string containing the number of the calling party | ||
# @param call_to string containing the number of the called 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 curr_user name of the user who is responsible for this | ||
# @param config ConfigParser instance holding the config data | # @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) | # @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): | |||
def faxIncoming(call,call_from,caller_name,call_to,called_name,curr_user,config,already_connected): | |||
try: | try: | ||
udir=cs_helpers.getOption(config,"","fax_user_dir") | udir=cs_helpers.getOption(config,"","fax_user_dir") | ||
| Zeile 315: | Zeile 497: | ||
stationID="" | stationID="" | ||
headline=cs_helpers.getOption(config,curr_user,"fax_headline","") # empty string is no problem here | headline=cs_helpers.getOption(config,curr_user,"fax_headline","") # empty string is no problem here | ||
capisuite.log("call from "+ | capisuite.log("call from "+caller_name+" to "+called_name+" for "+curr_user+" connecting with fax",1,call) | ||
if (already_connected): | if (already_connected): | ||
faxInfo=capisuite.switch_to_faxG3(call,stationID,headline) | faxInfo=capisuite.switch_to_faxG3(call,stationID,headline) | ||
| Zeile 354: | Zeile 536: | ||
action="saveonly" | action="saveonly" | ||
if (action=="mailandsave"): | if (action=="mailandsave"): | ||
cs_helpers.sendMIMEMail(fromaddress, mailaddress, "Fax | # XXX: changed Mail text | ||
caller_details = "" | |||
if (caller_name == call_from): | |||
caller_name, caller_details = check_number_online (call_from,call) | |||
cs_helpers.sendMIMEMail(fromaddress, mailaddress, | |||
"Fax von "+caller_name+" an "+called_name, faxFormat, | |||
"Fax Sender: "+caller_name+" ("+call_from+")\n" | |||
+" "+caller_details+"\n" | |||
+"Datum: "+time.strftime("%c")+"\n" | |||
+"Seiten: "+get_fax_pages(filename,call)+"\n\n" | |||
+"Siehe Anhang.\nDas Fax wurde gespeichert unter: "+filename+"\n", | |||
filename) | |||
# --------------------------------------------------------------------- | |||
# @brief called by callIncoming when an incoming voice call is 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 reference to the call. Needed by all capisuite functions | ||
# @param call_from string containing the number of the calling party | # @param call_from string containing the number of the calling party | ||
| Zeile 365: | Zeile 557: | ||
# @param curr_user name of the user who is responsible for this | # @param curr_user name of the user who is responsible for this | ||
# @param config ConfigParser instance holding the config data | # @param config ConfigParser instance holding the config data | ||
def voiceIncoming(call,call_from,call_to,curr_user,config): | |||
def voiceIncoming(call,call_from,caller_name,call_to,called_name,curr_user,config): | |||
try: | try: | ||
udir=cs_helpers.getOption(config,"","voice_user_dir") | udir=cs_helpers.getOption(config,"","voice_user_dir") | ||
if (udir==None): | if (udir==None): | ||
| Zeile 394: | Zeile 585: | ||
try: | try: | ||
capisuite.enable_DTMF(call) | capisuite.enable_DTMF(call) | ||
if | # XXX: neobiker's _adv | ||
user_prog = read_prog(config, curr_user, call_from) | |||
if user_prog: | |||
userannouncement = udir + user_prog['message'] | |||
else: | |||
userannouncement=udir+cs_helpers.getOption(config,curr_user,"announcement","announcement.la") | userannouncement=udir+cs_helpers.getOption(config,curr_user,"announcement","announcement.la") | ||
pin=cs_helpers.getOption(config,curr_user,"pin","") | pin=cs_helpers.getOption(config,curr_user,"pin","") | ||
if (os.access(userannouncement,os.R_OK)): | if (os.access(userannouncement,os.R_OK)): | ||
| Zeile 409: | Zeile 602: | ||
if (action!="none"): | if (action!="none"): | ||
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la"),1) | capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la"),1) | ||
if | # XXX: neobiker's _adv | ||
if user_prog: | |||
length = user_prog['length'] | |||
else: | |||
length=cs_helpers.getOption(config,curr_user,"record_length","60") | length=cs_helpers.getOption(config,curr_user,"record_length","60") | ||
silence_timeout=cs_helpers.getOption(config,curr_user,"record_silence_timeout","5") | silence_timeout=cs_helpers.getOption(config,curr_user,"record_silence_timeout","5") | ||
capisuite.audio_receive(call,filename,int(length), int(silence_timeout),1) | capisuite.audio_receive(call,filename,int(length), int(silence_timeout),1) | ||
| Zeile 420: | Zeile 614: | ||
if (os.access(filename,os.R_OK)): | if (os.access(filename,os.R_OK)): | ||
os.unlink(filename) | os.unlink(filename) | ||
faxIncoming(call,call_from,call_to,curr_user,config,1) | faxIncoming(call,call_from,caller_name,call_to,called_name,curr_user,config,1) | ||
elif (dtmf_list!="" and pin!=""): | elif (dtmf_list!="" and pin!=""): | ||
dtmf_list+=capisuite.read_DTMF(call,3) # wait 5 seconds for input | dtmf_list+=capisuite.read_DTMF(call,3) # wait 5 seconds for input | ||
| Zeile 459: | Zeile 653: | ||
mailaddress=curr_user | mailaddress=curr_user | ||
if (action=="mailandsave"): | if (action=="mailandsave"): | ||
cs_helpers.sendMIMEMail(fromaddress, mailaddress, " | # XXX: changed Mail text | ||
caller_details = "" | |||
if (caller_name == call_from): | |||
caller_name, caller_details = check_number_online (call_from,call) | |||
cs_helpers.sendMIMEMail(fromaddress, mailaddress, | |||
"Nachricht von "+caller_name+" fuer "+called_name, "la", | |||
"Anrufer: "+caller_name+" ("+call_from+")\n" | |||
+" "+caller_details+"\n" | |||
+"Laenge: "+str(os.stat(filename)[6]/8/1024)+" Sek.\n" | |||
+"Datum: "+time.strftime("%c")+"\n" | |||
+"Nummer: "+called_name+" ("+call_to+")\n\n" | |||
+"Siehe Anhang.\nDas File wurde gespeichert unter: "+filename+"\n", | |||
filename) | |||
# ----------------------------------------------------------- | |||
# @brief remote inquiry function (uses german wave snippets!) | # @brief remote inquiry function (uses german wave snippets!) | ||
# | # ----------------------------------------------------------- | ||
# commands for remote inquiry | # commands for remote inquiry | ||
# delete message - 1 | # delete message - 1 | ||
| Zeile 476: | Zeile 681: | ||
# @param curr_user name of the user who is responsible for this | # @param curr_user name of the user who is responsible for this | ||
# @param config ConfigParser instance holding the config data | # @param config ConfigParser instance holding the config data | ||
# | |||
def remoteInquiry(call,userdir,curr_user,config): | def remoteInquiry(call,userdir,curr_user,config): | ||
import time,fcntl,errno,os | import time,fcntl,errno,os | ||
| Zeile 555: | Zeile 761: | ||
cs_helpers.sayNumber(call,descr.get('GLOBAL','call_to'),curr_user,config) | 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) | capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"am.la"),1) | ||
locale.setlocale(locale.LC_ALL, 'C') | |||
calltime=time.strptime(descr.get('GLOBAL','time')) | calltime=time.strptime(descr.get('GLOBAL','time')) | ||
cs_helpers.sayNumber(call,str(calltime[2]),curr_user,config) | cs_helpers.sayNumber(call,str(calltime[2]),curr_user,config) | ||
| Zeile 591: | Zeile 798: | ||
os.unlink(userdir+"received/inquiry_lock") | os.unlink(userdir+"received/inquiry_lock") | ||
# --------------------------------------------------------------------------- | |||
# @brief remote inquiry: record new announcement (uses german wave snippets!) | # @brief remote inquiry: record new announcement (uses german wave snippets!) | ||
# | # --------------------------------------------------------------------------- | ||
# @param call reference to the call. Needed by all capisuite functions | # @param call reference to the call. Needed by all capisuite functions | ||
# @param userdir spool_dir of the current_user | # @param userdir spool_dir of the current_user | ||
# @param curr_user name of the user who is responsible for this | # @param curr_user name of the user who is responsible for this | ||
# @param config ConfigParser instance holding the config data | # @param config ConfigParser instance holding the config data | ||
def newAnnouncement(call,userdir,curr_user,config): | 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,"bitte-neue-ansage-komplett.la")) | ||
| Zeile 620: | Zeile 829: | ||
# History: | # History: | ||
# | # | ||
# $Log: | # $Log: incoming_adv.py,v $ | ||
# Revision 1 | # Revision 1.2 2009/04/22 18:14:20 root | ||
# added get_fax_pages() | |||
# | |||
# | # | ||
# Revision 1. | # Revision 1.1 2008/11/05 15:42:59 root | ||
# | # Initial revision | ||
# | # | ||
# Revision 1. | # Revision 1.9 2008/04/26 09:45:29 root | ||
# | # stripped old RCS comments | ||
# | # | ||
# Revision 1. | # Revision 1.8 2007/09/10 20:37:00 root | ||
# | # optimized read_phonebook() | ||
# | # | ||
# Revision 1. | # Revision 1.7 2007/09/09 19:24:47 root | ||
# | # Names now case sensitive | ||
# | # | ||
# Revision 1. | # Revision 1.6 2007/09/09 19:08:26 root | ||
# phonebook.conf optional | |||
# | |||
# | # | ||
# Revision 1. | # Revision 1.5 2007/09/09 19:05:26 root | ||
# | # integrated phonebook sections in capisuite config | ||
# | # | ||
# Revision 1. | # Revision 1.4 2007/09/09 16:01:50 root | ||
# | # added errorhandling and logging in check_number_online () | ||
# | # | ||
# Revision 1. | # Revision 1.3 2007/09/09 15:29:16 root | ||
# | # added German date format and record length to Email | ||
# | # | ||
# Revision 1. | # Revision 1.2 2007/09/09 13:49:40 root | ||
# | # added phonebook function and reverse lookup online | ||
# | # | ||
# Revision 1. | # Revision 1.1 2007/09/09 13:22:42 root | ||
# | # Initial revision | ||
# | # | ||
# Revision 1. | # Revision 1.4 2007/01/23 14:42:11 root | ||
# | # changed mail text to german and beatified layout | ||
# | # | ||
# Revision 1. | # Revision 1.3 2007/01/03 23:27:36 root | ||
# | # none | ||
# | # | ||
# Revision 1. | # Revision 1.2 2007/01/03 23:23:27 root | ||
# | # First checkin | ||
# | # | ||
# Revision 1. | # Revision 1.1 2007/01/03 23:18:13 root | ||
# Initial revision | |||
# ... removed old comments | |||
# | |||
# | |||
# | # | ||
# Revision 1.9.2.1 2003/08/24 12:47:19 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! | |||
# ... | |||
</pre> | |||
[[Capisuite|Zurück zu CapiSuite]] | |||
[[Neobiker%27s_Wiki:Portal|Zurück zum Portal]] | |||
Aktuelle Version vom 22. April 2009, 19:36 Uhr
File: incoming_adv.py
# incoming_adv.py - advanced incoming script for capisuite
# --------------------------------------------------------
# copyright : (c) 2007 by Neobiker
# Version : $Revision: 1.2 $
# Date : $Date: 2009/04/22 18:14:20 $
#
# original script : incoming.py
# copyright : (C) 2002 by Gernot Hillier
# email : gernot@hillier.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# general imports
import locale,datetime,time,os,re,string,pwd,commands
# CapiSuite imports
import capisuite,cs_helpers
# phonebook file (neobiker's _adv)
configfile_phonebook="/etc/capisuite/phonebook.conf"
configfile_fax="/etc/capisuite/fax.conf"
configfile_voice="/etc/capisuite/answering_machine.conf"
# read_Configfiles ()
# 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 read_Configfiles(file=""):
import ConfigParser
config=ConfigParser.ConfigParser()
if (file==""):
config.readfp(open(configfile_fax))
config.readfp(open(configfile_voice))
try:
config.readfp(open(configfile_phonebook))
except:
pass
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
# read_phonebook ()
# check if number is listed in phonebook
#
# read_phonebook (number):
# number: tel number
#
# return: name
def read_phonebook (number):
"""read_phonebook () read name of number if listed in phonebook"""
try:
fd=open(configfile_phonebook)
except:
return number
for line in fd:
line=line.strip()
if (not line) | line.startswith("#") | (not '=' in line):
continue
match=re.match(r'(.*?)\s*=\s*(.*)$', line)
name=match.group(1)
details=re.sub('[\s+()-]','',match.group(2))
if re.sub('[\s+()-]','',number) in details.split(','):
fd.close()
return name
fd.close()
return number
# ----------------------------------------
# get_fax_pages ()
# get number of pages of the fax file
#
# get_fax_pages (fax):
# fax: sff fax file
#
# return: pages
def get_fax_pages (fax, call):
"""get_fax_pages () get fax pages"""
pages = ""
cmd = "sfftobmp -t " + fax + " -o /dev/null 2>&1 | grep 'page(s)'"
try:
pages = commands.getoutput(cmd).split()[5]
except:
capisuite.log("Error: get_fax_pages() failed",1,call)
capisuite.log("Error: "+cmd+" failed",1,call)
return pages
# ----------------------------------------
# check_number_online ()
# online reverse lookup
#
# check_number_online (number):
# number: tel number
#
# return: name, details
def check_number_online (number,call):
"""check_number_online () check if number if listed in online phonebook"""
name = details = ""
cmd = "tbident.sh " + number + " 2>/dev/null"
try:
number, name, details = commands.getoutput(cmd).split("|")
except:
capisuite.log("Error: check_number_online() failed",1,call)
capisuite.log("Error: "+cmd+" failed",1,call)
return name, details
# ----------------------------------------
# check_date ()
# check if user program date fits actual date
#
# check_date (pdate, adate):
# pdate: program date
# adate: actual date
#
# return: True/False if 'pdate' fits 'adate'
def check_date (pdate, adate):
"""check_date() check if actual date fits user program date"""
# a '*' fits always...
if pdate == '*': return True
# initialise day.month.year from actual date if field is undefined
# update pdate fields day.month.year as defined in user program
idate=pdate.split('.')
pdate=adate[:]
for i in range(len(idate)):
if idate[i] and idate[i] != '*':
pdate[i] = int(idate[i])
return pdate == adate;
# ----------------------------------------
# check_date_section ()
# check if date is listed in date section
#
# check_date_section (config, dsect, adate):
# config: config file
# dsect: date section list of dates
# adate: actual date
#
# return: True/False if 'adate' is listed/not listed
def check_date_section (config, dsect, adate):
"""check_date_section () check if date is listed in date section"""
# check all entries in date section
# every entry (item) can be a list of dates
for i in range(len(config.items(dsect))):
for pdate in config.items(dsect)[i][1].split(','):
if check_date(pdate, adate):
return True
return False
# ----------------------------------------
# prog_time ()
# check if actual time fits user program time interval
#
# prog_time (ptime, atime):
# ptime: program time intervall
# atime: actual time
#
# return: True/False if 'ptime' fit's actual time
def prog_time (ptime, atime):
"""prog_time() check if actual time fits user program time interval"""
# a '*' fits always...
if ptime == '*': return True
# From: time intervall start
# From: minutes are optional, set default to :00
f0 = ptime.split('-')[0] + ':00'
f = [int(f0.split(':')[0]), int(f0.split(':')[1])]
# To: time intervall end
# To: minutes are optional, set default to :00
t0 = ptime.split('-')[1] + ':00'
t = [int(t0.split(':')[0]), int(t0.split(':')[1])]
# test actual time interval: From < PTime < To?
# 1.st test: f < t (means: t < 23:59)
# 2.nd test: f > t (means: t >= 00:00 -> on next day!)
if f < t:
return f <= atime <= t
else:
return not (t <= atime <= f)
# ----------------------------------------
# prog_wday()
# check if weekday is listed in user program
#
# prog_wday (pwday, wday):
# pwday program weekday
# wday actual weekday
#
# return: True/False if 'wday' is listed/not listed
def prog_wday (pwday, wday):
"""prog_wday() check if weekday is listed in user program"""
# a '*' fits always...
if pwday == '*': return True
wd={'MO': 0, 'DI': 1, 'MI': 2, 'DO': 3, 'FR': 4, 'SA': 5, 'SO': 6, \
'TU': 1, 'WE': 2, 'TH': 3, 'SU': 6}
return wday == wd[pwday.upper()]
# ----------------------------------------
# check_caller_section ()
# check if call_from is listed in user caller-section
#
# check_caller_section (config, csect, call_from):
# config: config file
# csect: caller-section
# call_from: caller number
#
# return: True/False if 'call_from' is listed/not listed
def check_caller_section (config, csect, call_from):
"""check_caller_section () check if call_from is listed in callers section"""
# check all entries in caller section
# every entry (item) can be a list of numbers
# standardise numbers (delete blanks etc.)
for i in range(len(config.items(csect))):
for j in config.items(csect)[i][1].split(','):
if call_from == re.sub('[\s+()-]','',j):
return True
return False
# ----------------------------------------
# check_caller()
# check if 'call_from' is listed in any callers list/section
#
# check_caller (config, callers, call_from):
# config: config file
# callers: list of numbers or sections
# call_from: caller number
#
# return: True/False if 'call_from' is listed/not listed
def check_caller (config, callers, call_from):
"""check_caller () check if caller_from matches group entries/section"""
# a '*' fits always...
if callers == '*': return True
# check all entries in group entry (caller numbers)
# every entry (item) can be a separate section
# correct format of numbers (delete blanks etc.)
for i in range(len(callers.split(','))):
pcaller = re.sub('[\s+()-]', '', callers.split(',')[i])
if pcaller in config.sections():
if check_caller_section(config, pcaller, call_from):
return True
elif call_from == pcaller:
return True
return False
# ----------------------------------------
# prog_active()
# check if given user program is active
#
# Check if Date/Time/Weekday fits actual date/time
# to see if user program is activ
#
# prog_active (config, dates, times, wdays):
# config: config file
# dates: program dates
# times: program time intervals
# wdays: program week days
#
# return: True/False if programm is active/not active
def prog_active (config, dates, times, wdays):
"""prog_active() check if given user program is active"""
# get actual date / time
d = datetime.datetime.now()
# check the dates in user program
for i in range(len(dates.split(','))):
pdate = dates.split(',')[i]
if pdate in config.sections():
if check_date_section(config, pdate, [d.day, d.month, d.year]):
break
elif check_date(pdate, [d.day, d.month, d.year]):
break
# date didn't fit
else:
return False
# check the times in user program
for i in range(len(times.split(','))):
ptime = times.split(',')[i]
if prog_time(ptime, [d.hour, d.minute]):
break
# time intervals didn't fit
else:
return False
# check the weekdays in user program
for i in range(len(wdays.split(','))):
pwday = wdays.split(',')[i]
if prog_wday(pwday, d.weekday()):
break
# weekdays didn't fit
else:
return False
# all tests OK til now
return True
# ----------------------------------------
# read_prog()
# read prog[dates, times, weekdays, message, delay, length, callers]
#
# It will search for a valid user program ('prog#') in the user section
# and reads coresponding definitions for
# - delay the delay of vbox activation
# - message the specific message to play
# - length the max. record length
#
# read_prog (config, section, call_from)
# config: config file
# section: section in config file to read
# call_from: caller number
#
# return: True/False if valid programm found or not
# @user_prog['delay': xx, 'message': yy, 'length': zz]
def read_prog (config, section, call_from):
"""read_prog(section) read user specific program for vbox"""
user_prog = {}
i = 1
prog = "prog" + str(i)
while config.has_option(section, prog):
# progX = [dates, times, weekdays, message, delay, length, callers]
p = config.get(section, prog).split()
if prog_active(config, p[0], p[1], p[2]):
if check_caller(config, re.sub('[\s+()-]', '', p[6]), call_from):
user_prog['message'] = p[3]
user_prog['delay'] = p[4]
user_prog['length'] = p[5]
return user_prog
i += 1
prog = "prog" + str(i)
# empty user_prog = False
return user_prog
# -------------------------------------------------------------------
# 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.
#
# callIncoming(call,service,call_from,call_to)
# 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
def callIncoming(call,service,call_from,call_to):
# read sections in config file
try:
locale.setlocale(locale.LC_ALL, 'de_DE')
config=read_Configfiles()
userlist=config.sections()
userlist.remove('GLOBAL')
# search for call_to in the user sections
curr_user=""
for u in userlist:
if config.has_option(u,'voice_numbers'):
numbers=config.get(u,'voice_numbers')
if (call_to in numbers.split(',') or numbers=="*"):
if (service==capisuite.SERVICE_VOICE):
curr_user=u
curr_service=capisuite.SERVICE_VOICE
break
if (service==capisuite.SERVICE_FAXG3):
curr_user=u
curr_service=capisuite.SERVICE_FAXG3
break
if config.has_option(u,'fax_numbers'):
numbers=config.get(u,'fax_numbers')
if (call_to in numbers.split(',') or numbers=="*"):
if (service in (capisuite.SERVICE_FAXG3,capisuite.SERVICE_VOICE)):
curr_user=u
curr_service=capisuite.SERVICE_FAXG3
break
except IOError,e:
capisuite.error("Error occured during config file reading: "+e+" Disconnecting...")
capisuite.reject(call,0x34A9)
return
# answer the call with the right service
if (curr_user==""):
capisuite.log("call from "+call_from+" to "+call_to+" ignoring",1,call)
capisuite.reject(call,1)
return
# XXX: neobiker's _adv
try:
phonebook=cs_helpers.readConfig(configfile_phonebook)
except IOError,e:
capisuite.error("Warning: Error occured during phonebook file reading: "+e)
try:
if (curr_service==capisuite.SERVICE_VOICE):
# XXX: neobiker's _adv
user_prog = read_prog(config, curr_user, call_from)
if user_prog:
delay = user_prog['delay']
else:
delay=cs_helpers.getOption(config,curr_user,"voice_delay")
caller_name = read_phonebook(call_from)
called_name = read_phonebook(call_to)
if (delay==None):
capisuite.error("voice_delay not found for user "+curr_user+"! -> rejecting call")
capisuite.reject(call,0x34A9)
return
capisuite.log("call from "+caller_name+" to "+called_name+" for "+curr_user+" connecting with voice",1,call)
capisuite.connect_voice(call,int(delay))
voiceIncoming(call,call_from,caller_name,call_to,called_name,curr_user,config)
elif (curr_service==capisuite.SERVICE_FAXG3):
caller_name = read_phonebook(call_from)
called_name = read_phonebook(call_to)
faxIncoming(call,call_from,caller_name,call_to,called_name,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,caller_name,call_to,called_name,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.access(udir,os.F_OK)):
userdata=pwd.getpwnam(curr_user)
os.mkdir(udir,0700)
os.chown(udir,userdata[2],userdata[3])
if (not os.access(udir+"received/",os.F_OK)):
userdata=pwd.getpwnam(curr_user)
os.mkdir(udir+"received/",0700)
os.chown(udir+"received/",userdata[2],userdata[3])
except KeyError:
capisuite.error("user "+curr_user+" is not a valid system user. Disconnecting",call)
capisuite.reject(call,0x34A9)
return
filename="" # assure the variable is defined...
try:
stationID=cs_helpers.getOption(config,curr_user,"fax_stationID")
if (stationID==None):
capisuite.error("Warning: fax_stationID not found for user "+curr_user+" -> using empty string")
stationID=""
headline=cs_helpers.getOption(config,curr_user,"fax_headline","") # empty string is no problem here
capisuite.log("call from "+caller_name+" to "+called_name+" for "+curr_user+" connecting with fax",1,call)
if (already_connected):
faxInfo=capisuite.switch_to_faxG3(call,stationID,headline)
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(udir+"received/","fax",faxFormat)
capisuite.fax_receive(call,filename)
(cause,causeB3)=capisuite.disconnect(call)
capisuite.log("connection finished with cause 0x%x,0x%x" % (cause,causeB3),1,call)
except capisuite.CallGoneError: # catch this here to get the cause info in the mail
(cause,causeB3)=capisuite.disconnect(call)
capisuite.log("connection lost with cause 0x%x,0x%x" % (cause,causeB3),1,call)
if (os.access(filename,os.R_OK)):
cs_helpers.writeDescription(filename,
"call_from=\""+call_from+"\"\ncall_to=\""+call_to+"\"\ntime=\""
+time.ctime()+"\"\ncause=\"0x%x/0x%x\"\n" % (cause,causeB3))
userdata=pwd.getpwnam(curr_user)
os.chmod(filename,0600)
os.chown(filename,userdata[2],userdata[3])
os.chmod(filename[:-3]+"txt",0600)
os.chown(filename[:-3]+"txt",userdata[2],userdata[3])
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 "+curr_user+" -> assuming SaveOnly")
action="saveonly"
if (action=="mailandsave"):
# XXX: changed Mail text
caller_details = ""
if (caller_name == call_from):
caller_name, caller_details = check_number_online (call_from,call)
cs_helpers.sendMIMEMail(fromaddress, mailaddress,
"Fax von "+caller_name+" an "+called_name, faxFormat,
"Fax Sender: "+caller_name+" ("+call_from+")\n"
+" "+caller_details+"\n"
+"Datum: "+time.strftime("%c")+"\n"
+"Seiten: "+get_fax_pages(filename,call)+"\n\n"
+"Siehe Anhang.\nDas Fax wurde gespeichert unter: "+filename+"\n",
filename)
# ---------------------------------------------------------------------
# @brief called by callIncoming when an incoming voice call is received
# ---------------------------------------------------------------------
# @param call reference to the call. Needed by all capisuite functions
# @param call_from string containing the number of the calling party
# @param call_to string containing the number of the called party
# @param curr_user name of the user who is responsible for this
# @param config ConfigParser instance holding the config data
def voiceIncoming(call,call_from,caller_name,call_to,called_name,curr_user,config):
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.access(udir,os.F_OK)):
userdata=pwd.getpwnam(curr_user)
os.mkdir(udir,0700)
os.chown(udir,userdata[2],userdata[3])
if (not os.access(udir+"received/",os.F_OK)):
userdata=pwd.getpwnam(curr_user)
os.mkdir(udir+"received/",0700)
os.chown(udir+"received/",userdata[2],userdata[3])
except KeyError:
capisuite.error("user "+curr_user+" is not a valid system user. Disconnecting",call)
capisuite.reject(call,0x34A9)
return
filename=cs_helpers.uniqueName(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 "+curr_user+" -> assuming SaveOnly")
action="saveonly"
try:
capisuite.enable_DTMF(call)
# XXX: neobiker's _adv
user_prog = read_prog(config, curr_user, call_from)
if user_prog:
userannouncement = udir + user_prog['message']
else:
userannouncement=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)
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)
if (action!="none"):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la"),1)
# XXX: neobiker's _adv
if user_prog:
length = user_prog['length']
else:
length=cs_helpers.getOption(config,curr_user,"record_length","60")
silence_timeout=cs_helpers.getOption(config,curr_user,"record_silence_timeout","5")
capisuite.audio_receive(call,filename,int(length), int(silence_timeout),1)
dtmf_list=capisuite.read_DTMF(call,0)
if (dtmf_list=="X"):
if (os.access(filename,os.R_OK)):
os.unlink(filename)
faxIncoming(call,call_from,caller_name,call_to,called_name,curr_user,config,1)
elif (dtmf_list!="" and pin!=""):
dtmf_list+=capisuite.read_DTMF(call,3) # wait 5 seconds for input
count=1
while (count<3 and pin!=dtmf_list): # try again if input was wrong
capisuite.log("wrong PIN entered...",1,call)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la"))
dtmf_list=capisuite.read_DTMF(call,3)
count+=1
if (pin==dtmf_list):
if (os.access(filename,os.R_OK)):
os.unlink(filename)
capisuite.log("Starting remote inquiry...",1,call)
remoteInquiry(call,udir,curr_user,config)
(cause,causeB3)=capisuite.disconnect(call)
capisuite.log("connection finished with cause 0x%x,0x%x" % (cause,causeB3),1,call)
except capisuite.CallGoneError: # catch this here to get the cause info in the mail
(cause,causeB3)=capisuite.disconnect(call)
capisuite.log("connection lost with cause 0x%x,0x%x" % (cause,causeB3),1,call)
if (os.access(filename,os.R_OK)):
cs_helpers.writeDescription(filename,
"call_from=\""+call_from+"\"\ncall_to=\""+call_to+"\"\ntime=\""
+time.ctime()+"\"\ncause=\"0x%x/0x%x\"\n" % (cause,causeB3))
userdata=pwd.getpwnam(curr_user)
os.chmod(filename,0600)
os.chown(filename,userdata[2],userdata[3])
os.chmod(filename[:-2]+"txt",0600)
os.chown(filename[:-2]+"txt",userdata[2],userdata[3])
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"):
# XXX: changed Mail text
caller_details = ""
if (caller_name == call_from):
caller_name, caller_details = check_number_online (call_from,call)
cs_helpers.sendMIMEMail(fromaddress, mailaddress,
"Nachricht von "+caller_name+" fuer "+called_name, "la",
"Anrufer: "+caller_name+" ("+call_from+")\n"
+" "+caller_details+"\n"
+"Laenge: "+str(os.stat(filename)[6]/8/1024)+" Sek.\n"
+"Datum: "+time.strftime("%c")+"\n"
+"Nummer: "+called_name+" ("+call_to+")\n\n"
+"Siehe Anhang.\nDas File wurde gespeichert unter: "+filename+"\n",
filename)
# -----------------------------------------------------------
# @brief remote inquiry function (uses german wave snippets!)
# -----------------------------------------------------------
# commands for remote inquiry
# delete message - 1
# next message - 4
# last message - 5
# repeat current message - 6
#
# @param call reference to the call. Needed by all capisuite functions
# @param userdir spool_dir of the current_user
# @param curr_user name of the user who is responsible for this
# @param config ConfigParser instance holding the config data
#
def remoteInquiry(call,userdir,curr_user,config):
import time,fcntl,errno,os
# acquire lock
lockfile=open(userdir+"received/inquiry_lock","w")
try:
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()
return
try:
# read directory contents
messages=os.listdir(userdir+"received/")
messages=filter (lambda s: re.match("voice-.*\.la",s),messages) # only use voice-* files
messages=map(lambda s: int(re.match("voice-([0-9]+)\.la",s).group(1)),messages) # filter out numbers
messages.sort()
# read the number of the message heard last at the last inquiry
lastinquiry=-1
if (os.access(userdir+"received/last_inquiry",os.W_OK)):
lastfile=open(userdir+"received/last_inquiry","r")
lastinquiry=int(lastfile.readline())
lastfile.close()
# 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)
if (len(messages)==1):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-nachricht.la"),1)
else:
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-nachrichten.la"),1)
# menu for record new announcement
cmd=""
while (cmd not in ("1","9")):
if (len(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)
return
# start inquiry
for curr_msgs in (messages,oldmessages):
cs_helpers.sayNumber(call,str(len(curr_msgs)),curr_user,config)
if (curr_msgs==messages):
if (len(curr_msgs)==1):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-nachricht.la"),1)
else:
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-nachrichten.la"),1)
else:
if (len(curr_msgs)==1):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"nachricht.la"),1)
else:
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"nachrichten.la"),1)
i=0
while (i<len(curr_msgs)):
filename=userdir+"received/voice-"+str(curr_msgs[i])+".la"
descr=cs_helpers.readConfig(filename[:-2]+"txt")
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"nachricht.la"),1)
cs_helpers.sayNumber(call,str(i+1),curr_user,config)
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)
locale.setlocale(locale.LC_ALL, 'C')
calltime=time.strptime(descr.get('GLOBAL','time'))
cs_helpers.sayNumber(call,str(calltime[2]),curr_user,config)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"..la"),1)
cs_helpers.sayNumber(call,str(calltime[1]),curr_user,config)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"..la"),1)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"um.la"),1)
cs_helpers.sayNumber(call,str(calltime[3]),curr_user,config)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"uhr.la"),1)
cs_helpers.sayNumber(call,str(calltime[4]),curr_user,config)
capisuite.audio_send(call,filename,1)
cmd=""
while (cmd not in ("1","4","5","6")):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"erklaerung.la"),1)
cmd=capisuite.read_DTMF(call,0,1)
if (cmd=="1"):
os.remove(filename)
os.remove(filename[:-2]+"txt")
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(userdir+"received/last_inquiry","w")
lastfile.write(str(curr_msgs[i])+"\n")
lastfile.close()
i+=1
elif (cmd=="5"):
i-=1
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"keine-weiteren-nachrichten.la"))
finally:
# unlock
fcntl.lockf(lockfile,fcntl.LOCK_UN)
lockfile.close()
os.unlink(userdir+"received/inquiry_lock")
# ---------------------------------------------------------------------------
# @brief remote inquiry: record new announcement (uses german wave snippets!)
# ---------------------------------------------------------------------------
# @param call reference to the call. Needed by all capisuite functions
# @param userdir spool_dir of the current_user
# @param curr_user name of the user who is responsible for this
# @param config ConfigParser instance holding the config data
def newAnnouncement(call,userdir,curr_user,config):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"bitte-neue-ansage-komplett.la"))
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la"))
cmd=""
while (cmd!="1"):
capisuite.audio_receive(call,userdir+"announcement-tmp.la",60,3)
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"neue-ansage-lautet.la"))
capisuite.audio_send(call,userdir+"announcement-tmp.la")
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"wenn-einverstanden-1.la"))
cmd=capisuite.read_DTMF(call,0,1)
if (cmd!="1"):
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"bitte-neue-ansage-kurz.la"))
capisuite.audio_send(call,cs_helpers.getAudio(config,curr_user,"beep.la"))
userannouncement=userdir+cs_helpers.getOption(config,curr_user,"announcement","announcement.la")
os.rename(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"))
#
# History:
#
# $Log: incoming_adv.py,v $
# Revision 1.2 2009/04/22 18:14:20 root
# added get_fax_pages()
#
# Revision 1.1 2008/11/05 15:42:59 root
# Initial revision
#
# Revision 1.9 2008/04/26 09:45:29 root
# stripped old RCS comments
#
# Revision 1.8 2007/09/10 20:37:00 root
# optimized read_phonebook()
#
# Revision 1.7 2007/09/09 19:24:47 root
# Names now case sensitive
#
# Revision 1.6 2007/09/09 19:08:26 root
# phonebook.conf optional
#
# Revision 1.5 2007/09/09 19:05:26 root
# integrated phonebook sections in capisuite config
#
# Revision 1.4 2007/09/09 16:01:50 root
# added errorhandling and logging in check_number_online ()
#
# Revision 1.3 2007/09/09 15:29:16 root
# added German date format and record length to Email
#
# Revision 1.2 2007/09/09 13:49:40 root
# added phonebook function and reverse lookup online
#
# Revision 1.1 2007/09/09 13:22:42 root
# Initial revision
#
# Revision 1.4 2007/01/23 14:42:11 root
# changed mail text to german and beatified layout
#
# Revision 1.3 2007/01/03 23:27:36 root
# none
#
# Revision 1.2 2007/01/03 23:23:27 root
# First checkin
#
# Revision 1.1 2007/01/03 23:18:13 root
# Initial revision
# ... removed old comments
#
# Revision 1.9.2.1 2003/08/24 12:47:19 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!
# ...