add new application 'zapscan' Bug #250
authorJeremy McNamara <jj@nufone.net>
Sun, 11 Jan 2004 07:37:47 +0000 (07:37 +0000)
committerJeremy McNamara <jj@nufone.net>
Sun, 11 Jan 2004 07:37:47 +0000 (07:37 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@1935 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/Makefile
apps/app_zapscan.c [new file with mode: 0755]

index e1f22a1..79f0358 100755 (executable)
@@ -24,7 +24,8 @@ APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_mp3.so\
      app_authenticate.so app_softhangup.so app_lookupblacklist.so \
      app_waitforring.so app_privacy.so app_db.so app_chanisavail.so \
      app_enumlookup.so app_transfer.so app_setcidnum.so app_cdr.so \
-     app_hasnewvoicemail.so app_sayunixtime.so app_cut.so app_read.so app_setcdruserfield.so
+     app_hasnewvoicemail.so app_sayunixtime.so app_cut.so app_read.so \
+     app_setcdruserfield.so app_zapscan.so
 
 ifneq (${OSARCH},Darwin)
 APPS+=app_intercom.so
diff --git a/apps/app_zapscan.c b/apps/app_zapscan.c
new file mode 100755 (executable)
index 0000000..54db27d
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Zap Scanner
+ *
+ * Copyright (C) 2003, Digium
+ *
+ * Modified from app_zapbarge by David Troy <dave@toad.net>
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ *
+ * Special thanks to comphealth.com for sponsoring this
+ * GPL application.
+ */
+
+#include <asterisk/lock.h>
+#include <asterisk/file.h>
+#include <asterisk/logger.h>
+#include <asterisk/channel.h>
+#include <asterisk/pbx.h>
+#include <asterisk/module.h>
+#include <asterisk/config.h>
+#include <asterisk/app.h>
+#include <asterisk/options.h>
+#include <asterisk/cli.h>
+#include <asterisk/say.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include <pthread.h>
+#include <linux/zaptel.h>
+static char *tdesc = "Scan Zap channels application";
+
+static char *app = "ZapScan";
+
+static char *synopsis = "Scan Zap channels to monitor calls";
+
+static char *descrip =
+"  ZapScan allows a call center manager to monitor\n"
+"phone conversations in a convenient way.";
+
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+
+#define CONF_SIZE 160
+
+static int careful_write(int fd, unsigned char *data, int len)
+{
+        int res;
+        while(len) {
+                res = write(fd, data, len);
+                if (res < 1) {
+                        if (errno != EAGAIN) {
+                                ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
+                                return -1;
+                        } else
+                                return 0;
+                }
+                len -= res;
+                data += res;
+        }
+        return 0;
+}
+
+static int conf_run(struct ast_channel *chan, int confno, int confflags)
+{
+        int fd;
+        struct zt_confinfo ztc;
+        struct ast_frame *f;
+        struct ast_channel *c;
+        struct ast_frame fr;
+        int outfd;
+        int ms;
+        int nfds;
+        int res;
+        int flags;
+        int retryzap;
+        int origfd;
+        int ret = -1;
+
+        ZT_BUFFERINFO bi;
+        char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
+        char *buf = __buf + AST_FRIENDLY_OFFSET;
+
+        /* Set it into U-law mode (write) */
+        if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
+                ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
+                goto outrun;
+        }
+
+        /* Set it into U-law mode (read) */
+        if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
+                ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
+                goto outrun;
+        }
+        ast_indicate(chan, -1);
+        retryzap = strcasecmp(chan->type, "Zap");
+zapretry:
+        origfd = chan->fds[0];
+        if (retryzap) {
+                fd = open("/dev/zap/pseudo", O_RDWR);
+                if (fd < 0) {
+                        ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
+                        goto outrun;
+                }
+                /* Make non-blocking */
+                flags = fcntl(fd, F_GETFL);
+                if (flags < 0) {
+                        ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
+                        close(fd);
+                        goto outrun;
+                }
+                if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+                        ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
+                        close(fd);
+                        goto outrun;
+                }
+                /* Setup buffering information */
+                memset(&bi, 0, sizeof(bi));
+                bi.bufsize = CONF_SIZE;
+                bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+                bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+                bi.numbufs = 4;
+                if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
+                        ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
+                        close(fd);
+                        goto outrun;
+                }
+                nfds = 1;
+        } else {
+                /* XXX Make sure we're not running on a pseudo channel XXX */
+                fd = chan->fds[0];
+                nfds = 0;
+        }
+        memset(&ztc, 0, sizeof(ztc));
+        /* Check to see if we're in a conference... */
+        ztc.chan = 0;
+        if (ioctl(fd, ZT_GETCONF, &ztc)) {
+                ast_log(LOG_WARNING, "Error getting conference\n");
+                close(fd);
+                goto outrun;
+        }
+        if (ztc.confmode) {
+                /* Whoa, already in a conference...  Retry... */
+                if (!retryzap) {
+                        ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
+                        retryzap = 1;
+                        goto zapretry;
+                }
+        }
+        memset(&ztc, 0, sizeof(ztc));
+        /* Add us to the conference */
+        ztc.chan = 0;
+        ztc.confno = confno;
+        ztc.confmode = ZT_CONF_MONITORBOTH;
+
+        if (ioctl(fd, ZT_SETCONF, &ztc)) {
+                ast_log(LOG_WARNING, "Error setting conference\n");
+                close(fd);
+                goto outrun;
+        }
+        ast_log(LOG_DEBUG, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
+
+        for(;;) {
+                outfd = -1;
+                ms = -1;
+                c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
+                if (c) {
+                        if (c->fds[0] != origfd) {
+                                if (retryzap) {
+                                        /* Kill old pseudo */
+                                        close(fd);
+                                }
+                                ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
+                                retryzap = 0;
+                                goto zapretry;
+                        }
+                        f = ast_read(c);
+                        if (!f)
+                                break;
+                        if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
+                                ret = 0;
+                                break;
+                        } else if (fd != chan->fds[0]) {
+                                if (f->frametype == AST_FRAME_VOICE) {
+                                        if (f->subclass == AST_FORMAT_ULAW) {
+                                                /* Carefully write */
+                                                careful_write(fd, f->data, f->datalen);
+                                        } else
+                                                ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%d) frame in the conference\n", f->subclass);
+                                }
+                        }
+                        ast_frfree(f);
+                } else if (outfd > -1) {
+                        res = read(outfd, buf, CONF_SIZE);
+                        if (res > 0) {
+                                memset(&fr, 0, sizeof(fr));
+                                fr.frametype = AST_FRAME_VOICE;
+                                fr.subclass = AST_FORMAT_ULAW;
+                                fr.datalen = res;
+                                fr.samples = res;
+                                fr.data = buf;
+                                fr.offset = AST_FRIENDLY_OFFSET;
+                                if (ast_write(chan, &fr) < 0) {
+                                        ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
+                                        /* break; */
+                                }
+                        } else
+                                ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
+                }
+        }
+        if (fd != chan->fds[0])
+                close(fd);
+        else {
+                /* Take out of conference */
+                /* Add us to the conference */
+                ztc.chan = 0;
+                ztc.confno = 0;
+                ztc.confmode = 0;
+                if (ioctl(fd, ZT_SETCONF, &ztc)) {
+                        ast_log(LOG_WARNING, "Error setting conference\n");
+                }
+        }
+
+outrun:
+
+        return ret;
+}
+
+static int conf_exec(struct ast_channel *chan, void *data)
+{
+        int res=-1;
+        struct localuser *u;
+        int confflags = 0;
+        int confno = 0;
+        char confstr[80], *tmp;
+        struct ast_channel *tempchan = NULL, *lastchan = NULL;
+
+        LOCAL_USER_ADD(u);
+
+        if (chan->_state != AST_STATE_UP)
+                ast_answer(chan);
+
+        for (;;) {
+                tempchan = ast_channel_walk(tempchan);
+                if ( !tempchan && !lastchan )
+                        break;
+                if ( tempchan && (!strcmp(tempchan->type, "Zap")) && (tempchan != chan) ) {
+                        ast_verbose(VERBOSE_PREFIX_3 "Zap channel %s is in-use, monitoring...\n", tempchan->name);
+                        strcpy(confstr, tempchan->name);
+                        if ((tmp = strchr(confstr,'-'))) {
+                                *tmp = '\0';
+                       }
+                        confno = atoi(strchr(confstr,'/') + 1);
+                        ast_stopstream(chan);
+                        ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language);
+                       res = conf_run(chan, confno, confflags);
+                        if (res<0) break;
+        }
+                lastchan = tempchan;
+        }
+
+        LOCAL_USER_REMOVE(u);
+        return res;
+}
+
+int unload_module(void)
+{
+        STANDARD_HANGUP_LOCALUSERS;
+        return ast_unregister_application(app);
+}
+
+int load_module(void)
+{
+        return ast_register_application(app, conf_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+        return tdesc;
+}
+
+int usecount(void)
+{
+        int res;
+        STANDARD_USECOUNT(res);
+        return res;
+}
+
+char *key()
+{
+        return ASTERISK_GPL_KEY;
+}
+