summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2004-01-05 22:26:34 +0000
committerLennart Poettering <lennart@poettering.net>2004-01-05 22:26:34 +0000
commit444b524c3a26d8dd83fae2a074d3d13b10ba17f7 (patch)
treee2f5d69324b7383f735fc3f350fa5a10ccaba6bc
parentd24a3f265ec4344b5502ec57df3cf8358f6f1499 (diff)
forgot some files
preliminary client implementation git-svn-id: file:///home/lennart/svn/public/ivam2/trunk@15 dbf6933d-3bce-0310-9bcc-ed052ba35b35
-rwxr-xr-xclient/ivam-autobox2
-rwxr-xr-xclient/ivam-createvb2
-rwxr-xr-xclient/ivam-echo3
-rwxr-xr-xclient/ivam-play3
-rwxr-xr-xclient/ivam-voicebox48
-rw-r--r--client/ivamApi.py44
-rw-r--r--client/ivamCore.py13
-rw-r--r--client/ivamDefs.py2
-rw-r--r--client/ivamShbufConnector.py26
-rw-r--r--client/ivamUtil.py13
-rw-r--r--client/ivamVoiceBox.py275
-rw-r--r--src/dtmffifo.c82
-rw-r--r--src/dtmffifo.h13
13 files changed, 526 insertions, 0 deletions
diff --git a/client/ivam-autobox b/client/ivam-autobox
new file mode 100755
index 0000000..8d5063a
--- /dev/null
+++ b/client/ivam-autobox
@@ -0,0 +1,2 @@
+#!/usr/bin/python
+
diff --git a/client/ivam-createvb b/client/ivam-createvb
new file mode 100755
index 0000000..8d5063a
--- /dev/null
+++ b/client/ivam-createvb
@@ -0,0 +1,2 @@
+#!/usr/bin/python
+
diff --git a/client/ivam-echo b/client/ivam-echo
new file mode 100755
index 0000000..b0f3e40
--- /dev/null
+++ b/client/ivam-echo
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec cat
diff --git a/client/ivam-play b/client/ivam-play
new file mode 100755
index 0000000..043ffbe
--- /dev/null
+++ b/client/ivam-play
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec zcat -f "$1"
diff --git a/client/ivam-voicebox b/client/ivam-voicebox
new file mode 100755
index 0000000..fc7b45a
--- /dev/null
+++ b/client/ivam-voicebox
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+import sys, os
+
+from ivamCore import log
+import ivamCore, ivamVoiceBox
+
+def usage(self):
+ log("%s [--record-time=SECS] [--pin=PIN] [--pin-file=PINFILE] [--debug]", 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"])
+ except getopt.GetoptError:
+ usage()
+ sys.exit(1)
+
+ vb.setDirectory(args[0])
+
+ for o, a in opts:
+ if o in ("-d", "--debug"):
+ 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"):
+ 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
new file mode 100644
index 0000000..3cf5651
--- /dev/null
+++ b/client/ivamApi.py
@@ -0,0 +1,44 @@
+
+class Processor:
+
+ def onConnect(self, c):
+ 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 softStopPlayback(self):
+ pass
+
+ def recordClip(self, fname, gzip = 0):
+ pass
+
+ def stopRecording(self):
+ pass
+
+ def timeout(self, t):
+ pass
+
diff --git a/client/ivamCore.py b/client/ivamCore.py
new file mode 100644
index 0000000..08d8771
--- /dev/null
+++ b/client/ivamCore.py
@@ -0,0 +1,13 @@
+
+import os, ivamShbufConnector, ivamPipeConnector;
+
+def newConnector(c):
+
+ if getenv("SHBUF") is None:
+ return ivamPipeConnector(c)
+ else:
+ return ivamShbufConnector(c)
+
+def log(s):
+ sys.stderr.write("%s\n" % s)
+
diff --git a/client/ivamDefs.py b/client/ivamDefs.py
new file mode 100644
index 0000000..45c5087
--- /dev/null
+++ b/client/ivamDefs.py
@@ -0,0 +1,2 @@
+
+spoolDirectory = "/var/spool/ivam"
diff --git a/client/ivamShbufConnector.py b/client/ivamShbufConnector.py
new file mode 100644
index 0000000..feef521
--- /dev/null
+++ b/client/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/client/ivamUtil.py b/client/ivamUtil.py
new file mode 100644
index 0000000..17e9801
--- /dev/null
+++ b/client/ivamUtil.py
@@ -0,0 +1,13 @@
+
+def getContents(fn):
+ f = file(a, "r")
+ r = f.readline().strip(" \t\r\n"))
+ f.close()
+ return r
+
+
+def setContents(fn, s):
+ f = file(a, "w")
+ f.write("%s\n" % s)
+ f.close()
+
diff --git a/client/ivamVoiceBox.py b/client/ivamVoiceBox.py
new file mode 100644
index 0000000..3cafab1
--- /dev/null
+++ b/client/ivamVoiceBox.py
@@ -0,0 +1,275 @@
+
+import getopt, sys
+import ivamApi, ivamCore, ivamDefs
+
+from ivamCore import log
+from ivamUtil import getContents, setContents
+
+DEBUG = False
+
+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
+
+ # Use GZIP compression?
+ useGzip = 1
+
+ def onConnect(self, c, callerNumber, calleeNumber):
+
+ if DEBUG:
+ log("onConnect()")
+
+ self.callerNumber = callerNumber
+ self.calleeNumber = calleeNumber
+
+ self.currentState = self.STATE_WELCOME
+ c.playClip("welcome")
+
+ def loginComplete(self):
+ self.messages = self.getMessageNames()
+ self.currentMessage = 0
+
+ if len(self.messages) == 0:
+ self.currentState = self.STATE_EMPTY
+ c.playClip("empty")
+ else:
+ self.currentState = self.STATE_MESSAGE_BEEP
+ c.playClip("beep")
+
+ def onClipFinish(self, c, fname):
+
+ if DEBUG:
+ log("onClipFinish('%s')" % fname)
+
+ if self.currentState == self.STATE_WELCOME:
+ self.currentState = self.STATE_WELCOME_BEEP;
+ c.playClip("beep")
+
+ elif self.currentState == self.STATE_WELCOME_BEEP:
+ self.currentState = self.STATE_RECORD
+ c.recordClip(self.nextMessageName(), self.useGzip)
+ 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()
+
+ elif self.currentState == self.STATE_MESSAGE_BEEP:
+
+ while True:
+
+ if self.currentMessage >= len(self.messages):
+ self.currentMessage = self.STATE_NO_MORE_MESSAGES
+ c.playClip("nomoremessages")
+ else:
+ self.currentState = self.STATE_MESSAGE_BEEP
+
+ try:
+ c.playClip(self.messages[self.currentMessage])
+ break
+
+ except IOError, e:
+ del(self.messages[self.currentMessage])
+
+ if len(self.messages) == 0:
+ self.currentState = self.STATE_EMPTY
+ c.playClip("empty")
+
+ continue
+
+ elif self.currentState == self.STATE_MESSAGE:
+
+ self.currentMessage +=1
+ self.currentState = self.STATE_MESSAGE_BEEP
+ c.playClip("beep")
+
+ elif self.currentState == self.STATE_REMOVE:
+
+ if len(self.messages) == 0:
+ self.currentState = self.STATE_EMPTY
+ c.playClip("empty")
+ else:
+ self.currentState = self.STATE_MESSAGE_BEEP
+ c.playClip("beep")
+
+ elif self.currentState == self.STATE_NO_MORE_MESSAGES:
+
+ # Silence ...
+ pass
+
+ elif self.currentState == self.STATE_EMPTY:
+ self.currentState = self.STATE_FINISH
+ c.hangup()
+
+ def onDtmfEvent(self, c, event):
+
+ if 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()
+
+ elif self.pin == "-":
+ self.currentState = self.STATE_FINISH
+ c.hangup()
+
+ else:
+ self.currentState = self.STATE_AUTH
+ self.inputPin = ""
+ c.stopPlayback()
+ c.playClip("auth")
+ c.setTimeout(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("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] :
+
+ changed = 0
+
+ if event == '0':
+
+ if self.currentMessage >= len(self.messages):
+ self.currentMessage = len(self.messages)-1
+
+ os.remove("%s.ulaw" % self.messages[self.currentMessage])
+ del(self.messages[self.currentMessage])
+
+ self.currentState = self.STATE_REMOVED
+ c.playClip("removed")
+
+ else:
+ if event in ['4', '2']:
+ self.currentMessage -= 1
+ changed = 1
+
+ if event in ['6', '8']:
+ self.currentMessage += 1
+ changed = 1
+
+ if event == '5':
+ changed = 1
+
+ if event == '1':
+ self.currentMessage = 0
+ changed = 1
+
+ if event == '7':
+ self.currentMessage = len(self.messages)-1
+ changed = 1
+
+ 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("beep")
+
+ else:
+ c.stopPlayback()
+
+ def onTimeout(self, c):
+
+ if DEBUG:
+ ivamCore.log("onTimeout()")
+
+ if self.currentState == self.STATE_RECORD:
+ self.currentState = self.STATE_PRE_FINISH
+ c.stopRecording()
+ c.playClip("beep")
+
+ elif self.currentState in [self.STATE_AUTH, self.STATE_PIN2, self.STATE_PIN3, self.STATE_PIN4]:
+ self.currentState = self.STATE_FINISH
+ c.hangup()
+
+ def onRecordFinish(self, c, fname):
+
+ if DEBUG:
+ ivamCore.log("onRecordFinish(%s)", fname)
+
+ log("Starting new message notification program.")
+ r = os.spawnvp(os.P_WAIT, self.messageProgram, [self.messageProgram, "%s.ulaw" % fname])
+ log("Program finished (return value is %i)." % r)
+
+ def getMessageNames(self):
+
+ f = filter(lambda e: e.startswith("message-"), os.listdir(self.directory))
+ f.sort()
+ f.reverse()
+ return f
+
+ def nextMessageName(self):
+
+ fn = "%s/message-%010i-%s-%s.ulaw" % (self.directory, time.time(), self.callerNumber, self.callerNumber)
+
+ if self.useGzip:
+ return fn+".gz"
+
+ return fn
+
+ def setPin(self, pin):
+
+ if re.match('^([0-9#*]*|-)$', pin).end() is None:
+ 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, path):
+
+ self.directory = path
+
+ try:
+ self.setPin(getContents("%s/PIN" % path))
+ except Exception:
+ self.pin = "-"
+
+ try:
+ self.recordTime = int(getContents("%s/RECORD_TIME" % path)))
+ except Exception:
+ self.recordTime = 60
+
+ messageProgram = "%s/NEWMESSAGE" % path
+
+def setupVoiceBox(dname, pin, recordTime):
+ 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`)
+
+ os.symlink
+
diff --git a/src/dtmffifo.c b/src/dtmffifo.c
new file mode 100644
index 0000000..54a1f62
--- /dev/null
+++ b/src/dtmffifo.c
@@ -0,0 +1,82 @@
+#include <unistd.h>
+#include <libdaemon/dlog.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "dtmffifo.h"
+
+struct dtmf_fifo* dtmf_fifo_new(void) {
+ struct dtmf_fifo *d = NULL;
+ char p[PATH_MAX];
+
+ d = malloc(sizeof(struct dtmf_fifo));
+ assert(d);
+ memset(d, 0, sizeof(struct dtmf_fifo));
+ d->fd = -1;
+
+ d->dir = strdup("/tmp/ivamd.XXXXXX");
+ assert(d->dir);
+
+ if (!mkdtemp(d->dir)) {
+ daemon_log(LOG_ERR, "Failed to create temporary directory '%s': %s", d->dir, strerror(errno));
+ goto fail;
+ }
+
+ snprintf(p, sizeof(p), "%s/%s", d->dir, "dtmf");
+ d->fname = strdup(p);
+ assert(d->fname);
+
+ if (mkfifo(d->fname, 0700) != 0) {
+ daemon_log(LOG_ERR, "Failed to create FIFO '%s': %s", d->fname, strerror(errno));
+ goto fail;
+ }
+
+ if ((d->fd = open(d->fname, O_RDWR|O_NDELAY)) < 0) {
+ daemon_log(LOG_ERR, "Failed to open FIFO '%s': %s", d->fname, strerror(errno));
+ goto fail;
+ }
+
+ daemon_log(LOG_INFO, "Sucessfully opened DTMF FIFO '%s'.", d->fname);
+
+ return d;
+
+fail:
+ if (d)
+ dtmf_fifo_free(d);
+
+ return NULL;
+}
+
+void dtmf_fifo_free(struct dtmf_fifo *d) {
+ assert(d);
+
+ if (d->fd >= 0)
+ close(d->fd);
+
+ if (d->fname) {
+ unlink(d->fname);
+ free(d->fname);
+ }
+
+ if (d->dir) {
+ rmdir(d->dir);
+ free(d->dir);
+ }
+
+ free(d);
+}
+
+void dtmf_fifo_pass(struct dtmf_fifo *d, char c) {
+ assert(d && d->fd >= 0);
+
+ daemon_log(LOG_INFO, "Recieved DTMF character '%c'", c);
+
+ if (write(d->fd, &c, 1) != 1)
+ daemon_log(LOG_ERR, "Failed to write to DTMF FIFO: %s", strerror(errno));
+}
diff --git a/src/dtmffifo.h b/src/dtmffifo.h
new file mode 100644
index 0000000..39e39c9
--- /dev/null
+++ b/src/dtmffifo.h
@@ -0,0 +1,13 @@
+#ifndef foodtmffifohfoo
+#define foodtmffifohfoo
+
+struct dtmf_fifo {
+ int fd;
+ char *dir, *fname;
+};
+
+struct dtmf_fifo* dtmf_fifo_new(void);
+void dtmf_fifo_free(struct dtmf_fifo *d);
+void dtmf_fifo_pass(struct dtmf_fifo *d, char c);
+
+#endif