From 5565b244c983bfbfd8b88077eda690fd630e3f1f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 8 Jan 2004 21:22:53 +0000 Subject: renaming client -> clients git-svn-id: file:///home/lennart/svn/public/ivam2/trunk@19 dbf6933d-3bce-0310-9bcc-ed052ba35b35 --- client/ivam-autobox | 59 --------- client/ivam-createvb | 6 - client/ivam-echo | 3 - client/ivam-play | 3 - client/ivam-voicebox | 55 -------- client/ivamApi.py | 49 ------- client/ivamCore.py | 18 --- client/ivamDefs.py | 3 - client/ivamPipeConnector.py | 247 ----------------------------------- client/ivamShbufConnector.py | 26 ---- client/ivamUtil.py | 36 ------ client/ivamVoiceBox.py | 291 ------------------------------------------ clients/ivam-autobox | 59 +++++++++ clients/ivam-createvb | 6 + clients/ivam-echo | 3 + clients/ivam-play | 3 + clients/ivam-voicebox | 55 ++++++++ clients/ivamApi.py | 49 +++++++ clients/ivamCore.py | 18 +++ clients/ivamDefs.py | 3 + clients/ivamPipeConnector.py | 247 +++++++++++++++++++++++++++++++++++ clients/ivamShbufConnector.py | 26 ++++ clients/ivamUtil.py | 36 ++++++ clients/ivamVoiceBox.py | 291 ++++++++++++++++++++++++++++++++++++++++++ 24 files changed, 796 insertions(+), 796 deletions(-) delete mode 100755 client/ivam-autobox delete mode 100755 client/ivam-createvb delete mode 100755 client/ivam-echo delete mode 100755 client/ivam-play delete mode 100755 client/ivam-voicebox delete mode 100644 client/ivamApi.py delete mode 100644 client/ivamCore.py delete mode 100644 client/ivamDefs.py delete mode 100644 client/ivamPipeConnector.py delete mode 100644 client/ivamShbufConnector.py delete mode 100644 client/ivamUtil.py delete mode 100644 client/ivamVoiceBox.py create mode 100755 clients/ivam-autobox create mode 100755 clients/ivam-createvb create mode 100755 clients/ivam-echo create mode 100755 clients/ivam-play create mode 100755 clients/ivam-voicebox create mode 100644 clients/ivamApi.py create mode 100644 clients/ivamCore.py create mode 100644 clients/ivamDefs.py create mode 100644 clients/ivamPipeConnector.py create mode 100644 clients/ivamShbufConnector.py create mode 100644 clients/ivamUtil.py create mode 100644 clients/ivamVoiceBox.py diff --git a/client/ivam-autobox b/client/ivam-autobox deleted file mode 100755 index bb7fcfc..0000000 --- a/client/ivam-autobox +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/python - -import sys, os, getopt - -from ivamCore import log -import ivamCore, ivamVoiceBox - -def usage(): - log("%s [--record-time=SECS] [--pin=PIN] [--pin-file=PINFILE] [--debug] [DIRECTORY]" % sys.argv[0]) - -def parseArgs(vb, argv): - - try: - opts, args = getopt.getopt(argv[1:], "ht:p:P:de:", ["help", "record-time=", "pin=", "pin-file=", "debug", "message-program"]) - except getopt.GetoptError: - usage() - sys.exit(1) - - recordTime = 60 - pin = "-" - - for o, a in opts: - if o in ("-d", "--debug"): - ivamCore.DEBUG = True - - elif o in ("-h", "--help"): - usage() - sys.exit() - - elif o in ("-t", "--record-time"): - recordTime = int(a) - - elif o in ("-p", "--pin"): - pin = a - - elif o in ("-P", "--pin-file"): - pin = getContents(a) - - dname = "msn-" + os.getenv("RINGMSN") - - if len(args): - dname = args[0] + "/" + dname - - try: - ivamVoiceBox.setupVoiceBox(dname, pin, recordTime) - except OSError: - pass - - vb.setDirectory(dname) - -def main(): - vb = ivamVoiceBox.VoiceBox() - parseArgs(vb, sys.argv) - ivamCore.newConnector(vb).run() - sys.exit() - -if __name__ == "__main__": - main() - diff --git a/client/ivam-createvb b/client/ivam-createvb deleted file mode 100755 index 7365db6..0000000 --- a/client/ivam-createvb +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/python - -import ivamVoiceBox - -ivamVoiceBox.setupVoiceBox("lennart") - diff --git a/client/ivam-echo b/client/ivam-echo deleted file mode 100755 index b0f3e40..0000000 --- a/client/ivam-echo +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -exec cat diff --git a/client/ivam-play b/client/ivam-play deleted file mode 100755 index 043ffbe..0000000 --- a/client/ivam-play +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -exec zcat -f "$1" diff --git a/client/ivam-voicebox b/client/ivam-voicebox deleted file mode 100755 index fbf6afa..0000000 --- a/client/ivam-voicebox +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python - -import sys, os, getopt - -from ivamCore import log -import ivamCore, ivamVoiceBox - -def usage(): - log("%s [--record-time=SECS] [--pin=PIN] [--pin-file=PINFILE] [--message-program=BINARY] [--debug] DIRECTORY" % sys.argv[0]) - -def parseArgs(vb, argv): - - try: - opts, args = getopt.getopt(argv[1:], "ht:p:P:de:", ["help", "record-time=", "pin=", "pin-file=", "debug", "message-program"]) - except getopt.GetoptError: - usage() - sys.exit(1) - - try: - dname = args[0] - except IndexError: - usage() - sys.exit(1) - - vb.setDirectory(dname) - - for o, a in opts: - if o in ("-d", "--debug"): - ivamCore.DEBUG = True - - elif o in ("-h", "--help"): - usage() - sys.exit() - - elif o in ("-t", "--record-time"): - vb.recordTime = int(a) - - elif o in ("-p", "--pin"): - vb.setPin(a) - - elif o in ("-P", "--pin-file"): - vb.setPin(getContents(a)) - - elif o in ("-e", "--message-program"): - vb.messageProgram = a - - -def main(): - vb = ivamVoiceBox.VoiceBox() - parseArgs(vb, sys.argv) - ivamCore.newConnector(vb).run() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/client/ivamApi.py b/client/ivamApi.py deleted file mode 100644 index 8cab6f9..0000000 --- a/client/ivamApi.py +++ /dev/null @@ -1,49 +0,0 @@ - -class Processor: - - def onConnect(self, c, callerNumber, ringNumber): - pass - - def onHangup(self, c): - pass - - def onDtmfEvent(self, c, event): - pass - - def onClipFinish(self, c, fname): - pass - - def onTimeout(self, c): - pass - - def onRecordFinish(self, c, fname): - pass - -class Connector: - - def run(self): - pass - - def playClip(self, fname): - pass - - def stopPlayback(self): - pass - - def stopPlayback2(self): # Same as previous but call onClipFinish - pass - - def recordClip(self, fname, gzip = False): - pass - - def stopRecording(self): - pass - - def setTimeout(self, t): - pass - - def hangup(self, t): - pass - - def flushOutput(self): - pass diff --git a/client/ivamCore.py b/client/ivamCore.py deleted file mode 100644 index 972c156..0000000 --- a/client/ivamCore.py +++ /dev/null @@ -1,18 +0,0 @@ - -import sys, os - -import ivamPipeConnector - -DEBUG = False - -def newConnector(p): - - if os.getenv("SHBUF") is None: - return ivamPipeConnector.PipeConnector(p) - else: - return None -# return ShbufConnector(c) - -def log(s): - sys.stderr.write("%s\n" % s) - diff --git a/client/ivamDefs.py b/client/ivamDefs.py deleted file mode 100644 index 0804e45..0000000 --- a/client/ivamDefs.py +++ /dev/null @@ -1,3 +0,0 @@ - -spoolDirectory = "/home/lennart/tmp/ivam-spool" -shareDirectory = "/home/lennart/projects/ivam-reloaded/ulaw" diff --git a/client/ivamPipeConnector.py b/client/ivamPipeConnector.py deleted file mode 100644 index af302b8..0000000 --- a/client/ivamPipeConnector.py +++ /dev/null @@ -1,247 +0,0 @@ - -import os, sys, time, select, signal, gzip - -import ivamCore, ivamApi, ivamUtil - -pc = None - -def sigterm(*args): - pc.quit = 1 - -class PipeConnector(ivamApi.Connector): - - BUFSIZE = 128 - - stdin = sys.stdin - stdout = sys.stdout - - playing = False - recording = False - - timeout = 0 - quit = False - - def __init__(self, processor): - - if ivamCore.DEBUG: - ivamCore.log("PipeConnector()") - - global pc - pc = self - signal.signal(signal.SIGTERM, sigterm) - - self.callerNumber = os.getenv("CALLERMSN") - self.ringNumber = os.getenv("RINGMSN") - self.processor = processor - - try: - s = os.fstat(128) - self.pipeHack = True - ivamCore.log("Found and enabled pipe hack.") - except OSError: - self.pipeHack = False - ivamCore.log("Pipe hack not detected.") - - def openDtmf(self): - - try: - fn = os.environ["DTMFFIFO"] - except KeyError: - self.dtmfFifo = None - return - - self.dtmfFifo = file(fn, "rb") - - def playClip(self, fname): - - self.stopPlayback() - - if ivamCore.DEBUG: - ivamCore.log("playClip('%s')" % fname) - - self.playFile = ivamUtil.magicFile(fname, "rb") - self.playName = fname - self.playing = True - self.playBuffer = None - - def stopPlayback(self): - - if ivamCore.DEBUG: - ivamCore.log("stopPlayback()") - - if self.playing: - self.playFile.close() - self.playing = False - - def stopPlayback2(self): - - self.stopPlayback() - self.processor.onClipFinish(self, self.playName) - - def recordClip(self, fname, z = False): - - self.stopRecording() - - if ivamCore.DEBUG: - ivamCore.log("recordClip('%s')" % fname) - - if z: - self.recordFile = gzip.open(fname, "w+b") - else: - self.recordFile = open(fname, "w+b") - - self.recordName = fname - self.recording = True - - def stopRecording(self): - - if ivamCore.DEBUG: - ivamCore.log("stopRecording()") - - if self.recording: - self.recordFile.close() - self.recording = False - - self.processor.onRecordFinish(self, self.recordName) - - def setTimeout(self, t): - - if ivamCore.DEBUG: - ivamCore.log("timeout(%u)" % t) - - if t: - self.timeout = time.time()+t - else: - self.timeout = 0 - - - def run(self): - - if ivamCore.DEBUG: - ivamCore.log("run()") - - self.openDtmf() - self.processor.onConnect(self, self.callerNumber, self.ringNumber) - self.loop() - self.stopPlayback() - self.stopRecording() - self.processor.onHangup(self) - - if not self.dtmfFifo is None: - self.dtmfFifo.close() - - if ivamCore.DEBUG: - ivamCore.log("run() finished") - - def hangup(self): - - if ivamCore.DEBUG: - ivamCore.log("hangup()") - - self.quit = True - - def loop(self): - - while not self.quit: - - t = None - now = time.time() - - if self.timeout: - t = self.timeout - now - - if t < 0: - t = 0 - - o = [] - i = [self.stdin] - - if not self.dtmfFifo is None: - i += [self.dtmfFifo] - - if self.playing: - o += [self.stdout] - - if t is None: - i, o, x = select.select(i, o, []) - else: - i, o, x = select.select(i, o, [], t) - - now = time.time() - - if self.timeout and self.timeout < now: - self.processor.onTimeout(self) - self.timeout = 0 - if self.quit: break - - # Check DTMF fifo - if not self.dtmfFifo is None and self.dtmfFifo in i: - self.handleDtmf() - if self.quit: break - - # Check STDOUT - if self.stdout in o: - self.writeStdout() - if self.quit: break - - # Check STDIN - if self.stdin in i: - self.readStdin() - if self.quit: break - - - def readStdin(self): - - buf = os.read(self.stdin.fileno(), self.BUFSIZE) - - if buf == "": - self.quit = 1 - elif self.recording: - self.recordFile.write(buf) - - - def writeStdout(self): - - if not self.playing: - return - - if self.playBuffer is None: - - self.playBuffer = self.playFile.read(self.BUFSIZE) - - # EOF? - if len(self.playBuffer) == 0: - self.stopPlayback2() - return - - c = os.write(self.stdout.fileno(), self.playBuffer) - self.playBuffer = self.playBuffer[c:] - - if len(self.playBuffer) == 0: - self.playBuffer = None - - def handleDtmf(self): - - if not self.dtmfFifo is None: - - d = self.dtmfFifo.read(1) - - if d == "": - self.dtmfFifo.close() - self.dtmfFifo = None - else: - self.processor.onDtmfEvent(self, d) - - def flushOutput(self): - - if not self.pipeHack: - return - - try: - b = os.read(128, 4096) - if ivamCore.DEBUG: - ivamCore.log("Pipe hack succeeded") - except OSError: - - if ivamCore.DEBUG: - ivamCore.log("Pipe hack failed") diff --git a/client/ivamShbufConnector.py b/client/ivamShbufConnector.py deleted file mode 100644 index feef521..0000000 --- a/client/ivamShbufConnector.py +++ /dev/null @@ -1,26 +0,0 @@ - -from ivamApi import ivamConnector - -class ivamShbufConnector(ivamAnsweringMachine): - - def run(self): - pass - - def playClip(self): - pass - - def stopPlayback(self): - pass - - def softStopPlayback(self): - pass - - def recordClip(self): - pass - - def stopRecording(self): - pass - - def timeout(self): - pass - diff --git a/client/ivamUtil.py b/client/ivamUtil.py deleted file mode 100644 index 053638e..0000000 --- a/client/ivamUtil.py +++ /dev/null @@ -1,36 +0,0 @@ - -import gzip - -def getContents(fn): - f = file(fn, "r") - r = f.readline().strip(" \t\r\n") - f.close() - return r - - -def setContents(fn, s): - f = file(fn, "w") - f.write("%s\n" % s) - f.close() - -def magicFile(fn, mode): - - f = None - - try: - f = gzip.open(fn, mode) - f.read(1) - f.seek(0) - return f - - except IOError: - - if not f is None: - f.close() - - return open(fn, mode) - - - - - diff --git a/client/ivamVoiceBox.py b/client/ivamVoiceBox.py deleted file mode 100644 index 252ce98..0000000 --- a/client/ivamVoiceBox.py +++ /dev/null @@ -1,291 +0,0 @@ - -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" - - 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 - - self.currentState = self.STATE_WELCOME - c.playClip(self.getClip("welcome")) - - 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 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: - - # Silence ... - pass - - 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'): - - if self.pin == "": - self.loginComplete(c) - - elif self.pin == "-": - self.currentState = self.STATE_FINISH - c.hangup() - - else: - self.currentState = self.STATE_AUTH - self.inputPin = "" - c.stopPlayback() - c.playClip(self.getClip("auth")) - c.setTimeout(self.AUTH_TIMEOUT) - - 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): - - 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 - ivamCore.log("Starting new message notification program.") - 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 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"): - if dname.find("/") == -1: - dname = "%s/%s" % (ivamDefs.spoolDirectory, dname) - - os.mkdir(dname, 0770) - os.mkdir("%s/messages" % dname, 0770) - - 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 + ['newmessage', 'README']: - os.symlink("%s/%s" % (ivamDefs.shareDirectory, f), "%s/%s" % (dname, f)) - diff --git a/clients/ivam-autobox b/clients/ivam-autobox new file mode 100755 index 0000000..bb7fcfc --- /dev/null +++ b/clients/ivam-autobox @@ -0,0 +1,59 @@ +#!/usr/bin/python + +import sys, os, getopt + +from ivamCore import log +import ivamCore, ivamVoiceBox + +def usage(): + log("%s [--record-time=SECS] [--pin=PIN] [--pin-file=PINFILE] [--debug] [DIRECTORY]" % sys.argv[0]) + +def parseArgs(vb, argv): + + try: + opts, args = getopt.getopt(argv[1:], "ht:p:P:de:", ["help", "record-time=", "pin=", "pin-file=", "debug", "message-program"]) + except getopt.GetoptError: + usage() + sys.exit(1) + + recordTime = 60 + pin = "-" + + for o, a in opts: + if o in ("-d", "--debug"): + ivamCore.DEBUG = True + + elif o in ("-h", "--help"): + usage() + sys.exit() + + elif o in ("-t", "--record-time"): + recordTime = int(a) + + elif o in ("-p", "--pin"): + pin = a + + elif o in ("-P", "--pin-file"): + pin = getContents(a) + + dname = "msn-" + os.getenv("RINGMSN") + + if len(args): + dname = args[0] + "/" + dname + + try: + ivamVoiceBox.setupVoiceBox(dname, pin, recordTime) + except OSError: + pass + + vb.setDirectory(dname) + +def main(): + vb = ivamVoiceBox.VoiceBox() + parseArgs(vb, sys.argv) + ivamCore.newConnector(vb).run() + sys.exit() + +if __name__ == "__main__": + main() + diff --git a/clients/ivam-createvb b/clients/ivam-createvb new file mode 100755 index 0000000..7365db6 --- /dev/null +++ b/clients/ivam-createvb @@ -0,0 +1,6 @@ +#!/usr/bin/python + +import ivamVoiceBox + +ivamVoiceBox.setupVoiceBox("lennart") + diff --git a/clients/ivam-echo b/clients/ivam-echo new file mode 100755 index 0000000..b0f3e40 --- /dev/null +++ b/clients/ivam-echo @@ -0,0 +1,3 @@ +#!/bin/sh + +exec cat diff --git a/clients/ivam-play b/clients/ivam-play new file mode 100755 index 0000000..043ffbe --- /dev/null +++ b/clients/ivam-play @@ -0,0 +1,3 @@ +#!/bin/sh + +exec zcat -f "$1" diff --git a/clients/ivam-voicebox b/clients/ivam-voicebox new file mode 100755 index 0000000..fbf6afa --- /dev/null +++ b/clients/ivam-voicebox @@ -0,0 +1,55 @@ +#!/usr/bin/python + +import sys, os, getopt + +from ivamCore import log +import ivamCore, ivamVoiceBox + +def usage(): + log("%s [--record-time=SECS] [--pin=PIN] [--pin-file=PINFILE] [--message-program=BINARY] [--debug] DIRECTORY" % sys.argv[0]) + +def parseArgs(vb, argv): + + try: + opts, args = getopt.getopt(argv[1:], "ht:p:P:de:", ["help", "record-time=", "pin=", "pin-file=", "debug", "message-program"]) + except getopt.GetoptError: + usage() + sys.exit(1) + + try: + dname = args[0] + except IndexError: + usage() + sys.exit(1) + + vb.setDirectory(dname) + + for o, a in opts: + if o in ("-d", "--debug"): + ivamCore.DEBUG = True + + elif o in ("-h", "--help"): + usage() + sys.exit() + + elif o in ("-t", "--record-time"): + vb.recordTime = int(a) + + elif o in ("-p", "--pin"): + vb.setPin(a) + + elif o in ("-P", "--pin-file"): + vb.setPin(getContents(a)) + + elif o in ("-e", "--message-program"): + vb.messageProgram = a + + +def main(): + vb = ivamVoiceBox.VoiceBox() + parseArgs(vb, sys.argv) + ivamCore.newConnector(vb).run() + sys.exit() + +if __name__ == "__main__": + main() diff --git a/clients/ivamApi.py b/clients/ivamApi.py new file mode 100644 index 0000000..8cab6f9 --- /dev/null +++ b/clients/ivamApi.py @@ -0,0 +1,49 @@ + +class Processor: + + def onConnect(self, c, callerNumber, ringNumber): + pass + + def onHangup(self, c): + pass + + def onDtmfEvent(self, c, event): + pass + + def onClipFinish(self, c, fname): + pass + + def onTimeout(self, c): + pass + + def onRecordFinish(self, c, fname): + pass + +class Connector: + + def run(self): + pass + + def playClip(self, fname): + pass + + def stopPlayback(self): + pass + + def stopPlayback2(self): # Same as previous but call onClipFinish + pass + + def recordClip(self, fname, gzip = False): + pass + + def stopRecording(self): + pass + + def setTimeout(self, t): + pass + + def hangup(self, t): + pass + + def flushOutput(self): + pass diff --git a/clients/ivamCore.py b/clients/ivamCore.py new file mode 100644 index 0000000..972c156 --- /dev/null +++ b/clients/ivamCore.py @@ -0,0 +1,18 @@ + +import sys, os + +import ivamPipeConnector + +DEBUG = False + +def newConnector(p): + + if os.getenv("SHBUF") is None: + return ivamPipeConnector.PipeConnector(p) + else: + return None +# return ShbufConnector(c) + +def log(s): + sys.stderr.write("%s\n" % s) + diff --git a/clients/ivamDefs.py b/clients/ivamDefs.py new file mode 100644 index 0000000..0804e45 --- /dev/null +++ b/clients/ivamDefs.py @@ -0,0 +1,3 @@ + +spoolDirectory = "/home/lennart/tmp/ivam-spool" +shareDirectory = "/home/lennart/projects/ivam-reloaded/ulaw" diff --git a/clients/ivamPipeConnector.py b/clients/ivamPipeConnector.py new file mode 100644 index 0000000..af302b8 --- /dev/null +++ b/clients/ivamPipeConnector.py @@ -0,0 +1,247 @@ + +import os, sys, time, select, signal, gzip + +import ivamCore, ivamApi, ivamUtil + +pc = None + +def sigterm(*args): + pc.quit = 1 + +class PipeConnector(ivamApi.Connector): + + BUFSIZE = 128 + + stdin = sys.stdin + stdout = sys.stdout + + playing = False + recording = False + + timeout = 0 + quit = False + + def __init__(self, processor): + + if ivamCore.DEBUG: + ivamCore.log("PipeConnector()") + + global pc + pc = self + signal.signal(signal.SIGTERM, sigterm) + + self.callerNumber = os.getenv("CALLERMSN") + self.ringNumber = os.getenv("RINGMSN") + self.processor = processor + + try: + s = os.fstat(128) + self.pipeHack = True + ivamCore.log("Found and enabled pipe hack.") + except OSError: + self.pipeHack = False + ivamCore.log("Pipe hack not detected.") + + def openDtmf(self): + + try: + fn = os.environ["DTMFFIFO"] + except KeyError: + self.dtmfFifo = None + return + + self.dtmfFifo = file(fn, "rb") + + def playClip(self, fname): + + self.stopPlayback() + + if ivamCore.DEBUG: + ivamCore.log("playClip('%s')" % fname) + + self.playFile = ivamUtil.magicFile(fname, "rb") + self.playName = fname + self.playing = True + self.playBuffer = None + + def stopPlayback(self): + + if ivamCore.DEBUG: + ivamCore.log("stopPlayback()") + + if self.playing: + self.playFile.close() + self.playing = False + + def stopPlayback2(self): + + self.stopPlayback() + self.processor.onClipFinish(self, self.playName) + + def recordClip(self, fname, z = False): + + self.stopRecording() + + if ivamCore.DEBUG: + ivamCore.log("recordClip('%s')" % fname) + + if z: + self.recordFile = gzip.open(fname, "w+b") + else: + self.recordFile = open(fname, "w+b") + + self.recordName = fname + self.recording = True + + def stopRecording(self): + + if ivamCore.DEBUG: + ivamCore.log("stopRecording()") + + if self.recording: + self.recordFile.close() + self.recording = False + + self.processor.onRecordFinish(self, self.recordName) + + def setTimeout(self, t): + + if ivamCore.DEBUG: + ivamCore.log("timeout(%u)" % t) + + if t: + self.timeout = time.time()+t + else: + self.timeout = 0 + + + def run(self): + + if ivamCore.DEBUG: + ivamCore.log("run()") + + self.openDtmf() + self.processor.onConnect(self, self.callerNumber, self.ringNumber) + self.loop() + self.stopPlayback() + self.stopRecording() + self.processor.onHangup(self) + + if not self.dtmfFifo is None: + self.dtmfFifo.close() + + if ivamCore.DEBUG: + ivamCore.log("run() finished") + + def hangup(self): + + if ivamCore.DEBUG: + ivamCore.log("hangup()") + + self.quit = True + + def loop(self): + + while not self.quit: + + t = None + now = time.time() + + if self.timeout: + t = self.timeout - now + + if t < 0: + t = 0 + + o = [] + i = [self.stdin] + + if not self.dtmfFifo is None: + i += [self.dtmfFifo] + + if self.playing: + o += [self.stdout] + + if t is None: + i, o, x = select.select(i, o, []) + else: + i, o, x = select.select(i, o, [], t) + + now = time.time() + + if self.timeout and self.timeout < now: + self.processor.onTimeout(self) + self.timeout = 0 + if self.quit: break + + # Check DTMF fifo + if not self.dtmfFifo is None and self.dtmfFifo in i: + self.handleDtmf() + if self.quit: break + + # Check STDOUT + if self.stdout in o: + self.writeStdout() + if self.quit: break + + # Check STDIN + if self.stdin in i: + self.readStdin() + if self.quit: break + + + def readStdin(self): + + buf = os.read(self.stdin.fileno(), self.BUFSIZE) + + if buf == "": + self.quit = 1 + elif self.recording: + self.recordFile.write(buf) + + + def writeStdout(self): + + if not self.playing: + return + + if self.playBuffer is None: + + self.playBuffer = self.playFile.read(self.BUFSIZE) + + # EOF? + if len(self.playBuffer) == 0: + self.stopPlayback2() + return + + c = os.write(self.stdout.fileno(), self.playBuffer) + self.playBuffer = self.playBuffer[c:] + + if len(self.playBuffer) == 0: + self.playBuffer = None + + def handleDtmf(self): + + if not self.dtmfFifo is None: + + d = self.dtmfFifo.read(1) + + if d == "": + self.dtmfFifo.close() + self.dtmfFifo = None + else: + self.processor.onDtmfEvent(self, d) + + def flushOutput(self): + + if not self.pipeHack: + return + + try: + b = os.read(128, 4096) + if ivamCore.DEBUG: + ivamCore.log("Pipe hack succeeded") + except OSError: + + if ivamCore.DEBUG: + ivamCore.log("Pipe hack failed") diff --git a/clients/ivamShbufConnector.py b/clients/ivamShbufConnector.py new file mode 100644 index 0000000..feef521 --- /dev/null +++ b/clients/ivamShbufConnector.py @@ -0,0 +1,26 @@ + +from ivamApi import ivamConnector + +class ivamShbufConnector(ivamAnsweringMachine): + + def run(self): + pass + + def playClip(self): + pass + + def stopPlayback(self): + pass + + def softStopPlayback(self): + pass + + def recordClip(self): + pass + + def stopRecording(self): + pass + + def timeout(self): + pass + diff --git a/clients/ivamUtil.py b/clients/ivamUtil.py new file mode 100644 index 0000000..053638e --- /dev/null +++ b/clients/ivamUtil.py @@ -0,0 +1,36 @@ + +import gzip + +def getContents(fn): + f = file(fn, "r") + r = f.readline().strip(" \t\r\n") + f.close() + return r + + +def setContents(fn, s): + f = file(fn, "w") + f.write("%s\n" % s) + f.close() + +def magicFile(fn, mode): + + f = None + + try: + f = gzip.open(fn, mode) + f.read(1) + f.seek(0) + return f + + except IOError: + + if not f is None: + f.close() + + return open(fn, mode) + + + + + diff --git a/clients/ivamVoiceBox.py b/clients/ivamVoiceBox.py new file mode 100644 index 0000000..252ce98 --- /dev/null +++ b/clients/ivamVoiceBox.py @@ -0,0 +1,291 @@ + +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" + + 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 + + self.currentState = self.STATE_WELCOME + c.playClip(self.getClip("welcome")) + + 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 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: + + # Silence ... + pass + + 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'): + + if self.pin == "": + self.loginComplete(c) + + elif self.pin == "-": + self.currentState = self.STATE_FINISH + c.hangup() + + else: + self.currentState = self.STATE_AUTH + self.inputPin = "" + c.stopPlayback() + c.playClip(self.getClip("auth")) + c.setTimeout(self.AUTH_TIMEOUT) + + 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): + + 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 + ivamCore.log("Starting new message notification program.") + 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 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"): + if dname.find("/") == -1: + dname = "%s/%s" % (ivamDefs.spoolDirectory, dname) + + os.mkdir(dname, 0770) + os.mkdir("%s/messages" % dname, 0770) + + 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 + ['newmessage', 'README']: + os.symlink("%s/%s" % (ivamDefs.shareDirectory, f), "%s/%s" % (dname, f)) + -- cgit