Incoming adv: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
Zeile 38: | Zeile 38: | ||
# @return the constructed config file object | # @return the constructed config file object | ||
def read_Configfiles(file=""): | def read_Configfiles(file=""): | ||
import ConfigParser | |||
config=ConfigParser.ConfigParser() | |||
if (file==""): | |||
config.readfp(open(configfile_fax)) | |||
config.readfp(open(configfile_voice)) | |||
try: | try: | ||
config.readfp(open(configfile_phonebook)) | |||
except: | except: | ||
pass | 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 () | # read_phonebook () | ||
Zeile 62: | Zeile 62: | ||
# | # | ||
# read_phonebook (number): | # read_phonebook (number): | ||
# number: | # number: tel number | ||
# | # | ||
# return: name | # return: name | ||
Zeile 108: | Zeile 108: | ||
# ---------------------------------------- | # ---------------------------------------- | ||
# check_date () | # check_date () | ||
# check if user program date fits actual date | # check if user program date fits actual date | ||
# | # | ||
# check_date (pdate, adate): | # check_date (pdate, adate): | ||
Zeile 128: | Zeile 128: | ||
for i in range(len(idate)): | for i in range(len(idate)): | ||
if idate[i] and idate[i] != '*': | if idate[i] and idate[i] != '*': | ||
pdate[i] = int(idate[i]) | pdate[i] = int(idate[i]) | ||
return pdate == adate; | return pdate == adate; | ||
Zeile 136: | Zeile 136: | ||
# | # | ||
# check_date_section (config, dsect, adate): | # check_date_section (config, dsect, adate): | ||
# config: | # config: config file | ||
# dsect: | # dsect: date section list of dates | ||
# adate: | # adate: actual date | ||
# | # | ||
# return: True/False if 'adate' is listed/not listed | # return: True/False if 'adate' is listed/not listed | ||
Zeile 145: | Zeile 145: | ||
"""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 | # check all entries in date section | ||
# every entry (item) can be a list of dates | # 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))): | ||
Zeile 158: | Zeile 158: | ||
# | # | ||
# prog_time (ptime, atime): | # prog_time (ptime, atime): | ||
# ptime: | # ptime: program time intervall | ||
# atime: | # atime: actual time | ||
# | # | ||
# return: True/False if 'ptime' fit's actual time | # return: True/False if 'ptime' fit's actual time | ||
Zeile 165: | Zeile 165: | ||
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""" | ||
# a '*' fits always... | # a '*' fits always... | ||
if ptime == '*': return True | if ptime == '*': return True | ||
Zeile 213: | Zeile 213: | ||
# | # | ||
# check_caller_section (config, csect, call_from): | # check_caller_section (config, csect, call_from): | ||
# config: | # config: config file | ||
# csect: | # csect: caller-section | ||
# call_from: caller number | # call_from: caller number | ||
# | # | ||
Zeile 222: | Zeile 222: | ||
"""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 | # check all entries in caller section | ||
# every entry (item) can be a list of numbers | # every entry (item) can be a list of numbers | ||
# standardise numbers (delete blanks etc.) | # standardise numbers (delete blanks etc.) | ||
Zeile 236: | Zeile 236: | ||
# | # | ||
# check_caller (config, callers, call_from): | # check_caller (config, callers, call_from): | ||
# config: | # config: config file | ||
# callers: | # callers: list of numbers or sections | ||
# call_from: caller number | # call_from: caller number | ||
# | # | ||
Zeile 268: | Zeile 268: | ||
# | # | ||
# prog_active (config, dates, times, wdays): | # prog_active (config, dates, times, wdays): | ||
# config: | # config: config file | ||
# dates: | # dates: program dates | ||
# times: | # times: program time intervals | ||
# wdays: | # wdays: program week days | ||
# | # | ||
# return: True/False if programm is active/not active | # return: True/False if programm is active/not active | ||
Zeile 292: | Zeile 292: | ||
else: | else: | ||
return False | 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(','))): | ||
Zeile 325: | Zeile 325: | ||
# | # | ||
# read_prog (config, section, call_from) | # read_prog (config, section, call_from) | ||
# config: | # config: config file | ||
# section: | # section: section in config file to read | ||
# call_from: caller number | # call_from: caller number | ||
# | # | ||
Zeile 339: | Zeile 339: | ||
prog = "prog" + str(i) | prog = "prog" + str(i) | ||
while config.has_option(section, prog): | while config.has_option(section, prog): | ||
# progX = [dates, times, weekdays, message, delay, length, callers] | # progX = [dates, times, weekdays, message, delay, length, callers] | ||
p = config.get(section, prog).split() | p = config.get(section, prog).split() | ||
if prog_active(config, p[0], p[1], p[2]): | if prog_active(config, p[0], p[1], p[2]): | ||
Zeile 361: | Zeile 361: | ||
# | # | ||
# callIncoming(call,service,call_from,call_to) | # callIncoming(call,service,call_from,call_to) | ||
# call | # call reference to the call. Needed by all capisuite functions | ||
# service | # service one of SERVICE_FAXG3, SERVICE_VOICE, SERVICE_OTHER | ||
# call_from string containing the number of the calling party | # call_from string containing the number of the calling party | ||
# call_to | # 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 | |||
try: | |||
locale.setlocale(locale.LC_ALL, 'de_DE') | 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 | # 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 | # 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 | # XXX: neobiker's _adv | ||
user_prog = read_prog(config, curr_user, call_from) | user_prog = read_prog(config, curr_user, call_from) | ||
Zeile 420: | Zeile 420: | ||
delay = user_prog['delay'] | delay = user_prog['delay'] | ||
else: | else: | ||
delay=cs_helpers.getOption(config,curr_user,"voice_delay") | |||
caller_name = read_phonebook(call_from) | caller_name = read_phonebook(call_from) | ||
called_name = read_phonebook(call_to) | 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) | caller_name = read_phonebook(call_from) | ||
called_name = read_phonebook(call_to) | 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) | |||
# ------------------------------------------------------------------- | # ------------------------------------------------------------------- | ||
Zeile 449: | Zeile 449: | ||
def faxIncoming(call,call_from,caller_name,call_to,called_name,curr_user,config,already_connected): | 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() | 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 | # XXX: changed Mail text | ||
caller_details = "" | caller_details = "" | ||
if (caller_name == call_from): | if (caller_name == call_from): | ||
caller_name, caller_details = check_number_online (call_from) | caller_name, caller_details = check_number_online (call_from) | ||
cs_helpers.sendMIMEMail(fromaddress, mailaddress, | |||
"Fax von "+caller_name+" an "+called_name, faxFormat, | "Fax von "+caller_name+" an "+called_name, faxFormat, | ||
"Fax Sender: "+caller_name+" ("+call_from+")\n" | |||
+" "+caller_details+"\n" | |||
+"Datum: "+time.strftime("%c")+"\n\n" | +"Datum: "+time.strftime("%c")+"\n\n" | ||
+"Siehe Anhang.\nDas Fax wurde gespeichert unter: "+filename+"\n", | |||
filename) | filename) | ||
Zeile 536: | Zeile 536: | ||
def voiceIncoming(call,call_from,caller_name,call_to,called_name,curr_user,config): | 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 | # XXX: neobiker's _adv | ||
user_prog = read_prog(config, curr_user, call_from) | user_prog = read_prog(config, curr_user, call_from) | ||
Zeile 567: | Zeile 567: | ||
userannouncement = udir + user_prog['message'] | userannouncement = udir + user_prog['message'] | ||
else: | 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 | # XXX: neobiker's _adv | ||
if user_prog: | if user_prog: | ||
length = user_prog['length'] | length = user_prog['length'] | ||
else: | 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 | # XXX: changed Mail text | ||
caller_details = "" | caller_details = "" | ||
if (caller_name == call_from): | if (caller_name == call_from): | ||
caller_name, caller_details = check_number_online (call_from) | caller_name, caller_details = check_number_online (call_from) | ||
cs_helpers.sendMIMEMail(fromaddress, mailaddress, | |||
"Nachricht von "+caller_name+" fuer "+called_name, "la", | "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" | +"Laenge: "+str(os.stat(filename)[6]/8/1024)+" Sek.\n" | ||
+"Datum: "+time.strftime("%c")+"\n" | +"Datum: "+time.strftime("%c")+"\n" | ||
+"Nummer: "+called_name+" ("+call_to+")\n\n" | +"Nummer: "+called_name+" ("+call_to+")\n\n" | ||
+"Siehe Anhang.\nDas File wurde gespeichert unter: "+filename+"\n", | |||
filename) | filename) | ||
Zeile 660: | Zeile 660: | ||
# | # | ||
def remoteInquiry(call,userdir,curr_user,config): | 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') | 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") | |||
# --------------------------------------------------------------------------- | # --------------------------------------------------------------------------- | ||
Zeile 786: | Zeile 784: | ||
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,"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")) | |||
# | # | ||
Zeile 811: | Zeile 809: | ||
# Revision 1.8 2007/09/10 20:37:00 root | # Revision 1.8 2007/09/10 20:37:00 root | ||
# optimized read_phonebook() | # 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 | # Revision 1.1 2007/09/09 13:22:42 root | ||
# Initial revision | # 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 | |||
# | |||
# Revision 1.9.2.1 2003/08/24 12:47:19 gernot | # Revision 1.9.2.1 2003/08/24 12:47:19 gernot | ||
# - faxIncoming tried to reconnect when it was called after a switch from | # - faxIncoming tried to reconnect when it was called after a switch from | ||
Zeile 824: | Zeile 851: | ||
# - replaced german umlaut in filename "nachricht-gelscht.la", can cause | # - replaced german umlaut in filename "nachricht-gelscht.la", can cause | ||
# problems on Redhat, thx to Herbert Hübner for reporting | # 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 | # Revision 1.10 2002/11/18 12:32:36 ghillie | ||
# - callIncoming lives now in __main__, not necessarily in pcallcontrol any more | # - callIncoming lives now in __main__, not necessarily in pcallcontrol any more |
Version vom 25. April 2008, 19:47 Uhr
File: incoming_adv.py
# incoming_adv.py - advanced incoming script for capisuite # -------------------------------------------------------- # copyright : (c) 2007 by Neobiker # Version : $Revision: 1.8 $ # Date : $Date: 2007/09/10 20:37:00 $ # # 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 # ---------------------------------------- # check_number_online () # online reverse lookup # # check_number_online (number): # number: tel number # # return: name, details def check_number_online (number): """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: "+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) 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\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) 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.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 # # 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! # # 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 #