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
#