# $Id$ # # This file is part of ivam2. # # ivam2 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. # # ivam2 is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with ivam2; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. import getopt, sys, os, time, re import ivamApi, ivamCore, ivamDefs from ivamUtil import getContents, setContents class VoiceBox(ivamApi.Processor): # Constants STATE_INVALID, STATE_WELCOME, STATE_WELCOME_BEEP, STATE_RECORD, STATE_PRE_FINISH, STATE_FINISH, STATE_AUTH, STATE_MESSAGE, STATE_AUTH_OK, STATE_NO_MORE_MESSAGES, STATE_MESSAGE_BEEP, STATE_REMOVED, STATE_EMPTY = range(13) AUTH_TIMEOUT = 20 # State currentState = STATE_INVALID fileSuffix = ".ulaw.gz" recordOnly = False noRecord = False welcomeMessage = "welcome" def getClip(self, s): return "%s/%s%s" % (self.directory, s, self.fileSuffix) def onConnect(self, c, callerNumber, ringNumber): if ivamCore.DEBUG: ivamCore.log("onConnect()") self.callerNumber = callerNumber self.ringNumber = ringNumber if self.noRecord: self.authNow(c) else: self.currentState = self.STATE_WELCOME c.playClip(self.getClip(self.welcomeMessage)) def loginComplete(self, c): self.messages = self.getMessageNames() self.currentMessage = 0 ivamCore.log("Successful login from %s for MSN %s." % (self.callerNumber, self.ringNumber)) if len(self.messages) == 0: self.currentState = self.STATE_EMPTY c.playClip(self.getClip("empty")) else: self.currentState = self.STATE_MESSAGE_BEEP c.playClip(self.getClip("beep")) def authNow(self, c): self.currentState = self.STATE_AUTH self.inputPin = "" c.stopPlayback() c.playClip(self.getClip("auth")) c.setTimeout(self.AUTH_TIMEOUT) def onClipFinish(self, c, fname): if ivamCore.DEBUG: ivamCore.log("onClipFinish('%s')" % fname) if self.currentState == self.STATE_WELCOME: self.currentState = self.STATE_WELCOME_BEEP; c.playClip(self.getClip("beep")) elif self.currentState == self.STATE_WELCOME_BEEP: self.currentState = self.STATE_RECORD c.recordClip(self.nextMessageName(), True) c.setTimeout(self.recordTime) elif self.currentState == self.STATE_PRE_FINISH: self.currentState = self.STATE_FINISH c.hangup() elif self.currentState == self.STATE_AUTH: # Silence ... pass elif self.currentState == self.STATE_AUTH_OK: self.loginComplete(c) elif self.currentState == self.STATE_MESSAGE_BEEP: while True: if self.currentMessage >= len(self.messages): self.currentState = self.STATE_NO_MORE_MESSAGES c.playClip(self.getClip("nomoremessages")) else: self.currentState = self.STATE_MESSAGE try: c.playClip(self.messages[self.currentMessage]) except IOError, e: del(self.messages[self.currentMessage]) if len(self.messages) > 0: continue self.currentState = self.STATE_EMPTY c.playClip(self.getClip("empty")) break elif self.currentState == self.STATE_MESSAGE: self.currentMessage +=1 self.currentState = self.STATE_MESSAGE_BEEP c.playClip(self.getClip("beep")) elif self.currentState == self.STATE_REMOVED: if len(self.messages) == 0: self.currentState = self.STATE_EMPTY c.playClip(self.getClip("empty")) else: self.currentState = self.STATE_MESSAGE_BEEP c.playClip(self.getClip("beep")) elif self.currentState == self.STATE_NO_MORE_MESSAGES: # Silence ... pass elif self.currentState == self.STATE_EMPTY: c.hangup(); def onDtmfEvent(self, c, event): if ivamCore.DEBUG: ivamCore.log("onDtmfEvent(%c)" % event) if ((self.currentState == self.STATE_WELCOME) or (self.currentState == self.STATE_WELCOME_BEEP)) and (event == '0') and not self.recordOnly: if self.pin == "": self.loginComplete(c) elif self.pin == "-": self.currentState = self.STATE_FINISH c.hangup() else: self.authNow(c) elif self.currentState == self.STATE_AUTH: c.stopPlayback() self.inputPin += event if len(self.inputPin) >= len(self.pin): c.setTimeout(0) if self.inputPin == self.pin: self.currentState = self.STATE_AUTH_OK c.playClip(self.getClip("authok")) else: self.currentState = self.STATE_FINISH c.hangup() elif self.currentState in [self.STATE_MESSAGE, self.STATE_MESSAGE_BEEP, self.STATE_NO_MORE_MESSAGES] : if event == '0': if self.currentMessage >= len(self.messages): self.currentMessage = len(self.messages)-1 os.remove(self.messages[self.currentMessage]) ivamCore.log("Sucessfully removed message '%s' on behalf of %s for MSN %s." % (self.messages[self.currentMessage], self.callerNumber, self.ringNumber)) del(self.messages[self.currentMessage]) self.currentState = self.STATE_REMOVED c.playClip(self.getClip("removed")) else: changed = False if event in ['4', '2']: self.currentMessage -= 1 changed = True if event in ['6', '8']: self.currentMessage += 1 changed = True if event == '5': changed = True if event == '1': self.currentMessage = 0 changed = True if event == '7': self.currentMessage = len(self.messages)-1 changed = True if self.currentMessage < 0: self.currentMessage = 0 if self.currentMessage >= len(self.messages): self.currentMessage = len(self.messages)-1 if changed: self.currentState = self.STATE_MESSAGE_BEEP c.playClip(self.getClip("beep")) else: c.flushOutput() c.stopPlayback2() def onTimeout(self, c): if ivamCore.DEBUG: ivamCore.log("onTimeout()") if self.currentState == self.STATE_RECORD: self.currentState = self.STATE_PRE_FINISH c.stopRecording() c.playClip(self.getClip("beep")) elif self.currentState == self.STATE_AUTH: self.currentState = self.STATE_FINISH c.hangup() def onRecordFinish(self, c, fname, length): if ivamCore.DEBUG: ivamCore.log("onRecordFinish(%s)" % fname) ivamCore.log("Sucessfully recorded new message '%s' from %s for MSN %s." % (fname, self.callerNumber, self.ringNumber)) os.environ["SPOOLDIR"] = self.directory os.environ["LENGTH"] = `c.recordLength` os.environ["SEC_LENGTH"] = "%0.1f" % (c.recordLength/8000.0) ivamCore.log("Starting new message notification program ('%s %s')." % (self.messageProgram, fname)) r=os.spawnvp(os.P_WAIT, self.messageProgram, (self.messageProgram, fname)) ivamCore.log("Program finished (return value is %i)." % r) def getMessageNames(self): f = os.listdir(self.messageDirectory) f = map(lambda e: "%s/%s" % (self.messageDirectory, e), f) f.sort() f.reverse() return f def nextMessageName(self): return "%s/%010u:%s:%s%s" % (self.messageDirectory, time.time(), self.ringNumber, self.callerNumber, self.fileSuffix) def setWelcomeMessage(self, welcomeMessage): if ivamCore.DEBUG: ivamCore.log("setWelcomeMessage('%s')" % welcomeMessage) self.welcomeMessage = welcomeMessage def setPin(self, pin): if ivamCore.DEBUG: ivamCore.log("setPin('%s')" % pin) if re.match('^([0-9#*]*|-)$', pin).end() is None: ivamCore.log("Invalid PIN. PIN has to consist of 0-9#*. Use '-' for always denying access.") raise Exception, "Invalid PIN" self.pin = pin def setDirectory(self, dname): if dname.find("/") == -1: dname = "%s/%s" % (ivamDefs.spoolDirectory, dname) self.directory = dname self.messageDirectory = dname + "/messages" try: self.setPin(getContents("%s/PIN" % dname)) except Exception: self.pin = "-" try: self.recordTime = int(getContents("%s/RECORD_TIME" % dname)) except Exception: self.recordTime = 60 self.messageProgram = "%s/newmessage" % dname def setupVoiceBox(dname, pin = "-", recordTime = 60, email = "root", umask = 0007): u = os.umask(umask) try: if dname.find("/") == -1: dname = "%s/%s" % (ivamDefs.spoolDirectory, dname) os.mkdir(dname, 0777) os.mkdir("%s/messages" % dname, 0777) setContents("%s/PIN" % dname, pin) setContents("%s/RECORD_TIME" % dname, `recordTime`) setContents("%s/EMAIL" % dname, email) ulaw = map(lambda e: e+".ulaw.gz", ('welcome', 'beep', 'empty', 'nomoremessages', 'auth', 'authok', 'removed')) for f in ulaw: os.symlink("%s/%s" % (ivamDefs.ulawDirectory, f), "%s/%s" % (dname, f)) os.symlink("%s/%s" % (ivamDefs.binDirectory, "ivam-newmessage"), "%s/%s" % (dname, "newmessage")) os.symlink("%s/%s" % (ivamDefs.shareDirectory, "README.VoiceBox"), "%s/%s" % (dname, "README")) return dname finally: os.umask(u)