aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/apps
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/apps')
-rw-r--r--trunk/apps/Makefile41
-rw-r--r--trunk/apps/app_adsiprog.c1581
-rw-r--r--trunk/apps/app_alarmreceiver.c813
-rw-r--r--trunk/apps/app_amd.c418
-rw-r--r--trunk/apps/app_authenticate.c212
-rw-r--r--trunk/apps/app_cdr.c63
-rw-r--r--trunk/apps/app_chanisavail.c157
-rw-r--r--trunk/apps/app_channelredirect.c93
-rw-r--r--trunk/apps/app_chanspy.c733
-rw-r--r--trunk/apps/app_controlplayback.c168
-rw-r--r--trunk/apps/app_db.c139
-rw-r--r--trunk/apps/app_dial.c2052
-rw-r--r--trunk/apps/app_dictate.c338
-rw-r--r--trunk/apps/app_directed_pickup.c172
-rw-r--r--trunk/apps/app_directory.c842
-rw-r--r--trunk/apps/app_disa.c367
-rw-r--r--trunk/apps/app_dumpchan.c160
-rw-r--r--trunk/apps/app_echo.c86
-rw-r--r--trunk/apps/app_exec.c221
-rw-r--r--trunk/apps/app_externalivr.c575
-rw-r--r--trunk/apps/app_festival.c523
-rw-r--r--trunk/apps/app_flash.c112
-rw-r--r--trunk/apps/app_followme.c1063
-rw-r--r--trunk/apps/app_forkcdr.c99
-rw-r--r--trunk/apps/app_getcpeid.c130
-rw-r--r--trunk/apps/app_ices.c205
-rw-r--r--trunk/apps/app_image.c80
-rw-r--r--trunk/apps/app_ivrdemo.c115
-rw-r--r--trunk/apps/app_jack.c971
-rw-r--r--trunk/apps/app_macro.c527
-rw-r--r--trunk/apps/app_meetme.c5592
-rw-r--r--trunk/apps/app_milliwatt.c134
-rw-r--r--trunk/apps/app_minivm.c3109
-rw-r--r--trunk/apps/app_mixmonitor.c424
-rw-r--r--trunk/apps/app_morsecode.c161
-rw-r--r--trunk/apps/app_mp3.c235
-rw-r--r--trunk/apps/app_nbscat.c218
-rw-r--r--trunk/apps/app_osplookup.c2041
-rw-r--r--trunk/apps/app_page.c193
-rw-r--r--trunk/apps/app_parkandannounce.c183
-rw-r--r--trunk/apps/app_pickupchan.c174
-rw-r--r--trunk/apps/app_playback.c524
-rw-r--r--trunk/apps/app_privacy.c173
-rw-r--r--trunk/apps/app_queue.c6153
-rw-r--r--trunk/apps/app_read.c231
-rw-r--r--trunk/apps/app_readexten.c258
-rw-r--r--trunk/apps/app_readfile.c107
-rw-r--r--trunk/apps/app_record.c365
-rw-r--r--trunk/apps/app_rpt.c7482
-rw-r--r--trunk/apps/app_sayunixtime.c112
-rw-r--r--trunk/apps/app_senddtmf.c127
-rw-r--r--trunk/apps/app_sendtext.c100
-rw-r--r--trunk/apps/app_setcallerid.c93
-rw-r--r--trunk/apps/app_skel.c125
-rw-r--r--trunk/apps/app_sms.c1932
-rw-r--r--trunk/apps/app_softhangup.c120
-rw-r--r--trunk/apps/app_speech_utils.c796
-rw-r--r--trunk/apps/app_stack.c416
-rw-r--r--trunk/apps/app_system.c129
-rw-r--r--trunk/apps/app_talkdetect.c211
-rw-r--r--trunk/apps/app_test.c467
-rw-r--r--trunk/apps/app_transfer.c125
-rw-r--r--trunk/apps/app_url.c149
-rw-r--r--trunk/apps/app_userevent.c89
-rw-r--r--trunk/apps/app_verbose.c158
-rw-r--r--trunk/apps/app_voicemail.c9840
-rw-r--r--trunk/apps/app_waitforring.c117
-rw-r--r--trunk/apps/app_waitforsilence.c189
-rw-r--r--trunk/apps/app_waituntil.c93
-rw-r--r--trunk/apps/app_while.c307
-rw-r--r--trunk/apps/app_zapateller.c113
-rw-r--r--trunk/apps/app_zapbarge.c299
-rw-r--r--trunk/apps/app_zapras.c238
-rw-r--r--trunk/apps/app_zapscan.c363
-rw-r--r--trunk/apps/enter.h287
-rw-r--r--trunk/apps/leave.h207
-rw-r--r--trunk/apps/rpt_flow.pdfbin51935 -> 0 bytes
77 files changed, 0 insertions, 58015 deletions
diff --git a/trunk/apps/Makefile b/trunk/apps/Makefile
deleted file mode 100644
index 22b8f6d7a..000000000
--- a/trunk/apps/Makefile
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# Asterisk -- A telephony toolkit for Linux.
-#
-# Makefile for PBX applications
-#
-# Copyright (C) 1999-2006, Digium, Inc.
-#
-# This program is free software, distributed under the terms of
-# the GNU General Public License
-#
-
--include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
-
-MODULE_PREFIX=app
-MENUSELECT_CATEGORY=APPS
-MENUSELECT_DESCRIPTION=Applications
-
-MENUSELECT_OPTS_app_directory:=$(MENUSELECT_OPTS_app_voicemail)
-ifneq ($(findstring ODBC_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),)
- MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_ODBC_STORAGE)
- MENUSELECT_DEPENDS_app_directory+=$(MENUSELECT_DEPENDS_ODBC_STORAGE)
-endif
-ifneq ($(findstring IMAP_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),)
- MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_IMAP_STORAGE)
- MENUSELECT_DEPENDS_app_directory+=$(MENUSELECT_DEPENDS_IMAP_STORAGE)
-endif
-
-ifeq (SunOS,$(shell uname))
- MENUSELECT_DEPENDS_app_chanspy+=RT
- RT_LIB=-lrt
-endif
-
-all: _all
-
-include $(ASTTOPDIR)/Makefile.moddir_rules
-
-ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
- LIBS+= -lres_features.so -lres_ael_share.so -lres_monitor.so -lres_speech.so
- LIBS+= -lres_smdi.so
-endif
-
diff --git a/trunk/apps/app_adsiprog.c b/trunk/apps/app_adsiprog.c
deleted file mode 100644
index 1b09ce7b5..000000000
--- a/trunk/apps/app_adsiprog.c
+++ /dev/null
@@ -1,1581 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Program Asterisk ADSI Scripts into phone
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>res_adsi</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <netinet/in.h>
-#include <ctype.h>
-
-#include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/adsi.h"
-#include "asterisk/utils.h"
-#include "asterisk/lock.h"
-
-static char *app = "ADSIProg";
-
-static char *synopsis = "Load Asterisk ADSI Scripts into phone";
-
-/* #define DUMP_MESSAGES */
-
-static char *descrip =
-" ADSIProg(script): This application programs an ADSI Phone with the given\n"
-"script. If nothing is specified, the default script (asterisk.adsi) is used.\n";
-
-struct adsi_event {
- int id;
- char *name;
-};
-
-static struct adsi_event events[] = {
- { 1, "CALLERID" },
- { 2, "VMWI" },
- { 3, "NEARANSWER" },
- { 4, "FARANSWER" },
- { 5, "ENDOFRING" },
- { 6, "IDLE" },
- { 7, "OFFHOOK" },
- { 8, "CIDCW" },
- { 9, "BUSY" },
- { 10, "FARRING" },
- { 11, "DIALTONE" },
- { 12, "RECALL" },
- { 13, "MESSAGE" },
- { 14, "REORDER" },
- { 15, "DISTINCTIVERING" },
- { 16, "RING" },
- { 17, "REMINDERRING" },
- { 18, "SPECIALRING" },
- { 19, "CODEDRING" },
- { 20, "TIMER" },
- { 21, "INUSE" },
- { 22, "EVENT22" },
- { 23, "EVENT23" },
- { 24, "CPEID" },
-};
-
-static struct adsi_event justify[] = {
- { 0, "CENTER" },
- { 1, "RIGHT" },
- { 2, "LEFT" },
- { 3, "INDENT" },
-};
-
-#define STATE_NORMAL 0
-#define STATE_INKEY 1
-#define STATE_INSUB 2
-#define STATE_INIF 3
-
-#define MAX_RET_CODE 20
-#define MAX_SUB_LEN 255
-#define MAX_MAIN_LEN 1600
-
-#define ARG_STRING (1 << 0)
-#define ARG_NUMBER (1 << 1)
-
-struct adsi_soft_key {
- char vname[40]; /* Which "variable" is associated with it */
- int retstrlen; /* Length of return string */
- int initlen; /* initial length */
- int id;
- int defined;
- char retstr[80]; /* Return string data */
-};
-
-struct adsi_subscript {
- char vname[40];
- int id;
- int defined;
- int datalen;
- int inscount;
- int ifinscount;
- char *ifdata;
- char data[2048];
-};
-
-struct adsi_state {
- char vname[40];
- int id;
-};
-
-struct adsi_flag {
- char vname[40];
- int id;
-};
-
-struct adsi_display {
- char vname[40];
- int id;
- char data[70];
- int datalen;
-};
-
-struct adsi_script {
- int state;
- int numkeys;
- int numsubs;
- int numstates;
- int numdisplays;
- int numflags;
- struct adsi_soft_key *key;
- struct adsi_subscript *sub;
- /* Pre-defined displays */
- struct adsi_display displays[63];
- /* ADSI States 1 (initial) - 254 */
- struct adsi_state states[256];
- /* Keys 2-63 */
- struct adsi_soft_key keys[62];
- /* Subscripts 0 (main) to 127 */
- struct adsi_subscript subs[128];
- /* Flags 1-7 */
- struct adsi_flag flags[7];
-
- /* Stuff from adsi script */
- unsigned char sec[5];
- char desc[19];
- unsigned char fdn[5];
- int ver;
-};
-
-
-static int process_token(void *out, char *src, int maxlen, int argtype)
-{
- if ((strlen(src) > 1) && src[0] == '\"') {
- /* This is a quoted string */
- if (!(argtype & ARG_STRING))
- return -1;
- src++;
- /* Don't take more than what's there */
- if (maxlen > strlen(src) - 1)
- maxlen = strlen(src) - 1;
- memcpy(out, src, maxlen);
- ((char *)out)[maxlen] = '\0';
- } else if (!ast_strlen_zero(src) && (src[0] == '\\')) {
- if (!(argtype & ARG_NUMBER))
- return -1;
- /* Octal value */
- if (sscanf(src, "%o", (int *)out) != 1)
- return -1;
- if (argtype & ARG_STRING) {
- /* Convert */
- *((unsigned int *)out) = htonl(*((unsigned int *)out));
- }
- } else if ((strlen(src) > 2) && (src[0] == '0') && (tolower(src[1]) == 'x')) {
- if (!(argtype & ARG_NUMBER))
- return -1;
- /* Hex value */
- if (sscanf(src + 2, "%x", (unsigned int *)out) != 1)
- return -1;
- if (argtype & ARG_STRING) {
- /* Convert */
- *((unsigned int *)out) = htonl(*((unsigned int *)out));
- }
- } else if ((!ast_strlen_zero(src) && isdigit(src[0]))) {
- if (!(argtype & ARG_NUMBER))
- return -1;
- /* Hex value */
- if (sscanf(src, "%d", (int *)out) != 1)
- return -1;
- if (argtype & ARG_STRING) {
- /* Convert */
- *((unsigned int *)out) = htonl(*((unsigned int *)out));
- }
- } else
- return -1;
- return 0;
-}
-
-static char *get_token(char **buf, char *script, int lineno)
-{
- char *tmp = *buf, *keyword;
- int quoted = 0;
-
- /* Advance past any white space */
- while(*tmp && (*tmp < 33))
- tmp++;
- if (!*tmp)
- return NULL;
- keyword = tmp;
- while(*tmp && ((*tmp > 32) || quoted)) {
- if (*tmp == '\"') {
- quoted = !quoted;
- }
- tmp++;
- }
- if (quoted) {
- ast_log(LOG_WARNING, "Mismatched quotes at line %d of %s\n", lineno, script);
- return NULL;
- }
- *tmp = '\0';
- tmp++;
- while(*tmp && (*tmp < 33))
- tmp++;
- /* Note where we left off */
- *buf = tmp;
- return keyword;
-}
-
-static char *validdtmf = "123456789*0#ABCD";
-
-static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
-{
- char dtmfstr[80], *a;
- int bytes = 0;
-
- if (!(a = get_token(&args, script, lineno))) {
- ast_log(LOG_WARNING, "Expecting something to send for SENDDTMF at line %d of %s\n", lineno, script);
- return 0;
- }
-
- if (process_token(dtmfstr, a, sizeof(dtmfstr) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "Invalid token for SENDDTMF at line %d of %s\n", lineno, script);
- return 0;
- }
-
- a = dtmfstr;
-
- while(*a) {
- if (strchr(validdtmf, *a)) {
- *buf = *a;
- buf++;
- bytes++;
- } else
- ast_log(LOG_WARNING, "'%c' is not a valid DTMF tone at line %d of %s\n", *a, lineno, script);
- a++;
- }
-
- return bytes;
-}
-
-static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
-{
- char *page = get_token(&args, script, lineno);
- char *gline = get_token(&args, script, lineno);
- int line;
- unsigned char cmd;
-
- if (!page || !gline) {
- ast_log(LOG_WARNING, "Expecting page and line number for GOTOLINE at line %d of %s\n", lineno, script);
- return 0;
- }
-
- if (!strcasecmp(page, "INFO")) {
- cmd = 0;
- } else if (!strcasecmp(page, "COMM")) {
- cmd = 0x80;
- } else {
- ast_log(LOG_WARNING, "Expecting either 'INFO' or 'COMM' page, got got '%s' at line %d of %s\n", page, lineno, script);
- return 0;
- }
-
- if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
- ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
- return 0;
- }
-
- cmd |= line;
- buf[0] = 0x8b;
- buf[1] = cmd;
-
- return 2;
-}
-
-static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
-{
- char *dir = get_token(&args, script, lineno);
- char *gline = get_token(&args, script, lineno);
- int line;
- unsigned char cmd;
-
- if (!dir || !gline) {
- ast_log(LOG_WARNING, "Expecting direction and number of lines for GOTOLINEREL at line %d of %s\n", lineno, script);
- return 0;
- }
-
- if (!strcasecmp(dir, "UP")) {
- cmd = 0;
- } else if (!strcasecmp(dir, "DOWN")) {
- cmd = 0x20;
- } else {
- ast_log(LOG_WARNING, "Expecting either 'UP' or 'DOWN' direction, got '%s' at line %d of %s\n", dir, lineno, script);
- return 0;
- }
-
- if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
- ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
- return 0;
- }
-
- cmd |= line;
- buf[0] = 0x8c;
- buf[1] = cmd;
-
- return 2;
-}
-
-static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
-{
- char *gtime = get_token(&args, script, lineno);
- int ms;
-
- if (!gtime) {
- ast_log(LOG_WARNING, "Expecting number of milliseconds to wait at line %d of %s\n", lineno, script);
- return 0;
- }
-
- if (process_token(&ms, gtime, sizeof(ms), ARG_NUMBER)) {
- ast_log(LOG_WARNING, "Invalid delay milliseconds '%s' at line %d of %s\n", gtime, lineno, script);
- return 0;
- }
-
- buf[0] = 0x90;
-
- if (id == 11)
- buf[1] = ms / 100;
- else
- buf[1] = ms / 10;
-
- return 2;
-}
-
-static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
-{
- char *gstate = get_token(&args, script, lineno);
- int state;
-
- if (!gstate) {
- ast_log(LOG_WARNING, "Expecting state number at line %d of %s\n", lineno, script);
- return 0;
- }
-
- if (process_token(&state, gstate, sizeof(state), ARG_NUMBER)) {
- ast_log(LOG_WARNING, "Invalid state number '%s' at line %d of %s\n", gstate, lineno, script);
- return 0;
- }
-
- buf[0] = id;
- buf[1] = state;
-
- return 2;
-}
-
-static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
-{
- char *tok = get_token(&args, script, lineno);
-
- if (tok)
- ast_log(LOG_WARNING, "Clearing timer requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
-
- buf[0] = id;
-
- /* For some reason the clear code is different slightly */
- if (id == 7)
- buf[1] = 0x10;
- else
- buf[1] = 0x00;
-
- return 2;
-}
-
-static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
-{
- int x;
-
- for (x = 0; x < state->numflags; x++) {
- if (!strcasecmp(state->flags[x].vname, name))
- return &state->flags[x];
- }
-
- /* Return now if we're not allowed to create */
- if (!create)
- return NULL;
-
- if (state->numflags > 6) {
- ast_log(LOG_WARNING, "No more flag space at line %d of %s\n", lineno, script);
- return NULL;
- }
-
- ast_copy_string(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname));
- state->flags[state->numflags].id = state->numflags + 1;
- state->numflags++;
-
- return &state->flags[state->numflags-1];
-}
-
-static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
-{
- char *tok = get_token(&args, script, lineno);
- char sname[80];
- struct adsi_flag *flag;
-
- if (!tok) {
- ast_log(LOG_WARNING, "Setting flag requires a flag number at line %d of %s\n", lineno, script);
- return 0;
- }
-
- if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
- return 0;
- }
-
- if (!(flag = getflagbyname(state, sname, script, lineno, 0))) {
- ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
- return 0;
- }
-
- buf[0] = id;
- buf[1] = ((flag->id & 0x7) << 4) | 1;
-
- return 2;
-}
-
-static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
-{
- char *tok = get_token(&args, script, lineno);
- struct adsi_flag *flag;
- char sname[80];
-
- if (!tok) {
- ast_log(LOG_WARNING, "Clearing flag requires a flag number at line %d of %s\n", lineno, script);
- return 0;
- }
-
- if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
- return 0;
- }
-
- if (!(flag = getflagbyname(state, sname, script, lineno, 0))) {
- ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
- return 0;
- }
-
- buf[0] = id;
- buf[1] = ((flag->id & 0x7) << 4);
-
- return 2;
-}
-
-static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
-{
- char *tok = get_token(&args, script, lineno);
- int secs;
-
- if (!tok) {
- ast_log(LOG_WARNING, "Missing number of seconds at line %d of %s\n", lineno, script);
- return 0;
- }
-
- if (process_token(&secs, tok, sizeof(secs), ARG_NUMBER)) {
- ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
- return 0;
- }
-
- buf[0] = id;
- buf[1] = 0x1;
- buf[2] = secs;
-
- return 3;
-}
-
-static int geteventbyname(char *name)
-{
- int x;
-
- for (x = 0; x < sizeof(events) / sizeof(events[0]); x++) {
- if (!strcasecmp(events[x].name, name))
- return events[x].id;
- }
-
- return 0;
-}
-
-static int getjustifybyname(char *name)
-{
- int x;
-
- for (x = 0; x <sizeof(justify) / sizeof(justify[0]); x++) {
- if (!strcasecmp(justify[x].name, name))
- return justify[x].id;
- }
-
- return -1;
-}
-
-static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name, char *script, int lineno)
-{
- int x;
-
- for (x = 0; x < state->numkeys; x++) {
- if (!strcasecmp(state->keys[x].vname, name))
- return &state->keys[x];
- }
-
- if (state->numkeys > 61) {
- ast_log(LOG_WARNING, "No more key space at line %d of %s\n", lineno, script);
- return NULL;
- }
-
- ast_copy_string(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname));
- state->keys[state->numkeys].id = state->numkeys + 2;
- state->numkeys++;
-
- return &state->keys[state->numkeys-1];
-}
-
-static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name, char *script, int lineno)
-{
- int x;
-
- for (x = 0; x < state->numsubs; x++) {
- if (!strcasecmp(state->subs[x].vname, name))
- return &state->subs[x];
- }
-
- if (state->numsubs > 127) {
- ast_log(LOG_WARNING, "No more subscript space at line %d of %s\n", lineno, script);
- return NULL;
- }
-
- ast_copy_string(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname));
- state->subs[state->numsubs].id = state->numsubs;
- state->numsubs++;
-
- return &state->subs[state->numsubs-1];
-}
-
-static struct adsi_state *getstatebyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
-{
- int x;
-
- for (x = 0; x <state->numstates; x++) {
- if (!strcasecmp(state->states[x].vname, name))
- return &state->states[x];
- }
-
- /* Return now if we're not allowed to create */
- if (!create)
- return NULL;
-
- if (state->numstates > 253) {
- ast_log(LOG_WARNING, "No more state space at line %d of %s\n", lineno, script);
- return NULL;
- }
-
- ast_copy_string(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname));
- state->states[state->numstates].id = state->numstates + 1;
- state->numstates++;
-
- return &state->states[state->numstates-1];
-}
-
-static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
-{
- int x;
-
- for (x = 0; x < state->numdisplays; x++) {
- if (!strcasecmp(state->displays[x].vname, name))
- return &state->displays[x];
- }
-
- /* Return now if we're not allowed to create */
- if (!create)
- return NULL;
-
- if (state->numdisplays > 61) {
- ast_log(LOG_WARNING, "No more display space at line %d of %s\n", lineno, script);
- return NULL;
- }
-
- ast_copy_string(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname));
- state->displays[state->numdisplays].id = state->numdisplays + 1;
- state->numdisplays++;
-
- return &state->displays[state->numdisplays-1];
-}
-
-static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
-{
- char *tok, newkey[80];
- int bytes, x, flagid = 0;
- unsigned char keyid[6];
- struct adsi_soft_key *key;
- struct adsi_flag *flag;
-
- for (x = 0; x < 7; x++) {
- /* Up to 6 key arguments */
- if (!(tok = get_token(&args, script, lineno)))
- break;
- if (!strcasecmp(tok, "UNLESS")) {
- /* Check for trailing UNLESS flag */
- if (!(tok = get_token(&args, script, lineno))) {
- ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
- } else if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "Invalid flag name '%s' at line %d of %s\n", tok, lineno, script);
- } else if (!(flag = getflagbyname(state, newkey, script, lineno, 0))) {
- ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", newkey, lineno, script);
- } else
- flagid = flag->id;
- if ((tok = get_token(&args, script, lineno)))
- ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
- break;
- }
- if (x > 5) {
- ast_log(LOG_WARNING, "Only 6 keys can be defined, ignoring '%s' at line %d of %s\n", tok, lineno, script);
- break;
- }
- if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "Invalid token for key name: %s\n", tok);
- continue;
- }
-
- if (!(key = getkeybyname(state, newkey, script, lineno)))
- break;
- keyid[x] = key->id;
- }
- buf[0] = id;
- buf[1] = (flagid & 0x7) << 3 | (x & 0x7);
- for (bytes = 0; bytes < x; bytes++) {
- buf[bytes + 2] = keyid[bytes];
- }
- return 2 + x;
-}
-
-static int showdisplay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
-{
- char *tok, dispname[80];
- int line = 0, flag = 0, cmd = 3;
- struct adsi_display *disp;
-
- /* Get display */
- if (!(tok = get_token(&args, script, lineno)) || process_token(dispname, tok, sizeof(dispname) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "Invalid display name: %s at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
- return 0;
- }
-
- if (!(disp = getdisplaybyname(state, dispname, script, lineno, 0))) {
- ast_log(LOG_WARNING, "Display '%s' is undefined at line %d of %s\n", dispname, lineno, script);
- return 0;
- }
-
- if (!(tok = get_token(&args, script, lineno)) || strcasecmp(tok, "AT")) {
- ast_log(LOG_WARNING, "Missing token 'AT' at line %d of %s\n", lineno, script);
- return 0;
- }
-
- /* Get line number */
- if (!(tok = get_token(&args, script, lineno)) || process_token(&line, tok, sizeof(line), ARG_NUMBER)) {
- ast_log(LOG_WARNING, "Invalid line: '%s' at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
- return 0;
- }
-
- if ((tok = get_token(&args, script, lineno)) && !strcasecmp(tok, "NOUPDATE")) {
- cmd = 1;
- tok = get_token(&args, script, lineno);
- }
-
- if (tok && !strcasecmp(tok, "UNLESS")) {
- /* Check for trailing UNLESS flag */
- if (!(tok = get_token(&args, script, lineno))) {
- ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
- } else if (process_token(&flag, tok, sizeof(flag), ARG_NUMBER)) {
- ast_log(LOG_WARNING, "Invalid flag number '%s' at line %d of %s\n", tok, lineno, script);
- }
- if ((tok = get_token(&args, script, lineno)))
- ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
- }
-
- buf[0] = id;
- buf[1] = (cmd << 6) | (disp->id & 0x3f);
- buf[2] = ((line & 0x1f) << 3) | (flag & 0x7);
-
- return 3;
-}
-
-static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
-{
- char *tok = get_token(&args, script, lineno);
-
- if (tok)
- ast_log(LOG_WARNING, "Clearing display requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
-
- buf[0] = id;
- buf[1] = 0x00;
- return 2;
-}
-
-static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
-{
- char *tok = get_token(&args, script, lineno);
-
- if (tok)
- ast_log(LOG_WARNING, "Digitdirect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
-
- buf[0] = id;
- buf[1] = 0x7;
- return 2;
-}
-
-static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
-{
- char *tok = get_token(&args, script, lineno);
-
- if (tok)
- ast_log(LOG_WARNING, "CLEARCB1 requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
-
- buf[0] = id;
- buf[1] = 0;
- return 2;
-}
-
-static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
-{
- char *tok = get_token(&args, script, lineno);
-
- if (tok)
- ast_log(LOG_WARNING, "Digitcollect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
-
- buf[0] = id;
- buf[1] = 0xf;
- return 2;
-}
-
-static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
-{
- char *tok = get_token(&args, script, lineno);
- char subscript[80];
- struct adsi_subscript *sub;
-
- if (!tok) {
- ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
- return 0;
- }
-
- if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
- return 0;
- }
-
- if (!(sub = getsubbyname(state, subscript, script, lineno)))
- return 0;
-
- buf[0] = 0x9d;
- buf[1] = sub->id;
-
- return 2;
-}
-
-static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
-{
- char *tok = get_token(&args, script, lineno);
- char subscript[80], sname[80];
- int sawin = 0, event, snums[8], scnt = 0, x;
- struct adsi_subscript *sub;
-
- if (!tok) {
- ast_log(LOG_WARNING, "Missing event for 'ONEVENT' at line %d of %s\n", lineno, script);
- return 0;
- }
-
- if ((event = geteventbyname(tok)) < 1) {
- ast_log(LOG_WARNING, "'%s' is not a valid event name, at line %d of %s\n", args, lineno, script);
- return 0;
- }
-
- tok = get_token(&args, script, lineno);
- while ((!sawin && !strcasecmp(tok, "IN")) ||
- (sawin && !strcasecmp(tok, "OR"))) {
- sawin = 1;
- if (scnt > 7) {
- ast_log(LOG_WARNING, "No more than 8 states may be specified for inclusion at line %d of %s\n", lineno, script);
- return 0;
- }
- /* Process 'in' things */
- tok = get_token(&args, script, lineno);
- if (process_token(sname, tok, sizeof(sname), ARG_STRING)) {
- ast_log(LOG_WARNING, "'%s' is not a valid state name at line %d of %s\n", tok, lineno, script);
- return 0;
- }
- if ((snums[scnt] = getstatebyname(state, sname, script, lineno, 0) < 0)) {
- ast_log(LOG_WARNING, "State '%s' not declared at line %d of %s\n", sname, lineno, script);
- return 0;
- }
- scnt++;
- if (!(tok = get_token(&args, script, lineno)))
- break;
- }
- if (!tok || strcasecmp(tok, "GOTO")) {
- if (!tok)
- tok = "<nothing>";
- if (sawin)
- ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'OR' at line %d of %s\n", tok, lineno, script);
- else
- ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'IN' at line %d of %s\n", tok, lineno, script);
- }
- if (!(tok = get_token(&args, script, lineno))) {
- ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
- return 0;
- }
- if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "Invalid subscript '%s' at line %d of %s\n", tok, lineno, script);
- return 0;
- }
- if (!(sub = getsubbyname(state, subscript, script, lineno)))
- return 0;
- buf[0] = 8;
- buf[1] = event;
- buf[2] = sub->id | 0x80;
- for (x = 0; x < scnt; x++)
- buf[3 + x] = snums[x];
- return 3 + scnt;
-}
-
-struct adsi_key_cmd {
- char *name;
- int id;
- int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno);
-};
-
-static struct adsi_key_cmd kcmds[] = {
- { "SENDDTMF", 0, send_dtmf },
- /* Encoded DTMF would go here */
- { "ONHOOK", 0x81 },
- { "OFFHOOK", 0x82 },
- { "FLASH", 0x83 },
- { "WAITDIALTONE", 0x84 },
- /* Send line number */
- { "BLANK", 0x86 },
- { "SENDCHARS", 0x87 },
- { "CLEARCHARS", 0x88 },
- { "BACKSPACE", 0x89 },
- /* Tab column */
- { "GOTOLINE", 0x8b, goto_line },
- { "GOTOLINEREL", 0x8c, goto_line_rel },
- { "PAGEUP", 0x8d },
- { "PAGEDOWN", 0x8e },
- /* Extended DTMF */
- { "DELAY", 0x90, send_delay },
- { "DIALPULSEONE", 0x91 },
- { "DATAMODE", 0x92 },
- { "VOICEMODE", 0x93 },
- /* Display call buffer 'n' */
- /* Clear call buffer 'n' */
- { "CLEARCB1", 0x95, clearcbone },
- { "DIGITCOLLECT", 0x96, digitcollect },
- { "DIGITDIRECT", 0x96, digitdirect },
- { "CLEAR", 0x97 },
- { "SHOWDISPLAY", 0x98, showdisplay },
- { "CLEARDISPLAY", 0x98, cleardisplay },
- { "SHOWKEYS", 0x99, showkeys },
- { "SETSTATE", 0x9a, set_state },
- { "TIMERSTART", 0x9b, starttimer },
- { "TIMERCLEAR", 0x9b, cleartimer },
- { "SETFLAG", 0x9c, setflag },
- { "CLEARFLAG", 0x9c, clearflag },
- { "GOTO", 0x9d, subscript },
- { "EVENT22", 0x9e },
- { "EVENT23", 0x9f },
- { "EXIT", 0xa0 },
-};
-
-static struct adsi_key_cmd opcmds[] = {
-
- /* 1 - Branch on event -- handled specially */
- { "SHOWKEYS", 2, showkeys },
- /* Display Control */
- { "SHOWDISPLAY", 3, showdisplay },
- { "CLEARDISPLAY", 3, cleardisplay },
- { "CLEAR", 5 },
- { "SETSTATE", 6, set_state },
- { "TIMERSTART", 7, starttimer },
- { "TIMERCLEAR", 7, cleartimer },
- { "ONEVENT", 8, onevent },
- /* 9 - Subroutine label, treated specially */
- { "SETFLAG", 10, setflag },
- { "CLEARFLAG", 10, clearflag },
- { "DELAY", 11, send_delay },
- { "EXIT", 12 },
-};
-
-
-static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, char *script, int lineno)
-{
- int x, res;
- char *unused;
-
- for (x = 0; x < sizeof(kcmds) / sizeof(kcmds[0]); x++) {
- if ((kcmds[x].id > -1) && !strcasecmp(kcmds[x].name, code)) {
- if (kcmds[x].add_args) {
- res = kcmds[x].add_args(key->retstr + key->retstrlen,
- code, kcmds[x].id, args, state, script, lineno);
- if ((key->retstrlen + res - key->initlen) <= MAX_RET_CODE)
- key->retstrlen += res;
- else
- ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", kcmds[x].name, key->vname, lineno, script);
- } else {
- if ((unused = get_token(&args, script, lineno)))
- ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", kcmds[x].name, lineno, script, unused);
- if ((key->retstrlen + 1 - key->initlen) <= MAX_RET_CODE) {
- key->retstr[key->retstrlen] = kcmds[x].id;
- key->retstrlen++;
- } else
- ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", kcmds[x].name, key->vname, lineno, script);
- }
- return 0;
- }
- }
- return -1;
-}
-
-static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, char *script, int lineno)
-{
- int x, res, max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN;
- char *unused;
-
- for (x = 0; x < sizeof(opcmds) / sizeof(opcmds[0]); x++) {
- if ((opcmds[x].id > -1) && !strcasecmp(opcmds[x].name, code)) {
- if (opcmds[x].add_args) {
- res = opcmds[x].add_args(sub->data + sub->datalen,
- code, opcmds[x].id, args, state, script, lineno);
- if ((sub->datalen + res + 1) <= max)
- sub->datalen += res;
- else {
- ast_log(LOG_WARNING, "No space for '%s' code in subscript '%s' at line %d of %s\n", opcmds[x].name, sub->vname, lineno, script);
- return -1;
- }
- } else {
- if ((unused = get_token(&args, script, lineno)))
- ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", opcmds[x].name, lineno, script, unused);
- if ((sub->datalen + 2) <= max) {
- sub->data[sub->datalen] = opcmds[x].id;
- sub->datalen++;
- } else {
- ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", opcmds[x].name, sub->vname, lineno, script);
- return -1;
- }
- }
- /* Separate commands with 0xff */
- sub->data[sub->datalen] = 0xff;
- sub->datalen++;
- sub->inscount++;
- return 0;
- }
- }
- return -1;
-}
-
-static int adsi_process(struct adsi_script *state, char *buf, char *script, int lineno)
-{
- char *keyword = get_token(&buf, script, lineno);
- char *args, vname[256], tmp[80], tmp2[80];
- int lrci, wi, event;
- struct adsi_display *disp;
- struct adsi_subscript *newsub;
-
- if (!keyword)
- return 0;
-
- switch(state->state) {
- case STATE_NORMAL:
- if (!strcasecmp(keyword, "DESCRIPTION")) {
- if ((args = get_token(&buf, script, lineno))) {
- if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING))
- ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script);
- } else
- ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script);
- } else if (!strcasecmp(keyword, "VERSION")) {
- if ((args = get_token(&buf, script, lineno))) {
- if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER))
- ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script);
- } else
- ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script);
- } else if (!strcasecmp(keyword, "SECURITY")) {
- if ((args = get_token(&buf, script, lineno))) {
- if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER))
- ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script);
- } else
- ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script);
- } else if (!strcasecmp(keyword, "FDN")) {
- if ((args = get_token(&buf, script, lineno))) {
- if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER))
- ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script);
- } else
- ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script);
- } else if (!strcasecmp(keyword, "KEY")) {
- if (!(args = get_token(&buf, script, lineno))) {
- ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script);
- break;
- }
- if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
- break;
- }
- if (!(state->key = getkeybyname(state, vname, script, lineno))) {
- ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script);
- break;
- }
- if (state->key->defined) {
- ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script);
- break;
- }
- if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
- ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
- break;
- }
- if (!(args = get_token(&buf, script, lineno))) {
- ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script);
- break;
- }
- if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script);
- break;
- }
- if ((args = get_token(&buf, script, lineno))) {
- if (strcasecmp(args, "OR")) {
- ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
- break;
- }
- if (!(args = get_token(&buf, script, lineno))) {
- ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script);
- break;
- }
- if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script);
- break;
- }
- } else {
- ast_copy_string(tmp2, tmp, sizeof(tmp2));
- }
- if (strlen(tmp2) > 18) {
- ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
- tmp2[18] = '\0';
- }
- if (strlen(tmp) > 7) {
- ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script);
- tmp[7] = '\0';
- }
- /* Setup initial stuff */
- state->key->retstr[0] = 128;
- /* 1 has the length */
- state->key->retstr[2] = state->key->id;
- /* Put the Full name in */
- memcpy(state->key->retstr + 3, tmp2, strlen(tmp2));
- /* Update length */
- state->key->retstrlen = strlen(tmp2) + 3;
- /* Put trailing 0xff */
- state->key->retstr[state->key->retstrlen++] = 0xff;
- /* Put the short name */
- memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp));
- /* Update length */
- state->key->retstrlen += strlen(tmp);
- /* Put trailing 0xff */
- state->key->retstr[state->key->retstrlen++] = 0xff;
- /* Record initial length */
- state->key->initlen = state->key->retstrlen;
- state->state = STATE_INKEY;
- } else if (!strcasecmp(keyword, "SUB")) {
- if (!(args = get_token(&buf, script, lineno))) {
- ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
- break;
- }
- if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
- break;
- }
- if (!(state->sub = getsubbyname(state, vname, script, lineno))) {
- ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script);
- break;
- }
- if (state->sub->defined) {
- ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script);
- break;
- }
- /* Setup sub */
- state->sub->data[0] = 130;
- /* 1 is the length */
- state->sub->data[2] = 0x0; /* Clear extensibility bit */
- state->sub->datalen = 3;
- if (state->sub->id) {
- /* If this isn't the main subroutine, make a subroutine label for it */
- state->sub->data[3] = 9;
- state->sub->data[4] = state->sub->id;
- /* 5 is length */
- state->sub->data[6] = 0xff;
- state->sub->datalen = 7;
- }
- if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
- ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
- break;
- }
- state->state = STATE_INSUB;
- } else if (!strcasecmp(keyword, "STATE")) {
- if (!(args = get_token(&buf, script, lineno))) {
- ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script);
- break;
- }
- if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script);
- break;
- }
- if (getstatebyname(state, vname, script, lineno, 0)) {
- ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script);
- break;
- }
- getstatebyname(state, vname, script, lineno, 1);
- } else if (!strcasecmp(keyword, "FLAG")) {
- if (!(args = get_token(&buf, script, lineno))) {
- ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script);
- break;
- }
- if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script);
- break;
- }
- if (getflagbyname(state, vname, script, lineno, 0)) {
- ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname);
- break;
- }
- getflagbyname(state, vname, script, lineno, 1);
- } else if (!strcasecmp(keyword, "DISPLAY")) {
- lrci = 0;
- wi = 0;
- if (!(args = get_token(&buf, script, lineno))) {
- ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
- break;
- }
- if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
- break;
- }
- if (getdisplaybyname(state, vname, script, lineno, 0)) {
- ast_log(LOG_WARNING, "State '%s' is already defined\n", vname);
- break;
- }
- if (!(disp = getdisplaybyname(state, vname, script, lineno, 1)))
- break;
- if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
- ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
- break;
- }
- if (!(args = get_token(&buf, script, lineno))) {
- ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script);
- break;
- }
- if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script);
- break;
- }
- if (strlen(tmp) > 20) {
- ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script);
- tmp[20] = '\0';
- }
- memcpy(disp->data + 5, tmp, strlen(tmp));
- disp->datalen = strlen(tmp) + 5;
- disp->data[disp->datalen++] = 0xff;
-
- args = get_token(&buf, script, lineno);
- if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
- /* Got a column two */
- if (strlen(tmp) > 20) {
- ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script);
- tmp[20] = '\0';
- }
- memcpy(disp->data + disp->datalen, tmp, strlen(tmp));
- disp->datalen += strlen(tmp);
- args = get_token(&buf, script, lineno);
- }
- while(args) {
- if (!strcasecmp(args, "JUSTIFY")) {
- args = get_token(&buf, script, lineno);
- if (!args) {
- ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script);
- break;
- }
- lrci = getjustifybyname(args);
- if (lrci < 0) {
- ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script);
- break;
- }
- } else if (!strcasecmp(args, "WRAP")) {
- wi = 0x80;
- } else {
- ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script);
- break;
- }
- args = get_token(&buf, script, lineno);
- }
- if (args) {
- /* Something bad happened */
- break;
- }
- disp->data[0] = 129;
- disp->data[1] = disp->datalen - 2;
- disp->data[2] = ((lrci & 0x3) << 6) | disp->id;
- disp->data[3] = wi;
- disp->data[4] = 0xff;
- } else {
- ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword);
- }
- break;
- case STATE_INKEY:
- if (process_returncode(state->key, keyword, buf, state, script, lineno)) {
- if (!strcasecmp(keyword, "ENDKEY")) {
- /* Return to normal operation and increment current key */
- state->state = STATE_NORMAL;
- state->key->defined = 1;
- state->key->retstr[1] = state->key->retstrlen - 2;
- state->key = NULL;
- } else {
- ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script);
- }
- }
- break;
- case STATE_INIF:
- if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
- if (!strcasecmp(keyword, "ENDIF")) {
- /* Return to normal SUB operation and increment current key */
- state->state = STATE_INSUB;
- state->sub->defined = 1;
- /* Store the proper number of instructions */
- state->sub->ifdata[2] = state->sub->ifinscount;
- } else if (!strcasecmp(keyword, "GOTO")) {
- if (!(args = get_token(&buf, script, lineno))) {
- ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script);
- break;
- }
- if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
- ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script);
- break;
- }
- if (!(newsub = getsubbyname(state, tmp, script, lineno)))
- break;
- /* Somehow you use GOTO to go to another place */
- state->sub->data[state->sub->datalen++] = 0x8;
- state->sub->data[state->sub->datalen++] = state->sub->ifdata[1];
- state->sub->data[state->sub->datalen++] = newsub->id;
- /* Terminate */
- state->sub->data[state->sub->datalen++] = 0xff;
- /* Increment counters */
- state->sub->inscount++;
- state->sub->ifinscount++;
- } else {
- ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script);
- }
- } else
- state->sub->ifinscount++;
- break;
- case STATE_INSUB:
- if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
- if (!strcasecmp(keyword, "ENDSUB")) {
- /* Return to normal operation and increment current key */
- state->state = STATE_NORMAL;
- state->sub->defined = 1;
- /* Store the proper length */
- state->sub->data[1] = state->sub->datalen - 2;
- if (state->sub->id) {
- /* if this isn't main, store number of instructions, too */
- state->sub->data[5] = state->sub->inscount;
- }
- state->sub = NULL;
- } else if (!strcasecmp(keyword, "IFEVENT")) {
- if (!(args = get_token(&buf, script, lineno))) {
- ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
- break;
- }
- if ((event = geteventbyname(args)) < 1) {
- ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
- break;
- }
- if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "THEN")) {
- ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script);
- break;
- }
- state->sub->ifinscount = 0;
- state->sub->ifdata = state->sub->data +
- state->sub->datalen;
- /* Reserve header and insert op codes */
- state->sub->ifdata[0] = 0x1;
- state->sub->ifdata[1] = event;
- /* 2 is for the number of instructions */
- state->sub->ifdata[3] = 0xff;
- state->sub->datalen += 4;
- /* Update Subscript instruction count */
- state->sub->inscount++;
- state->state = STATE_INIF;
- } else {
- ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script);
- }
- }
- break;
- default:
- ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state);
- }
- return 0;
-}
-
-static struct adsi_script *compile_script(char *script)
-{
- FILE *f;
- char fn[256], buf[256], *c;
- int lineno = 0, x, err;
- struct adsi_script *scr;
-
- if (script[0] == '/')
- ast_copy_string(fn, script, sizeof(fn));
- else
- snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, script);
-
- if (!(f = fopen(fn, "r"))) {
- ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
- return NULL;
- }
-
- if (!(scr = ast_calloc(1, sizeof(*scr)))) {
- fclose(f);
- return NULL;
- }
-
- /* Create "main" as first subroutine */
- getsubbyname(scr, "main", NULL, 0);
- while(!feof(f)) {
- fgets(buf, sizeof(buf), f);
- if (!feof(f)) {
- lineno++;
- /* Trim off trailing return */
- buf[strlen(buf) - 1] = '\0';
- /* Strip comments */
- if ((c = strchr(buf, ';')))
- *c = '\0';
- if (!ast_strlen_zero(buf))
- adsi_process(scr, buf, script, lineno);
- }
- }
- fclose(f);
- /* Make sure we're in the main routine again */
- switch(scr->state) {
- case STATE_NORMAL:
- break;
- case STATE_INSUB:
- ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script);
- ast_free(scr);
- return NULL;
- case STATE_INKEY:
- ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
- ast_free(scr);
- return NULL;
- }
- err = 0;
-
- /* Resolve all keys and record their lengths */
- for (x = 0; x < scr->numkeys; x++) {
- if (!scr->keys[x].defined) {
- ast_log(LOG_WARNING, "Key '%s' referenced but never defined in file %s\n", scr->keys[x].vname, fn);
- err++;
- }
- }
-
- /* Resolve all subs */
- for (x = 0; x < scr->numsubs; x++) {
- if (!scr->subs[x].defined) {
- ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn);
- err++;
- }
- if (x == (scr->numsubs - 1)) {
- /* Clear out extension bit on last message */
- scr->subs[x].data[2] = 0x80;
- }
- }
-
- if (err) {
- ast_free(scr);
- return NULL;
- }
- return scr;
-}
-
-#ifdef DUMP_MESSAGES
-static void dump_message(char *type, char *vname, unsigned char *buf, int buflen)
-{
- int x;
- printf("%s %s: [ ", type, vname);
- for (x = 0; x < buflen; x++)
- printf("%02x ", buf[x]);
- printf("]\n");
-}
-#endif
-
-static int adsi_prog(struct ast_channel *chan, char *script)
-{
- struct adsi_script *scr;
- int x, bytes;
- unsigned char buf[1024];
-
- if (!(scr = compile_script(script)))
- return -1;
-
- /* Start an empty ADSI Session */
- if (ast_adsi_load_session(chan, NULL, 0, 1) < 1)
- return -1;
-
- /* Now begin the download attempt */
- if (ast_adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
- /* User rejected us for some reason */
- ast_verb(3, "User rejected download attempt\n");
- ast_log(LOG_NOTICE, "User rejected download on channel %s\n", chan->name);
- ast_free(scr);
- return -1;
- }
-
- bytes = 0;
- /* Start with key definitions */
- for (x = 0; x < scr->numkeys; x++) {
- if (bytes + scr->keys[x].retstrlen > 253) {
- /* Send what we've collected so far */
- if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
- ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
- return -1;
- }
- bytes =0;
- }
- memcpy(buf + bytes, scr->keys[x].retstr, scr->keys[x].retstrlen);
- bytes += scr->keys[x].retstrlen;
-#ifdef DUMP_MESSAGES
- dump_message("Key", scr->keys[x].vname, scr->keys[x].retstr, scr->keys[x].retstrlen);
-#endif
- }
- if (bytes) {
- if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
- ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
- return -1;
- }
- }
-
- bytes = 0;
- /* Continue with the display messages */
- for (x = 0; x < scr->numdisplays; x++) {
- if (bytes + scr->displays[x].datalen > 253) {
- /* Send what we've collected so far */
- if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
- ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
- return -1;
- }
- bytes =0;
- }
- memcpy(buf + bytes, scr->displays[x].data, scr->displays[x].datalen);
- bytes += scr->displays[x].datalen;
-#ifdef DUMP_MESSAGES
- dump_message("Display", scr->displays[x].vname, scr->displays[x].data, scr->displays[x].datalen);
-#endif
- }
- if (bytes) {
- if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
- ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
- return -1;
- }
- }
-
- bytes = 0;
- /* Send subroutines */
- for (x = 0; x < scr->numsubs; x++) {
- if (bytes + scr->subs[x].datalen > 253) {
- /* Send what we've collected so far */
- if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
- ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
- return -1;
- }
- bytes =0;
- }
- memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen);
- bytes += scr->subs[x].datalen;
-#ifdef DUMP_MESSAGES
- dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen);
-#endif
- }
- if (bytes) {
- if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
- ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
- return -1;
- }
- }
-
-
- bytes = 0;
- bytes += ast_adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
- bytes += ast_adsi_set_line(buf, ADSI_INFO_PAGE, 1);
- if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
- return -1;
- if (ast_adsi_end_download(chan)) {
- /* Download failed for some reason */
- ast_verb(3, "Download attempt failed\n");
- ast_log(LOG_NOTICE, "Download failed on %s\n", chan->name);
- ast_free(scr);
- return -1;
- }
- ast_free(scr);
- ast_adsi_unload_session(chan);
- return 0;
-}
-
-static int adsi_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
-
- if (ast_strlen_zero(data))
- data = "asterisk.adsi";
-
- if (!ast_adsi_available(chan)) {
- ast_verb(3, "ADSI Unavailable on CPE. Not bothering to try.\n");
- } else {
- ast_verb(3, "ADSI Available on CPE. Attempting Upload.\n");
- res = adsi_prog(chan, data);
- }
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- if (ast_register_application(app, adsi_exec, synopsis, descrip))
- return AST_MODULE_LOAD_FAILURE;
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk ADSI Programming Application");
diff --git a/trunk/apps/app_alarmreceiver.c b/trunk/apps/app_alarmreceiver.c
deleted file mode 100644
index c4fa81109..000000000
--- a/trunk/apps/app_alarmreceiver.c
+++ /dev/null
@@ -1,813 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2004 - 2005 Steve Rodgers
- *
- * Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- * \brief Central Station Alarm receiver for Ademco Contact ID
- * \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
- *
- * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
- *
- * Use at your own risk. Please consult the GNU GPL license document included with Asterisk. *
- *
- * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <math.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/ulaw.h"
-#include "asterisk/app.h"
-#include "asterisk/dsp.h"
-#include "asterisk/config.h"
-#include "asterisk/localtime.h"
-#include "asterisk/callerid.h"
-#include "asterisk/astdb.h"
-#include "asterisk/utils.h"
-
-#define ALMRCV_CONFIG "alarmreceiver.conf"
-#define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
-
-struct event_node{
- char data[17];
- struct event_node *next;
-};
-
-typedef struct event_node event_node_t;
-
-static char *app = "AlarmReceiver";
-
-static char *synopsis = "Provide support for receiving alarm reports from a burglar or fire alarm panel";
-static char *descrip =
-" AlarmReceiver(): Only 1 signalling format is supported at this time: Ademco\n"
-"Contact ID. This application should be called whenever there is an alarm\n"
-"panel calling in to dump its events. The application will handshake with the\n"
-"alarm panel, and receive events, validate them, handshake them, and store them\n"
-"until the panel hangs up. Once the panel hangs up, the application will run the\n"
-"system command specified by the eventcmd setting in alarmreceiver.conf and pipe\n"
-"the events to the standard input of the application. The configuration file also\n"
-"contains settings for DTMF timing, and for the loudness of the acknowledgement\n"
-"tones.\n";
-
-/* Config Variables */
-
-static int fdtimeout = 2000;
-static int sdtimeout = 200;
-static int toneloudness = 4096;
-static int log_individual_events = 0;
-static char event_spool_dir[128] = {'\0'};
-static char event_app[128] = {'\0'};
-static char db_family[128] = {'\0'};
-static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
-
-/* Misc variables */
-
-static char event_file[14] = "/event-XXXXXX";
-
-/*
-* Attempt to access a database variable and increment it,
-* provided that the user defined db-family in alarmreceiver.conf
-* The alarmreceiver app will write statistics to a few variables
-* in this family if it is defined. If the new key doesn't exist in the
-* family, then create it and set its value to 1.
-*/
-
-static void database_increment( char *key )
-{
- int res = 0;
- unsigned v;
- char value[16];
-
-
- if (ast_strlen_zero(db_family))
- return; /* If not defined, don't do anything */
-
- res = ast_db_get(db_family, key, value, sizeof(value) - 1);
-
- if(res){
- ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
- /* Guess we have to create it */
- res = ast_db_put(db_family, key, "1");
- return;
- }
-
- sscanf(value, "%u", &v);
- v++;
-
- if(option_verbose >= 4)
- ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: New value for %s: %u\n", key, v);
-
- snprintf(value, sizeof(value), "%u", v);
-
- res = ast_db_put(db_family, key, value);
-
- if((res)&&(option_verbose >= 4))
- ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: database_increment write error\n");
-
- return;
-}
-
-
-/*
-* Build a MuLaw data block for a single frequency tone
-*/
-
-static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x)
-{
- int i;
- float val;
-
- for(i = 0; i < len; i++){
- val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0);
- data[i] = AST_LIN2MU((int)val);
- }
-
- /* wrap back around from 8000 */
-
- if (*x >= 8000) *x = 0;
- return;
-}
-
-/*
-* Send a single tone burst for a specifed duration and frequency.
-* Returns 0 if successful
-*/
-
-static int send_tone_burst(struct ast_channel *chan, float freq, int duration, int tldn)
-{
- int res = 0;
- int i = 0;
- int x = 0;
- struct ast_frame *f, wf;
-
- struct {
- unsigned char offset[AST_FRIENDLY_OFFSET];
- unsigned char buf[640];
- } tone_block;
-
- for(;;)
- {
-
- if (ast_waitfor(chan, -1) < 0){
- res = -1;
- break;
- }
-
- f = ast_read(chan);
- if (!f){
- res = -1;
- break;
- }
-
- if (f->frametype == AST_FRAME_VOICE) {
- wf.frametype = AST_FRAME_VOICE;
- wf.subclass = AST_FORMAT_ULAW;
- wf.offset = AST_FRIENDLY_OFFSET;
- wf.mallocd = 0;
- wf.data = tone_block.buf;
- wf.datalen = f->datalen;
- wf.samples = wf.datalen;
-
- make_tone_burst(tone_block.buf, freq, (float) tldn, wf.datalen, &x);
-
- i += wf.datalen / 8;
- if (i > duration) {
- ast_frfree(f);
- break;
- }
- if (ast_write(chan, &wf)){
- ast_verb(4, "AlarmReceiver: Failed to write frame on %s\n", chan->name);
- ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",chan->name);
- res = -1;
- ast_frfree(f);
- break;
- }
- }
-
- ast_frfree(f);
- }
- return res;
-}
-
-/*
-* Receive a string of DTMF digits where the length of the digit string is known in advance. Do not give preferential
-* treatment to any digit value, and allow separate time out values to be specified for the first digit and all subsequent
-* digits.
-*
-* Returns 0 if all digits successfully received.
-* Returns 1 if a digit time out occurred
-* Returns -1 if the caller hung up or there was a channel error.
-*
-*/
-
-static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto)
-{
- int res = 0;
- int i = 0;
- int r;
- struct ast_frame *f;
- struct timeval lastdigittime;
-
- lastdigittime = ast_tvnow();
- for(;;){
- /* if outa time, leave */
- if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
- ((i > 0) ? sdto : fdto)){
- if(option_verbose >= 4)
- ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: DTMF Digit Timeout on %s\n", chan->name);
-
- ast_debug(1,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name);
-
- res = 1;
- break;
- }
-
- if ((r = ast_waitfor(chan, -1) < 0)) {
- ast_debug(1, "Waitfor returned %d\n", r);
- continue;
- }
-
- f = ast_read(chan);
-
- if (f == NULL){
- res = -1;
- break;
- }
-
- /* If they hung up, leave */
- if ((f->frametype == AST_FRAME_CONTROL) &&
- (f->subclass == AST_CONTROL_HANGUP)){
- ast_frfree(f);
- res = -1;
- break;
- }
-
- /* if not DTMF, just do it again */
- if (f->frametype != AST_FRAME_DTMF){
- ast_frfree(f);
- continue;
- }
-
- digit_string[i++] = f->subclass; /* save digit */
-
- ast_frfree(f);
-
- /* If we have all the digits we expect, leave */
- if(i >= length)
- break;
-
- lastdigittime = ast_tvnow();
- }
-
- digit_string[i] = '\0'; /* Nul terminate the end of the digit string */
- return res;
-
-}
-
-/*
-* Write the metadata to the log file
-*/
-
-static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
-{
- int res = 0;
- struct timeval t;
- struct ast_tm now;
- char *cl,*cn;
- char workstring[80];
- char timestamp[80];
-
- /* Extract the caller ID location */
- if (chan->cid.cid_num)
- ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
- workstring[sizeof(workstring) - 1] = '\0';
-
- ast_callerid_parse(workstring, &cn, &cl);
- if (cl)
- ast_shrink_phone_number(cl);
-
-
- /* Get the current time */
-
- t = ast_tvnow();
- ast_localtime(&t, &now, NULL);
-
- /* Format the time */
-
- ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
-
-
- res = fprintf(logfile, "\n\n[metadata]\n\n");
-
- if(res >= 0)
- res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
-
- if(res >= 0)
- res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "<unknown>" : cl);
-
- if(res >- 0)
- res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "<unknown>" : cn);
-
- if(res >= 0)
- res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
-
- if(res >= 0)
- res = fprintf(logfile, "[events]\n\n");
-
- if(res < 0){
- if (option_verbose >= 3 )
- ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't write metadata\n");
-
- ast_debug(1,"AlarmReceiver: can't write metadata\n");
- }
- else
- res = 0;
-
- return res;
-}
-
-/*
-* Write a single event to the log file
-*/
-
-static int write_event( FILE *logfile, event_node_t *event)
-{
- int res = 0;
-
- if( fprintf(logfile, "%s\n", event->data) < 0)
- res = -1;
-
- return res;
-}
-
-
-/*
-* If we are configured to log events, do so here.
-*
-*/
-
-static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event)
-{
-
- int res = 0;
- char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
- int fd;
- FILE *logfile;
- event_node_t *elp = event;
-
- if (!ast_strlen_zero(event_spool_dir)) {
-
- /* Make a template */
-
- ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
- strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
-
- /* Make the temporary file */
-
- fd = mkstemp(workstring);
-
- if(fd == -1) {
- if (option_verbose >= 3)
- ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't make temporary file\n");
- ast_debug(1,"AlarmReceiver: can't make temporary file\n");
- res = -1;
- }
-
- if(!res){
- logfile = fdopen(fd, "w");
- if(logfile){
- /* Write the file */
- res = write_metadata(logfile, signalling_type, chan);
- if(!res)
- while((!res) && (elp != NULL)){
- res = write_event(logfile, elp);
- elp = elp->next;
- }
- if(!res){
- if(fflush(logfile) == EOF)
- res = -1;
- if(!res){
- if(fclose(logfile) == EOF)
- res = -1;
- }
- }
- }
- else
- res = -1;
- }
- }
-
- return res;
-}
-
-/*
-* This function implements the logic to receive the Ademco contact ID format.
-*
-* The function will return 0 when the caller hangs up, else a -1 if there was a problem.
-*/
-
-static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
-{
- int i,j;
- int res = 0;
- int checksum;
- char event[17];
- event_node_t *enew, *elp;
- int got_some_digits = 0;
- int events_received = 0;
- int ack_retries = 0;
-
- static char digit_map[15] = "0123456789*#ABC";
- static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
-
- database_increment("calls-received");
-
- /* Wait for first event */
-
- if(option_verbose >= 4)
- ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for first event from panel\n");
-
- while(res >= 0){
-
- if(got_some_digits == 0){
-
- /* Send ACK tone sequence */
-
-
- if(option_verbose >= 4)
- ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
-
-
- res = send_tone_burst(chan, 1400.0, 100, tldn);
-
- if(!res)
- res = ast_safe_sleep(chan, 100);
-
- if(!res){
- if(option_verbose >= 4)
- ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
-
- res = send_tone_burst(chan, 2300.0, 100, tldn);
- }
-
- }
-
- if( res >= 0)
- res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
-
- if (res < 0){
-
- if(events_received == 0)
- /* Hangup with no events received should be logged in the DB */
- database_increment("no-events-received");
- else{
- if(ack_retries){
- if(option_verbose >= 4)
- ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
-
- database_increment("ack-retries");
- }
- }
- if(option_verbose >= 4)
- ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: App exiting...\n");
- res = -1;
- break;
- }
-
- if(res != 0){
- /* Didn't get all of the digits */
- if(option_verbose >= 2)
- ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
-
- if(!got_some_digits){
- got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
- ack_retries++;
- }
- continue;
- }
-
- got_some_digits = 1;
-
- ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
- ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
-
- /* Calculate checksum */
-
- for(j = 0, checksum = 0; j < 16; j++){
- for(i = 0 ; i < sizeof(digit_map) ; i++){
- if(digit_map[i] == event[j])
- break;
- }
-
- if(i == 16)
- break;
-
- checksum += digit_weights[i];
- }
-
- if(i == 16){
- ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
- continue; /* Bad character */
- }
-
- /* Checksum is mod(15) of the total */
-
- checksum = checksum % 15;
-
- if (checksum) {
- database_increment("checksum-errors");
- ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
- ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
- continue;
- }
-
- /* Check the message type for correctness */
-
- if(strncmp(event + 4, "18", 2)){
- if(strncmp(event + 4, "98", 2)){
- database_increment("format-errors");
- ast_verb(2, "AlarmReceiver: Wrong message type\n");
- ast_debug(1, "AlarmReceiver: Wrong message type\n");
- continue;
- }
- }
-
- events_received++;
-
- /* Queue the Event */
- if (!(enew = ast_calloc(1, sizeof(*enew)))) {
- res = -1;
- break;
- }
-
- enew->next = NULL;
- ast_copy_string(enew->data, event, sizeof(enew->data));
-
- /*
- * Insert event onto end of list
- */
-
- if(*ehead == NULL){
- *ehead = enew;
- }
- else{
- for(elp = *ehead; elp->next != NULL; elp = elp->next)
- ;
-
- elp->next = enew;
- }
-
- if(res > 0)
- res = 0;
-
- /* Let the user have the option of logging the single event before sending the kissoff tone */
-
- if((res == 0) && (log_individual_events))
- res = log_events(chan, ADEMCO_CONTACT_ID, enew);
-
- /* Wait 200 msec before sending kissoff */
-
- if(res == 0)
- res = ast_safe_sleep(chan, 200);
-
- /* Send the kissoff tone */
-
- if(res == 0)
- res = send_tone_burst(chan, 1400.0, 900, tldn);
- }
-
-
- return res;
-}
-
-
-/*
-* This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
-* This function will always return 0.
-*/
-
-static int alarmreceiver_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- event_node_t *elp, *efree;
- char signalling_type[64] = "";
-
- event_node_t *event_head = NULL;
-
- /* Set write and read formats to ULAW */
-
- ast_verb(4, "AlarmReceiver: Setting read and write formats to ULAW\n");
-
- if (ast_set_write_format(chan,AST_FORMAT_ULAW)){
- ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
- return -1;
- }
-
- if (ast_set_read_format(chan,AST_FORMAT_ULAW)){
- ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
- return -1;
- }
-
- /* Set default values for this invocation of the application */
-
- ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
-
-
- /* Answer the channel if it is not already */
-
- ast_verb(4, "AlarmReceiver: Answering channel\n");
-
- if (chan->_state != AST_STATE_UP) {
- if ((res = ast_answer(chan)))
- return -1;
- }
-
- /* Wait for the connection to settle post-answer */
-
- ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
-
- res = ast_safe_sleep(chan, 1250);
-
- /* Attempt to receive the events */
-
- if(!res){
-
- /* Determine the protocol to receive in advance */
- /* Note: Ademco contact is the only one supported at this time */
- /* Others may be added later */
-
- if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
- receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
- else
- res = -1;
- }
-
-
-
- /* Events queued by receiver, write them all out here if so configured */
-
- if((!res) && (log_individual_events == 0)){
- res = log_events(chan, signalling_type, event_head);
-
- }
-
- /*
- * Do we exec a command line at the end?
- */
-
- if((!res) && (!ast_strlen_zero(event_app)) && (event_head)){
- ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
- ast_safe_system(event_app);
- }
-
- /*
- * Free up the data allocated in our linked list
- */
-
- for(elp = event_head; (elp != NULL);){
- efree = elp;
- elp = elp->next;
- ast_free(efree);
- }
-
- return 0;
-}
-
-/*
-* Load the configuration from the configuration file
-*/
-
-static int load_config(void)
-{
- struct ast_config *cfg;
- const char *p;
- struct ast_flags config_flags = { 0 };
-
- /* Read in the config file */
-
- cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
-
- if(!cfg){
-
- if(option_verbose >= 4)
- ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: No config file\n");
- return 0;
- }
- else{
-
-
- p = ast_variable_retrieve(cfg, "general", "eventcmd");
-
- if(p){
- ast_copy_string(event_app, p, sizeof(event_app));
- event_app[sizeof(event_app) - 1] = '\0';
- }
-
- p = ast_variable_retrieve(cfg, "general", "loudness");
- if(p){
- toneloudness = atoi(p);
- if(toneloudness < 100)
- toneloudness = 100;
- if(toneloudness > 8192)
- toneloudness = 8192;
- }
- p = ast_variable_retrieve(cfg, "general", "fdtimeout");
- if(p){
- fdtimeout = atoi(p);
- if(fdtimeout < 1000)
- fdtimeout = 1000;
- if(fdtimeout > 10000)
- fdtimeout = 10000;
- }
-
- p = ast_variable_retrieve(cfg, "general", "sdtimeout");
- if(p){
- sdtimeout = atoi(p);
- if(sdtimeout < 110)
- sdtimeout = 110;
- if(sdtimeout > 4000)
- sdtimeout = 4000;
-
- }
-
- p = ast_variable_retrieve(cfg, "general", "logindividualevents");
- if(p){
- log_individual_events = ast_true(p);
-
- }
-
- p = ast_variable_retrieve(cfg, "general", "eventspooldir");
-
- if(p){
- ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
- event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
- }
-
- p = ast_variable_retrieve(cfg, "general", "timestampformat");
-
- if(p){
- ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
- time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
- }
-
- p = ast_variable_retrieve(cfg, "general", "db-family");
-
- if(p){
- ast_copy_string(db_family, p, sizeof(db_family));
- db_family[sizeof(db_family) - 1] = '\0';
- }
- ast_config_destroy(cfg);
- }
- return 1;
-
-}
-
-/*
-* These functions are required to implement an Asterisk App.
-*/
-
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- if(load_config()) {
- if (ast_register_application(app, alarmreceiver_exec, synopsis, descrip))
- return AST_MODULE_LOAD_FAILURE;
- return AST_MODULE_LOAD_SUCCESS;
- }
- else
- return AST_MODULE_LOAD_DECLINE;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");
diff --git a/trunk/apps/app_amd.c b/trunk/apps/app_amd.c
deleted file mode 100644
index 4e440d388..000000000
--- a/trunk/apps/app_amd.c
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2003 - 2006, Aheeva Technology.
- *
- * Claude Klimos (claude.klimos@aheeva.com)
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- *
- * A license has been granted to Digium (via disclaimer) for the use of
- * this code.
- */
-
-/*! \file
- *
- * \brief Answering machine detection
- *
- * \author Claude Klimos (claude.klimos@aheeva.com)
- */
-
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-#include "asterisk/channel.h"
-#include "asterisk/dsp.h"
-#include "asterisk/pbx.h"
-#include "asterisk/config.h"
-#include "asterisk/app.h"
-
-
-static char *app = "AMD";
-static char *synopsis = "Attempts to detect answering machines";
-static char *descrip =
-" AMD([initialSilence],[greeting],[afterGreetingSilence],[totalAnalysisTime]\n"
-" ,[minimumWordLength],[betweenWordsSilence],[maximumNumberOfWords]\n"
-" ,[silenceThreshold],[|maximumWordLength])\n"
-" This application attempts to detect answering machines at the beginning\n"
-" of outbound calls. Simply call this application after the call\n"
-" has been answered (outbound only, of course).\n"
-" When loaded, AMD reads amd.conf and uses the parameters specified as\n"
-" default values. Those default values get overwritten when calling AMD\n"
-" with parameters.\n"
-"- 'initialSilence' is the maximum silence duration before the greeting. If\n"
-" exceeded then MACHINE.\n"
-"- 'greeting' is the maximum length of a greeting. If exceeded then MACHINE.\n"
-"- 'afterGreetingSilence' is the silence after detecting a greeting.\n"
-" If exceeded then HUMAN.\n"
-"- 'totalAnalysisTime' is the maximum time allowed for the algorithm to decide\n"
-" on a HUMAN or MACHINE.\n"
-"- 'minimumWordLength'is the minimum duration of Voice to considered as a word.\n"
-"- 'betweenWordsSilence' is the minimum duration of silence after a word to \n"
-" consider the audio that follows as a new word.\n"
-"- 'maximumNumberOfWords'is the maximum number of words in the greeting. \n"
-" If exceeded then MACHINE.\n"
-"- 'silenceThreshold' is the silence threshold.\n"
-"- 'maximumWordLength' is the maximum duration of a word to accept. If exceeded then MACHINE\n"
-"This application sets the following channel variables upon completion:\n"
-" AMDSTATUS - This is the status of the answering machine detection.\n"
-" Possible values are:\n"
-" MACHINE | HUMAN | NOTSURE | HANGUP\n"
-" AMDCAUSE - Indicates the cause that led to the conclusion.\n"
-" Possible values are:\n"
-" TOOLONG-<%d total_time>\n"
-" INITIALSILENCE-<%d silenceDuration>-<%d initialSilence>\n"
-" HUMAN-<%d silenceDuration>-<%d afterGreetingSilence>\n"
-" MAXWORDS-<%d wordsCount>-<%d maximumNumberOfWords>\n"
-" LONGGREETING-<%d voiceDuration>-<%d greeting>\n"
-" MAXWORDLENGTH-<%d consecutiveVoiceDuration>\n";
-
-#define STATE_IN_WORD 1
-#define STATE_IN_SILENCE 2
-
-/* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
-static int dfltInitialSilence = 2500;
-static int dfltGreeting = 1500;
-static int dfltAfterGreetingSilence = 800;
-static int dfltTotalAnalysisTime = 5000;
-static int dfltMinimumWordLength = 100;
-static int dfltBetweenWordsSilence = 50;
-static int dfltMaximumNumberOfWords = 3;
-static int dfltSilenceThreshold = 256;
-static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
-
-static void isAnsweringMachine(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_frame *f = NULL;
- struct ast_dsp *silenceDetector = NULL;
- int dspsilence = 0, readFormat, framelength;
- int inInitialSilence = 1;
- int inGreeting = 0;
- int voiceDuration = 0;
- int silenceDuration = 0;
- int iTotalTime = 0;
- int iWordsCount = 0;
- int currentState = STATE_IN_SILENCE;
- int previousState = STATE_IN_SILENCE;
- int consecutiveVoiceDuration = 0;
- char amdCause[256] = "", amdStatus[256] = "";
- char *parse = ast_strdupa(data);
-
- /* Lets set the initial values of the variables that will control the algorithm.
- The initial values are the default ones. If they are passed as arguments
- when invoking the application, then the default values will be overwritten
- by the ones passed as parameters. */
- int initialSilence = dfltInitialSilence;
- int greeting = dfltGreeting;
- int afterGreetingSilence = dfltAfterGreetingSilence;
- int totalAnalysisTime = dfltTotalAnalysisTime;
- int minimumWordLength = dfltMinimumWordLength;
- int betweenWordsSilence = dfltBetweenWordsSilence;
- int maximumNumberOfWords = dfltMaximumNumberOfWords;
- int silenceThreshold = dfltSilenceThreshold;
- int maximumWordLength = dfltMaximumWordLength;
-
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(argInitialSilence);
- AST_APP_ARG(argGreeting);
- AST_APP_ARG(argAfterGreetingSilence);
- AST_APP_ARG(argTotalAnalysisTime);
- AST_APP_ARG(argMinimumWordLength);
- AST_APP_ARG(argBetweenWordsSilence);
- AST_APP_ARG(argMaximumNumberOfWords);
- AST_APP_ARG(argSilenceThreshold);
- AST_APP_ARG(argMaximumWordLength);
- );
-
- ast_verb(3, "AMD: %s %s %s (Fmt: %d)\n", chan->name ,chan->cid.cid_ani, chan->cid.cid_rdnis, chan->readformat);
-
- /* Lets parse the arguments. */
- if (!ast_strlen_zero(parse)) {
- /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
- AST_STANDARD_APP_ARGS(args, parse);
- if (!ast_strlen_zero(args.argInitialSilence))
- initialSilence = atoi(args.argInitialSilence);
- if (!ast_strlen_zero(args.argGreeting))
- greeting = atoi(args.argGreeting);
- if (!ast_strlen_zero(args.argAfterGreetingSilence))
- afterGreetingSilence = atoi(args.argAfterGreetingSilence);
- if (!ast_strlen_zero(args.argTotalAnalysisTime))
- totalAnalysisTime = atoi(args.argTotalAnalysisTime);
- if (!ast_strlen_zero(args.argMinimumWordLength))
- minimumWordLength = atoi(args.argMinimumWordLength);
- if (!ast_strlen_zero(args.argBetweenWordsSilence))
- betweenWordsSilence = atoi(args.argBetweenWordsSilence);
- if (!ast_strlen_zero(args.argMaximumNumberOfWords))
- maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
- if (!ast_strlen_zero(args.argSilenceThreshold))
- silenceThreshold = atoi(args.argSilenceThreshold);
- if (!ast_strlen_zero(args.argMaximumWordLength))
- maximumWordLength = atoi(args.argMaximumWordLength);
- } else {
- ast_debug(1, "AMD using the default parameters.\n");
- }
-
- /* Now we're ready to roll! */
- ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
- "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
- initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
- minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
-
- /* Set read format to signed linear so we get signed linear frames in */
- readFormat = chan->readformat;
- if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) {
- ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name );
- pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
- pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
- return;
- }
-
- /* Create a new DSP that will detect the silence */
- if (!(silenceDetector = ast_dsp_new())) {
- ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name );
- pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
- pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
- return;
- }
-
- /* Set silence threshold to specified value */
- ast_dsp_set_threshold(silenceDetector, silenceThreshold);
-
- /* Now we go into a loop waiting for frames from the channel */
- while ((res = ast_waitfor(chan, totalAnalysisTime)) > -1) {
- /* If we fail to read in a frame, that means they hung up */
- if (!(f = ast_read(chan))) {
- ast_verb(3, "AMD: Channel [%s]. HANGUP\n", chan->name);
- ast_debug(1, "Got hangup\n");
- strcpy(amdStatus, "HANGUP");
- break;
- }
-
- if (f->frametype == AST_FRAME_VOICE) {
- /* If the total time exceeds the analysis time then give up as we are not too sure */
- framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
- iTotalTime += framelength;
- if (iTotalTime >= totalAnalysisTime) {
- ast_verb(3, "AMD: Channel [%s]. Too long...\n", chan->name );
- ast_frfree(f);
- strcpy(amdStatus , "NOTSURE");
- sprintf(amdCause , "TOOLONG-%d", iTotalTime);
- break;
- }
-
- /* Feed the frame of audio into the silence detector and see if we get a result */
- dspsilence = 0;
- ast_dsp_silence(silenceDetector, f, &dspsilence);
- if (dspsilence) {
- silenceDuration = dspsilence;
-
- if (silenceDuration >= betweenWordsSilence) {
- if (currentState != STATE_IN_SILENCE ) {
- previousState = currentState;
- ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", chan->name);
- }
- /* Find words less than word duration */
- if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
- ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", chan->name, consecutiveVoiceDuration);
- }
- currentState = STATE_IN_SILENCE;
- consecutiveVoiceDuration = 0;
- }
-
- if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
- ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
- chan->name, silenceDuration, initialSilence);
- ast_frfree(f);
- strcpy(amdStatus , "MACHINE");
- sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
- break;
- }
-
- if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
- ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
- chan->name, silenceDuration, afterGreetingSilence);
- ast_frfree(f);
- strcpy(amdStatus , "HUMAN");
- sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
- break;
- }
-
- } else {
- consecutiveVoiceDuration += framelength;
- voiceDuration += framelength;
-
- /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
- number of words if my previous state was Silence, which means that I moved into a word. */
- if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
- iWordsCount++;
- ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", chan->name, iWordsCount);
- previousState = currentState;
- currentState = STATE_IN_WORD;
- }
- if (consecutiveVoiceDuration >= maximumWordLength){
- ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", chan->name, consecutiveVoiceDuration);
- ast_frfree(f);
- strcpy(amdStatus , "MACHINE");
- sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
- break;
- }
- if (iWordsCount >= maximumNumberOfWords) {
- ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", chan->name, iWordsCount);
- ast_frfree(f);
- strcpy(amdStatus , "MACHINE");
- sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
- break;
- }
-
- if (inGreeting == 1 && voiceDuration >= greeting) {
- ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", chan->name, voiceDuration, greeting);
- ast_frfree(f);
- strcpy(amdStatus , "MACHINE");
- sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
- break;
- }
-
- if (voiceDuration >= minimumWordLength ) {
- if (silenceDuration > 0)
- ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", chan->name, silenceDuration);
- silenceDuration = 0;
- }
- if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0){
- /* Only go in here once to change the greeting flag when we detect the 1st word */
- if (silenceDuration > 0)
- ast_verb(3, "AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n", chan->name, silenceDuration, voiceDuration);
- inInitialSilence = 0;
- inGreeting = 1;
- }
-
- }
- }
- ast_frfree(f);
- }
-
- if (!res) {
- /* It took too long to get a frame back. Giving up. */
- ast_verb(3, "AMD: Channel [%s]. Too long...\n", chan->name);
- strcpy(amdStatus , "NOTSURE");
- sprintf(amdCause , "TOOLONG-%d", iTotalTime);
- }
-
- /* Set the status and cause on the channel */
- pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
- pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
-
- /* Restore channel read format */
- if (readFormat && ast_set_read_format(chan, readFormat))
- ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name);
-
- /* Free the DSP used to detect silence */
- ast_dsp_free(silenceDetector);
-
- return;
-}
-
-
-static int amd_exec(struct ast_channel *chan, void *data)
-{
- isAnsweringMachine(chan, data);
-
- return 0;
-}
-
-static int load_config(int reload)
-{
- struct ast_config *cfg = NULL;
- char *cat = NULL;
- struct ast_variable *var = NULL;
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
-
- if (!(cfg = ast_config_load("amd.conf", config_flags))) {
- ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
- return -1;
- } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
-
- cat = ast_category_browse(cfg, NULL);
-
- while (cat) {
- if (!strcasecmp(cat, "general") ) {
- var = ast_variable_browse(cfg, cat);
- while (var) {
- if (!strcasecmp(var->name, "initial_silence")) {
- dfltInitialSilence = atoi(var->value);
- } else if (!strcasecmp(var->name, "greeting")) {
- dfltGreeting = atoi(var->value);
- } else if (!strcasecmp(var->name, "after_greeting_silence")) {
- dfltAfterGreetingSilence = atoi(var->value);
- } else if (!strcasecmp(var->name, "silence_threshold")) {
- dfltSilenceThreshold = atoi(var->value);
- } else if (!strcasecmp(var->name, "total_analysis_time")) {
- dfltTotalAnalysisTime = atoi(var->value);
- } else if (!strcasecmp(var->name, "min_word_length")) {
- dfltMinimumWordLength = atoi(var->value);
- } else if (!strcasecmp(var->name, "between_words_silence")) {
- dfltBetweenWordsSilence = atoi(var->value);
- } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
- dfltMaximumNumberOfWords = atoi(var->value);
- } else if (!strcasecmp(var->name, "maximum_word_length")) {
- dfltMaximumWordLength = atoi(var->value);
-
- } else {
- ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
- app, cat, var->name, var->lineno);
- }
- var = var->next;
- }
- }
- cat = ast_category_browse(cfg, cat);
- }
-
- ast_config_destroy(cfg);
-
- ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
- "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
- dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
- dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
-
- return 0;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- if (load_config(0))
- return AST_MODULE_LOAD_DECLINE;
- if (ast_register_application(app, amd_exec, synopsis, descrip))
- return AST_MODULE_LOAD_FAILURE;
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-static int reload(void)
-{
- if (load_config(1))
- return AST_MODULE_LOAD_DECLINE;
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
diff --git a/trunk/apps/app_authenticate.c b/trunk/apps/app_authenticate.c
deleted file mode 100644
index 3a3e7582e..000000000
--- a/trunk/apps/app_authenticate.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Execute arbitrary authenticate commands
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-#include "asterisk/astdb.h"
-#include "asterisk/utils.h"
-
-enum {
- OPT_ACCOUNT = (1 << 0),
- OPT_DATABASE = (1 << 1),
- OPT_MULTIPLE = (1 << 3),
- OPT_REMOVE = (1 << 4),
-} auth_option_flags;
-
-AST_APP_OPTIONS(auth_app_options, {
- AST_APP_OPTION('a', OPT_ACCOUNT),
- AST_APP_OPTION('d', OPT_DATABASE),
- AST_APP_OPTION('m', OPT_MULTIPLE),
- AST_APP_OPTION('r', OPT_REMOVE),
-});
-
-
-static char *app = "Authenticate";
-
-static char *synopsis = "Authenticate a user";
-
-static char *descrip =
-" Authenticate(password[,options[,maxdigits]]): This application asks the caller\n"
-"to enter a given password in order to continue dialplan execution. If the password\n"
-"begins with the '/' character, it is interpreted as a file which contains a list of\n"
-"valid passwords, listed 1 password per line in the file.\n"
-" When using a database key, the value associated with the key can be anything.\n"
-"Users have three attempts to authenticate before the channel is hung up.\n"
-" Options:\n"
-" a - Set the channels' account code to the password that is entered\n"
-" d - Interpret the given path as database key, not a literal file\n"
-" m - Interpret the given path as a file which contains a list of account\n"
-" codes and password hashes delimited with ':', listed one per line in\n"
-" the file. When one of the passwords is matched, the channel will have\n"
-" its account code set to the corresponding account code in the file.\n"
-" r - Remove the database key upon successful entry (valid with 'd' only)\n"
-" maxdigits - maximum acceptable number of digits. Stops reading after\n"
-" maxdigits have been entered (without requiring the user to\n"
-" press the '#' key).\n"
-" Defaults to 0 - no limit - wait for the user press the '#' key.\n"
-;
-
-static int auth_exec(struct ast_channel *chan, void *data)
-{
- int res = 0, retries, maxdigits;
- char passwd[256], *prompt = "agent-pass", *argcopy = NULL;
- struct ast_flags flags = {0};
-
- AST_DECLARE_APP_ARGS(arglist,
- AST_APP_ARG(password);
- AST_APP_ARG(options);
- AST_APP_ARG(maxdigits);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
- return -1;
- }
-
- if (chan->_state != AST_STATE_UP) {
- if ((res = ast_answer(chan)))
- return -1;
- }
-
- argcopy = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(arglist, argcopy);
-
- if (!ast_strlen_zero(arglist.options))
- ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options);
-
- if (!ast_strlen_zero(arglist.maxdigits)) {
- maxdigits = atoi(arglist.maxdigits);
- if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2))
- maxdigits = sizeof(passwd) - 2;
- } else {
- maxdigits = sizeof(passwd) - 2;
- }
-
- /* Start asking for password */
- for (retries = 0; retries < 3; retries++) {
- if ((res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0)) < 0)
- break;
- res = 0;
- if (arglist.password[0] == '/') {
- if (ast_test_flag(&flags,OPT_DATABASE)) {
- char tmp[256];
- /* Compare against a database key */
- if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) {
- /* It's a good password */
- if (ast_test_flag(&flags,OPT_REMOVE))
- ast_db_del(arglist.password + 1, passwd);
- break;
- }
- } else {
- /* Compare against a file */
- FILE *f;
- char buf[256] = "", md5passwd[33] = "", *md5secret = NULL;
-
- if (!(f = fopen(arglist.password, "r"))) {
- ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
- continue;
- }
-
- while (!feof(f)) {
- fgets(buf, sizeof(buf), f);
- if (!feof(f) && !ast_strlen_zero(buf)) {
- buf[strlen(buf) - 1] = '\0';
- if (ast_test_flag(&flags,OPT_MULTIPLE)) {
- md5secret = strchr(buf, ':');
- if (md5secret == NULL)
- continue;
- *md5secret = '\0';
- md5secret++;
- ast_md5_hash(md5passwd, passwd);
- if (!strcmp(md5passwd, md5secret)) {
- if (ast_test_flag(&flags,OPT_ACCOUNT))
- ast_cdr_setaccount(chan, buf);
- break;
- }
- } else {
- if (!strcmp(passwd, buf)) {
- if (ast_test_flag(&flags,OPT_ACCOUNT))
- ast_cdr_setaccount(chan, buf);
- break;
- }
- }
- }
- }
- fclose(f);
- if (!ast_strlen_zero(buf)) {
- if (ast_test_flag(&flags,OPT_MULTIPLE)) {
- if (md5secret && !strcmp(md5passwd, md5secret))
- break;
- } else {
- if (!strcmp(passwd, buf))
- break;
- }
- }
- }
- } else {
- /* Compare against a fixed password */
- if (!strcmp(passwd, arglist.password))
- break;
- }
- prompt = "auth-incorrect";
- }
- if ((retries < 3) && !res) {
- if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE))
- ast_cdr_setaccount(chan, passwd);
- if (!(res = ast_streamfile(chan, "auth-thankyou", chan->language)))
- res = ast_waitstream(chan, "");
- } else {
- if (!ast_streamfile(chan, "vm-goodbye", chan->language))
- res = ast_waitstream(chan, "");
- res = -1;
- }
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- if (ast_register_application(app, auth_exec, synopsis, descrip))
- return AST_MODULE_LOAD_FAILURE;
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");
diff --git a/trunk/apps/app_cdr.c b/trunk/apps/app_cdr.c
deleted file mode 100644
index 7804a806b..000000000
--- a/trunk/apps/app_cdr.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Martin Pycko <martinp@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Applications connected with CDR engine
- *
- * \author Martin Pycko <martinp@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/channel.h"
-#include "asterisk/module.h"
-
-static char *nocdr_descrip =
-" NoCDR(): This application will tell Asterisk not to maintain a CDR for the\n"
-"current call.\n";
-
-static char *nocdr_app = "NoCDR";
-static char *nocdr_synopsis = "Tell Asterisk to not maintain a CDR for the current call";
-
-
-static int nocdr_exec(struct ast_channel *chan, void *data)
-{
- if (chan->cdr)
- ast_set_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED);
-
- return 0;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(nocdr_app);
-}
-
-static int load_module(void)
-{
- if (ast_register_application(nocdr_app, nocdr_exec, nocdr_synopsis, nocdr_descrip))
- return AST_MODULE_LOAD_FAILURE;
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Tell Asterisk to not maintain a CDR for the current call");
diff --git a/trunk/apps/app_chanisavail.c b/trunk/apps/app_chanisavail.c
deleted file mode 100644
index 67d29e6ee..000000000
--- a/trunk/apps/app_chanisavail.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
-* Asterisk -- An open source telephony toolkit.
-*
-* Copyright (C) 1999 - 2005, Digium, Inc.
-*
-* Mark Spencer <markster@digium.com>
-* James Golovich <james@gnuinter.net>
-*
-* See http://www.asterisk.org for more information about
-* the Asterisk project. Please do not directly contact
-* any of the maintainers of this project for assistance;
-* the project provides a web site, mailing lists and IRC
-* channels for your use.
-*
-* This program is free software, distributed under the terms of
-* the GNU General Public License Version 2. See the LICENSE file
-* at the top of the source tree.
-*/
-
-/*! \file
- *
- * \brief Check if Channel is Available
- *
- * \author Mark Spencer <markster@digium.com>
- * \author James Golovich <james@gnuinter.net>
-
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <sys/ioctl.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-#include "asterisk/devicestate.h"
-
-static char *app = "ChanIsAvail";
-
-static char *synopsis = "Check channel availability";
-
-static char *descrip =
-" ChanIsAvail(Technology/resource[&Technology2/resource2...][,options]): \n"
-"This application will check to see if any of the specified channels are\n"
-"available.\n"
-" Options:\n"
-" s - Consider the channel unavailable if the channel is in use at all.\n"
-" t - Simply checks if specified channels exist in the channel list\n"
-" (implies option s).\n"
-"This application sets the following channel variable upon completion:\n"
-" AVAILCHAN - the name of the available channel, if one exists\n"
-" AVAILORIGCHAN - the canonical channel name that was used to create the channel\n"
-" AVAILSTATUS - the status code for the available channel\n";
-
-
-static int chanavail_exec(struct ast_channel *chan, void *data)
-{
- int res=-1, inuse=-1, option_state=0, string_compare=0;
- int status;
- char *info, tmp[512], trychan[512], *peers, *tech, *number, *rest, *cur;
- struct ast_channel *tempchan;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(reqchans);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "ChanIsAvail requires an argument (Zap/1&Zap/2)\n");
- return -1;
- }
-
- info = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, info);
-
- if (args.options) {
- if (strchr(args.options, 's'))
- option_state = 1;
- if (strchr(args.options, 't'))
- string_compare = 1;
- }
- peers = args.reqchans;
- if (peers) {
- cur = peers;
- do {
- /* remember where to start next time */
- rest = strchr(cur, '&');
- if (rest) {
- *rest = 0;
- rest++;
- }
- tech = cur;
- number = strchr(tech, '/');
- if (!number) {
- ast_log(LOG_WARNING, "ChanIsAvail argument takes format ([technology]/[device])\n");
- return -1;
- }
- *number = '\0';
- number++;
-
- if (string_compare) {
- /* ast_parse_device_state checks for "SIP/1234" as a channel name.
- ast_device_state will ask the SIP driver for the channel state. */
-
- snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
- status = inuse = ast_parse_device_state(trychan);
- } else if (option_state) {
- /* If the pbx says in use then don't bother trying further.
- This is to permit testing if someone's on a call, even if the
- channel can permit more calls (ie callwaiting, sip calls, etc). */
-
- snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
- status = inuse = ast_device_state(trychan);
- }
- if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status))) {
- pbx_builtin_setvar_helper(chan, "AVAILCHAN", tempchan->name);
- /* Store the originally used channel too */
- snprintf(tmp, sizeof(tmp), "%s/%s", tech, number);
- pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", tmp);
- snprintf(tmp, sizeof(tmp), "%d", status);
- pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
- ast_hangup(tempchan);
- tempchan = NULL;
- res = 1;
- break;
- } else {
- snprintf(tmp, sizeof(tmp), "%d", status);
- pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
- }
- cur = rest;
- } while (cur);
- }
- if (res < 1) {
- pbx_builtin_setvar_helper(chan, "AVAILCHAN", "");
- pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", "");
- }
-
- return 0;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, chanavail_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Check channel availability");
diff --git a/trunk/apps/app_channelredirect.c b/trunk/apps/app_channelredirect.c
deleted file mode 100644
index 325c681ac..000000000
--- a/trunk/apps/app_channelredirect.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2006, Sergey Basmanov
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief ChannelRedirect application
- *
- * \author Sergey Basmanov <sergey_basmanov@mail.ru>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-#include "asterisk/app.h"
-#include "asterisk/features.h"
-
-static char *app = "ChannelRedirect";
-static char *synopsis = "Redirects given channel to a dialplan target.";
-static char *descrip =
-"ChannelRedirect(channel,[[context,]extension,]priority)\n"
-" Sends the specified channel to the specified extension priority\n";
-
-
-static int asyncgoto_exec(struct ast_channel *chan, void *data)
-{
- int res = -1;
- char *info;
- struct ast_channel *chan2 = NULL;
-
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(channel);
- AST_APP_ARG(label);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "%s requires an argument (channel,[[context,]exten,]priority)\n", app);
- return -1;
- }
-
- info = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, info);
-
- if (ast_strlen_zero(args.channel) || ast_strlen_zero(args.label)) {
- ast_log(LOG_WARNING, "%s requires an argument (channel,[[context,]exten,]priority)\n", app);
- goto quit;
- }
-
- chan2 = ast_get_channel_by_name_locked(args.channel);
- if (!chan2) {
- ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
- goto quit;
- }
-
- res = ast_parseable_goto(chan2, args.label);
-
- ast_channel_unlock(chan2);
-quit:
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, asyncgoto_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Redirects a given channel to a dialplan target");
diff --git a/trunk/apps/app_chanspy.c b/trunk/apps/app_chanspy.c
deleted file mode 100644
index a68461baa..000000000
--- a/trunk/apps/app_chanspy.c
+++ /dev/null
@@ -1,733 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
- * Copyright (C) 2005 - 2006, Digium, Inc.
- *
- * A license has been granted to Digium (via disclaimer) for the use of
- * this code.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief ChanSpy: Listen in on any channel.
- *
- * \author Anthony Minessale II <anthmct@yahoo.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <ctype.h>
-
-#include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/audiohook.h"
-#include "asterisk/features.h"
-#include "asterisk/app.h"
-#include "asterisk/utils.h"
-#include "asterisk/say.h"
-#include "asterisk/pbx.h"
-#include "asterisk/translate.h"
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-
-#define AST_NAME_STRLEN 256
-
-static const char *tdesc = "Listen to a channel, and optionally whisper into it";
-static const char *app_chan = "ChanSpy";
-static const char *desc_chan =
-" ChanSpy([chanprefix][,options]): This application is used to listen to the\n"
-"audio from an Asterisk channel. This includes the audio coming in and\n"
-"out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
-"only channels beginning with this string will be spied upon.\n"
-" While spying, the following actions may be performed:\n"
-" - Dialing # cycles the volume level.\n"
-" - Dialing * will stop spying and look for another channel to spy on.\n"
-" - Dialing a series of digits followed by # builds a channel name to append\n"
-" to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
-" the digits '1234#' while spying will begin spying on the channel\n"
-" 'Agent/1234'.\n"
-" Note: The X option supersedes the three features above in that if a valid\n"
-" single digit extension exists in the correct context ChanSpy will\n"
-" exit to it. This also disables choosing a channel based on 'chanprefix'\n"
-" and a digit sequence.\n"
-" Options:\n"
-" b - Only spy on channels involved in a bridged call.\n"
-" g(grp) - Match only channels where their SPYGROUP variable is set to\n"
-" contain 'grp' in an optional : delimited list.\n"
-" q - Don't play a beep when beginning to spy on a channel, or speak the\n"
-" selected channel name.\n"
-" r[(basename)] - Record the session to the monitor spool directory. An\n"
-" optional base for the filename may be specified. The\n"
-" default is 'chanspy'.\n"
-" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
-" negative value refers to a quieter setting.\n"
-" w - Enable 'whisper' mode, so the spying channel can talk to\n"
-" the spied-on channel.\n"
-" W - Enable 'private whisper' mode, so the spying channel can\n"
-" talk to the spied-on channel but cannot listen to that\n"
-" channel.\n"
-" o - Only listen to audio coming from this channel.\n"
-" X - Allow the user to exit ChanSpy to a valid single digit\n"
-" numeric extension in the current context or the context\n"
-" specified by the SPY_EXIT_CONTEXT channel variable. The\n"
-" name of the last channel that was spied on will be stored\n"
-" in the SPY_CHANNEL variable.\n"
-;
-
-static const char *app_ext = "ExtenSpy";
-static const char *desc_ext =
-" ExtenSpy(exten[@context][,options]): This application is used to listen to the\n"
-"audio from an Asterisk channel. This includes the audio coming in and\n"
-"out of the channel being spied on. Only channels created by outgoing calls for the\n"
-"specified extension will be selected for spying. If the optional context is not\n"
-"supplied, the current channel's context will be used.\n"
-" While spying, the following actions may be performed:\n"
-" - Dialing # cycles the volume level.\n"
-" - Dialing * will stop spying and look for another channel to spy on.\n"
-" Note: The X option superseeds the two features above in that if a valid\n"
-" single digit extension exists in the correct context it ChanSpy will\n"
-" exit to it.\n"
-" Options:\n"
-" b - Only spy on channels involved in a bridged call.\n"
-" g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
-" contain 'grp' in an optional : delimited list.\n"
-" q - Don't play a beep when beginning to spy on a channel, or speak the\n"
-" selected channel name.\n"
-" r[(basename)] - Record the session to the monitor spool directory. An\n"
-" optional base for the filename may be specified. The\n"
-" default is 'chanspy'.\n"
-" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
-" negative value refers to a quieter setting.\n"
-" w - Enable 'whisper' mode, so the spying channel can talk to\n"
-" the spied-on channel.\n"
-" W - Enable 'private whisper' mode, so the spying channel can\n"
-" talk to the spied-on channel but cannot listen to that\n"
-" channel.\n"
-" o - Only listen to audio coming from this channel.\n"
-" X - Allow the user to exit ChanSpy to a valid single digit\n"
-" numeric extension in the current context or the context\n"
-" specified by the SPY_EXIT_CONTEXT channel variable. The\n"
-" name of the last channel that was spied on will be stored\n"
-" in the SPY_CHANNEL variable.\n"
-;
-
-enum {
- OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
- OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
- OPTION_VOLUME = (1 << 2), /* Specify initial volume */
- OPTION_GROUP = (1 << 3), /* Only look at channels in group */
- OPTION_RECORD = (1 << 4),
- OPTION_WHISPER = (1 << 5),
- OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
- OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
- OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
-} chanspy_opt_flags;
-
-enum {
- OPT_ARG_VOLUME = 0,
- OPT_ARG_GROUP,
- OPT_ARG_RECORD,
- OPT_ARG_ARRAY_SIZE,
-} chanspy_opt_args;
-
-AST_APP_OPTIONS(spy_opts, {
- AST_APP_OPTION('q', OPTION_QUIET),
- AST_APP_OPTION('b', OPTION_BRIDGED),
- AST_APP_OPTION('w', OPTION_WHISPER),
- AST_APP_OPTION('W', OPTION_PRIVATE),
- AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
- AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
- AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
- AST_APP_OPTION('o', OPTION_READONLY),
- AST_APP_OPTION('X', OPTION_EXIT),
-});
-
-
-struct chanspy_translation_helper {
- /* spy data */
- struct ast_audiohook spy_audiohook;
- struct ast_audiohook whisper_audiohook;
- int fd;
- int volfactor;
-};
-
-static void *spy_alloc(struct ast_channel *chan, void *data)
-{
- /* just store the data pointer in the channel structure */
- return data;
-}
-
-static void spy_release(struct ast_channel *chan, void *data)
-{
- /* nothing to do */
-}
-
-static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
-{
- struct chanspy_translation_helper *csth = data;
- struct ast_frame *f = NULL;
-
- ast_audiohook_lock(&csth->spy_audiohook);
- if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
- /* Channel is already gone more than likely */
- ast_audiohook_unlock(&csth->spy_audiohook);
- return -1;
- }
-
- f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
-
- ast_audiohook_unlock(&csth->spy_audiohook);
-
- if (!f)
- return 0;
-
- if (ast_write(chan, f)) {
- ast_frfree(f);
- return -1;
- }
-
- if (csth->fd)
- write(csth->fd, f->data, f->datalen);
-
- ast_frfree(f);
-
- return 0;
-}
-
-static struct ast_generator spygen = {
- .alloc = spy_alloc,
- .release = spy_release,
- .generate = spy_generate,
-};
-
-static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_audiohook *audiohook)
-{
- int res = 0;
- struct ast_channel *peer = NULL;
-
- ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
-
- res = ast_audiohook_attach(chan, audiohook);
-
- if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
- ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
-
- return res;
-}
-
-static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd,
- const struct ast_flags *flags, char *exitcontext)
-{
- struct chanspy_translation_helper csth;
- int running = 0, res, x = 0;
- char inp[24] = {0};
- char *name;
- struct ast_frame *f;
- struct ast_silence_generator *silgen = NULL;
-
- if (ast_check_hangup(chan) || ast_check_hangup(spyee))
- return 0;
-
- name = ast_strdupa(spyee->name);
- ast_verb(2, "Spying on channel %s\n", name);
-
- memset(&csth, 0, sizeof(csth));
-
- ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
-
- if (start_spying(spyee, chan, &csth.spy_audiohook)) {
- ast_audiohook_destroy(&csth.spy_audiohook);
- return 0;
- }
-
- if (ast_test_flag(flags, OPTION_WHISPER)) {
- ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
- start_spying(spyee, chan, &csth.whisper_audiohook);
- }
-
- csth.volfactor = *volfactor;
-
- if (csth.volfactor) {
- csth.spy_audiohook.options.read_volume = csth.volfactor;
- csth.spy_audiohook.options.write_volume = csth.volfactor;
- }
-
- csth.fd = fd;
-
- if (ast_test_flag(flags, OPTION_PRIVATE))
- silgen = ast_channel_start_silence_generator(chan);
- else
- ast_activate_generator(chan, &spygen, &csth);
-
- /* We can no longer rely on 'spyee' being an actual channel;
- it can be hung up and freed out from under us. However, the
- channel destructor will put NULL into our csth.spy.chan
- field when that happens, so that is our signal that the spyee
- channel has gone away.
- */
-
- /* Note: it is very important that the ast_waitfor() be the first
- condition in this expression, so that if we wait for some period
- of time before receiving a frame from our spying channel, we check
- for hangup on the spied-on channel _after_ knowing that a frame
- has arrived, since the spied-on channel could have gone away while
- we were waiting
- */
- while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
- if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
- running = -1;
- break;
- }
-
- if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
- ast_audiohook_lock(&csth.whisper_audiohook);
- ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
- ast_audiohook_unlock(&csth.whisper_audiohook);
- ast_frfree(f);
- continue;
- }
-
- res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
- ast_frfree(f);
- if (!res)
- continue;
-
- if (x == sizeof(inp))
- x = 0;
-
- if (res < 0) {
- running = -1;
- break;
- }
-
- if (ast_test_flag(flags, OPTION_EXIT)) {
- char tmp[2];
- tmp[0] = res;
- tmp[1] = '\0';
- if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
- ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
- pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
- running = -2;
- break;
- } else {
- ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
- }
- } else if (res >= '0' && res <= '9') {
- inp[x++] = res;
- }
-
- if (res == '*') {
- running = 0;
- break;
- } else if (res == '#') {
- if (!ast_strlen_zero(inp)) {
- running = atoi(inp);
- break;
- }
-
- (*volfactor)++;
- if (*volfactor > 4)
- *volfactor = -4;
- ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
-
- csth.volfactor = *volfactor;
- csth.spy_audiohook.options.read_volume = csth.volfactor;
- csth.spy_audiohook.options.write_volume = csth.volfactor;
- }
- }
-
- if (ast_test_flag(flags, OPTION_PRIVATE))
- ast_channel_stop_silence_generator(chan, silgen);
- else
- ast_deactivate_generator(chan);
-
- if (ast_test_flag(flags, OPTION_WHISPER)) {
- ast_audiohook_lock(&csth.whisper_audiohook);
- ast_audiohook_detach(&csth.whisper_audiohook);
- ast_audiohook_unlock(&csth.whisper_audiohook);
- ast_audiohook_destroy(&csth.whisper_audiohook);
- }
-
- ast_audiohook_lock(&csth.spy_audiohook);
- ast_audiohook_detach(&csth.spy_audiohook);
- ast_audiohook_unlock(&csth.spy_audiohook);
- ast_audiohook_destroy(&csth.spy_audiohook);
-
- ast_verb(2, "Done Spying on channel %s\n", name);
-
- return running;
-}
-
-static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
- const char *exten, const char *context)
-{
- struct ast_channel *this;
-
- redo:
- if (spec)
- this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
- else if (exten)
- this = ast_walk_channel_by_exten_locked(last, exten, context);
- else
- this = ast_channel_walk_locked(last);
-
- if (this) {
- ast_channel_unlock(this);
- if (!strncmp(this->name, "Zap/pseudo", 10))
- goto redo;
- }
-
- return this;
-}
-
-static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
- int volfactor, const int fd, const char *mygroup, const char *spec,
- const char *exten, const char *context)
-{
- struct ast_channel *peer, *prev, *next;
- char nameprefix[AST_NAME_STRLEN];
- char peer_name[AST_NAME_STRLEN + 5];
- char exitcontext[AST_MAX_CONTEXT] = "";
- signed char zero_volume = 0;
- int waitms;
- int res;
- char *ptr;
- int num;
- int num_spyed_upon = 1;
-
- if (ast_test_flag(flags, OPTION_EXIT)) {
- const char *c;
- if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT")))
- ast_copy_string(exitcontext, c, sizeof(exitcontext));
- else if (!ast_strlen_zero(chan->macrocontext))
- ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
- else
- ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
- }
-
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
-
- waitms = 100;
-
- for (;;) {
- if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
- res = ast_streamfile(chan, "beep", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
- else if (res < 0) {
- ast_clear_flag(chan, AST_FLAG_SPYING);
- break;
- }
- if (!ast_strlen_zero(exitcontext)) {
- char tmp[2];
- tmp[0] = res;
- tmp[1] = '\0';
- if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
- goto exit;
- else
- ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
- }
- }
-
- res = ast_waitfordigit(chan, waitms);
- if (res < 0) {
- ast_clear_flag(chan, AST_FLAG_SPYING);
- break;
- }
- if (!ast_strlen_zero(exitcontext)) {
- char tmp[2];
- tmp[0] = res;
- tmp[1] = '\0';
- if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
- goto exit;
- else
- ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
- }
-
- /* reset for the next loop around, unless overridden later */
- waitms = 100;
- peer = prev = next = NULL;
- num_spyed_upon = 0;
-
- for (peer = next_channel(peer, spec, exten, context);
- peer;
- prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
- const char *group;
- int igrp = !mygroup;
- char *groups[25];
- int num_groups = 0;
- char *dup_group;
- int x;
- char *s;
-
- if (peer == prev)
- break;
-
- if (peer == chan)
- continue;
-
- if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
- continue;
-
- if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
- continue;
-
- if (mygroup) {
- if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
- dup_group = ast_strdupa(group);
- num_groups = ast_app_separate_args(dup_group, ':', groups,
- sizeof(groups) / sizeof(groups[0]));
- }
-
- for (x = 0; x < num_groups; x++) {
- if (!strcmp(mygroup, groups[x])) {
- igrp = 1;
- break;
- }
- }
- }
-
- if (!igrp)
- continue;
-
- strcpy(peer_name, "spy-");
- strncat(peer_name, peer->name, AST_NAME_STRLEN);
- ptr = strchr(peer_name, '/');
- *ptr++ = '\0';
-
- for (s = peer_name; s < ptr; s++)
- *s = tolower(*s);
-
- if (!ast_test_flag(flags, OPTION_QUIET)) {
- if (ast_fileexists(peer_name, NULL, NULL) != -1) {
- res = ast_streamfile(chan, peer_name, chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
- if (res)
- break;
- } else
- res = ast_say_character_str(chan, peer_name, "", chan->language);
- if ((num = atoi(ptr)))
- ast_say_digits(chan, atoi(ptr), "", chan->language);
- }
-
- waitms = 5000;
- res = channel_spy(chan, peer, &volfactor, fd, flags, exitcontext);
- num_spyed_upon++;
-
- if (res == -1) {
- goto exit;
- } else if (res == -2) {
- res = 0;
- goto exit;
- } else if (res > 1 && spec) {
- snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
- if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
- ast_channel_unlock(next);
- } else {
- /* stay on this channel */
- next = peer;
- }
- peer = NULL;
- }
- }
- }
-exit:
-
- ast_clear_flag(chan, AST_FLAG_SPYING);
-
- ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
-
- return res;
-}
-
-static int chanspy_exec(struct ast_channel *chan, void *data)
-{
- char *mygroup = NULL;
- char *recbase = NULL;
- int fd = 0;
- struct ast_flags flags;
- int oldwf = 0;
- int volfactor = 0;
- int res;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(spec);
- AST_APP_ARG(options);
- );
- char *opts[OPT_ARG_ARRAY_SIZE];
-
- data = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, data);
-
- if (args.spec && !strcmp(args.spec, "all"))
- args.spec = NULL;
-
- if (args.options) {
- ast_app_parse_options(spy_opts, &flags, opts, args.options);
- if (ast_test_flag(&flags, OPTION_GROUP))
- mygroup = opts[OPT_ARG_GROUP];
-
- if (ast_test_flag(&flags, OPTION_RECORD) &&
- !(recbase = opts[OPT_ARG_RECORD]))
- recbase = "chanspy";
-
- if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
- int vol;
-
- if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
- ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
- else
- volfactor = vol;
- }
-
- if (ast_test_flag(&flags, OPTION_PRIVATE))
- ast_set_flag(&flags, OPTION_WHISPER);
- } else
- ast_clear_flag(&flags, AST_FLAGS_ALL);
-
- oldwf = chan->writeformat;
- if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
- ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
- return -1;
- }
-
- if (recbase) {
- char filename[512];
-
- snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
- if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
- ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
- fd = 0;
- }
- }
-
- res = common_exec(chan, &flags, volfactor, fd, mygroup, args.spec, NULL, NULL);
-
- if (fd)
- close(fd);
-
- if (oldwf && ast_set_write_format(chan, oldwf) < 0)
- ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
-
- return res;
-}
-
-static int extenspy_exec(struct ast_channel *chan, void *data)
-{
- char *ptr, *exten = NULL;
- char *mygroup = NULL;
- char *recbase = NULL;
- int fd = 0;
- struct ast_flags flags;
- int oldwf = 0;
- int volfactor = 0;
- int res;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(context);
- AST_APP_ARG(options);
- );
-
- data = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, data);
- if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
- exten = args.context;
- *ptr++ = '\0';
- args.context = ptr;
- }
-
- if (ast_strlen_zero(args.context))
- args.context = ast_strdupa(chan->context);
-
- if (args.options) {
- char *opts[OPT_ARG_ARRAY_SIZE];
-
- ast_app_parse_options(spy_opts, &flags, opts, args.options);
- if (ast_test_flag(&flags, OPTION_GROUP))
- mygroup = opts[OPT_ARG_GROUP];
-
- if (ast_test_flag(&flags, OPTION_RECORD) &&
- !(recbase = opts[OPT_ARG_RECORD]))
- recbase = "chanspy";
-
- if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
- int vol;
-
- if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
- ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
- else
- volfactor = vol;
- }
-
- if (ast_test_flag(&flags, OPTION_PRIVATE))
- ast_set_flag(&flags, OPTION_WHISPER);
- } else
- ast_clear_flag(&flags, AST_FLAGS_ALL);
-
- oldwf = chan->writeformat;
- if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
- ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
- return -1;
- }
-
- if (recbase) {
- char filename[512];
-
- snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
- if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
- ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
- fd = 0;
- }
- }
-
- res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, args.context);
-
- if (fd)
- close(fd);
-
- if (oldwf && ast_set_write_format(chan, oldwf) < 0)
- ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
-
- return res;
-}
-
-static int unload_module(void)
-{
- int res = 0;
-
- res |= ast_unregister_application(app_chan);
- res |= ast_unregister_application(app_ext);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res = 0;
-
- res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
- res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");
diff --git a/trunk/apps/app_controlplayback.c b/trunk/apps/app_controlplayback.c
deleted file mode 100644
index b6dfd898e..000000000
--- a/trunk/apps/app_controlplayback.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Trivial application to control playback of a sound file
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/pbx.h"
-#include "asterisk/app.h"
-#include "asterisk/module.h"
-
-static const char *app = "ControlPlayback";
-
-static const char *synopsis = "Play a file with fast forward and rewind";
-
-static const char *descrip =
-" ControlPlayback(file[,skipms[,ff[,rew[,stop[,pause[,restart,options]]]]]]]):\n"
-"This application will play back the given filename. By default, the '*' key\n"
-"can be used to rewind, and the '#' key can be used to fast-forward.\n"
-"Parameters:\n"
-" skipms - This is number of milliseconds to skip when rewinding or\n"
-" fast-forwarding.\n"
-" ff - Fast-forward when this DTMF digit is received.\n"
-" rew - Rewind when this DTMF digit is received.\n"
-" stop - Stop playback when this DTMF digit is received.\n"
-" pause - Pause playback when this DTMF digit is received.\n"
-" restart - Restart playback when this DTMF digit is received.\n"
-"Options:\n"
-" o(#) - Start at # ms from the beginning of the file.\n"
-"This application sets the following channel variables upon completion:\n"
-" CPLAYBACKSTATUS - This variable contains the status of the attempt as a text\n"
-" string, one of: SUCCESS | USERSTOPPED | ERROR\n"
-" CPLAYBACKOFFSET - This contains the offset in ms into the file where\n"
-" playback was at when it stopped. -1 is end of file.\n"
-" CPLAYBACKSTOPKEY - If the playback is stopped by the user this variable contains\n"
-" the key that was pressed.\n";
-
-enum {
- OPT_OFFSET = (1 << 1),
-};
-
-enum {
- OPT_ARG_OFFSET = 0,
- /* must stay as the last entry ... */
- OPT_ARG_ARRAY_LEN,
-};
-
-AST_APP_OPTIONS(cpb_opts, BEGIN_OPTIONS
- AST_APP_OPTION_ARG('o', OPT_OFFSET, OPT_ARG_OFFSET),
-END_OPTIONS );
-
-static int is_on_phonepad(char key)
-{
- return key == 35 || key == 42 || (key >= 48 && key <= 57);
-}
-
-static int controlplayback_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- int skipms = 0;
- long offsetms = 0;
- char offsetbuf[20];
- char stopkeybuf[2];
- char *tmp;
- struct ast_flags opts = { 0, };
- char *opt_args[OPT_ARG_ARRAY_LEN];
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(filename);
- AST_APP_ARG(skip);
- AST_APP_ARG(fwd);
- AST_APP_ARG(rev);
- AST_APP_ARG(stop);
- AST_APP_ARG(pause);
- AST_APP_ARG(restart);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
- return -1;
- }
-
- tmp = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, tmp);
-
- if (args.argc < 1) {
- ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
- return -1;
- }
-
- skipms = args.skip ? (atoi(args.skip) ? atoi(args.skip) : 3000) : 3000;
-
- if (!args.fwd || !is_on_phonepad(*args.fwd))
- args.fwd = "#";
- if (!args.rev || !is_on_phonepad(*args.rev))
- args.rev = "*";
- if (args.stop && !is_on_phonepad(*args.stop))
- args.stop = NULL;
- if (args.pause && !is_on_phonepad(*args.pause))
- args.pause = NULL;
- if (args.restart && !is_on_phonepad(*args.restart))
- args.restart = NULL;
-
- if (args.options) {
- ast_app_parse_options(cpb_opts, &opts, opt_args, args.options);
- if (ast_test_flag(&opts, OPT_OFFSET))
- offsetms = atol(opt_args[OPT_ARG_OFFSET]);
- }
-
- res = ast_control_streamfile(chan, args.filename, args.fwd, args.rev, args.stop, args.pause, args.restart, skipms, &offsetms);
-
- /* If we stopped on one of our stop keys, return 0 */
- if (res > 0 && args.stop && strchr(args.stop, res)) {
- pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED");
- snprintf(stopkeybuf, sizeof(stopkeybuf), "%c", res);
- pbx_builtin_setvar_helper(chan, "CPLAYBACKSTOPKEY", stopkeybuf);
- res = 0;
- } else {
- if (res < 0) {
- res = 0;
- pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR");
- } else
- pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS");
- }
-
- snprintf(offsetbuf, sizeof(offsetbuf), "%ld", offsetms);
- pbx_builtin_setvar_helper(chan, "CPLAYBACKOFFSET", offsetbuf);
-
- return res;
-}
-
-static int unload_module(void)
-{
- int res;
- res = ast_unregister_application(app);
- return res;
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, controlplayback_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Control Playback Application");
diff --git a/trunk/apps/app_db.c b/trunk/apps/app_db.c
deleted file mode 100644
index 0cc5043ce..000000000
--- a/trunk/apps/app_db.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- * Copyright (C) 2003, Jefferson Noxon
- *
- * Mark Spencer <markster@digium.com>
- * Jefferson Noxon <jeff@debian.org>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Database access functions
- *
- * \author Mark Spencer <markster@digium.com>
- * \author Jefferson Noxon <jeff@debian.org>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/astdb.h"
-#include "asterisk/lock.h"
-
-/*! \todo XXX Remove this application after 1.4 is relased */
-static char *d_descrip =
-" DBdel(family/key): This application will delete a key from the Asterisk\n"
-"database.\n"
-" This application has been DEPRECATED in favor of the DB_DELETE function.\n";
-
-static char *dt_descrip =
-" DBdeltree(family[/keytree]): This application will delete a family or keytree\n"
-"from the Asterisk database\n";
-
-static char *d_app = "DBdel";
-static char *dt_app = "DBdeltree";
-
-static char *d_synopsis = "Delete a key from the database";
-static char *dt_synopsis = "Delete a family or keytree from the database";
-
-
-static int deltree_exec(struct ast_channel *chan, void *data)
-{
- char *argv, *family, *keytree;
-
- argv = ast_strdupa(data);
-
- if (strchr(argv, '/')) {
- family = strsep(&argv, "/");
- keytree = strsep(&argv, "\0");
- if (!family || !keytree) {
- ast_debug(1, "Ignoring; Syntax error in argument\n");
- return 0;
- }
- if (ast_strlen_zero(keytree))
- keytree = 0;
- } else {
- family = argv;
- keytree = 0;
- }
-
- if (keytree)
- ast_verb(3, "DBdeltree: family=%s, keytree=%s\n", family, keytree);
- else
- ast_verb(3, "DBdeltree: family=%s\n", family);
-
- if (ast_db_deltree(family, keytree))
- ast_verb(3, "DBdeltree: Error deleting key from database.\n");
-
- return 0;
-}
-
-static int del_exec(struct ast_channel *chan, void *data)
-{
- char *argv, *family, *key;
- static int deprecation_warning = 0;
-
- if (!deprecation_warning) {
- deprecation_warning = 1;
- ast_log(LOG_WARNING, "The DBdel application has been deprecated in favor of the DB_DELETE dialplan function!\n");
- }
-
- argv = ast_strdupa(data);
-
- if (strchr(argv, '/')) {
- family = strsep(&argv, "/");
- key = strsep(&argv, "\0");
- if (!family || !key) {
- ast_debug(1, "Ignoring; Syntax error in argument\n");
- return 0;
- }
- ast_verb(3, "DBdel: family=%s, key=%s\n", family, key);
- if (ast_db_del(family, key))
- ast_verb(3, "DBdel: Error deleting key from database.\n");
- } else {
- ast_debug(1, "Ignoring, no parameters\n");
- }
-
- return 0;
-}
-
-static int unload_module(void)
-{
- int retval;
-
- retval = ast_unregister_application(dt_app);
- retval |= ast_unregister_application(d_app);
-
- return retval;
-}
-
-static int load_module(void)
-{
- int retval;
-
- retval = ast_register_application(d_app, del_exec, d_synopsis, d_descrip);
- retval |= ast_register_application(dt_app, deltree_exec, dt_synopsis, dt_descrip);
-
- return retval;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Database Access Functions");
diff --git a/trunk/apps/app_dial.c b/trunk/apps/app_dial.c
deleted file mode 100644
index 39886a11b..000000000
--- a/trunk/apps/app_dial.c
+++ /dev/null
@@ -1,2052 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2006, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief dial() & retrydial() - Trivial application to dial a channel and send an URL on answer
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>chan_local</depend>
- ***/
-
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <sys/time.h>
-#include <sys/signal.h>
-#include <sys/stat.h>
-#include <netinet/in.h>
-
-#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/say.h"
-#include "asterisk/config.h"
-#include "asterisk/features.h"
-#include "asterisk/musiconhold.h"
-#include "asterisk/callerid.h"
-#include "asterisk/utils.h"
-#include "asterisk/app.h"
-#include "asterisk/causes.h"
-#include "asterisk/rtp.h"
-#include "asterisk/cdr.h"
-#include "asterisk/manager.h"
-#include "asterisk/privacy.h"
-#include "asterisk/stringfields.h"
-#include "asterisk/global_datastores.h"
-
-static char *app = "Dial";
-
-static char *synopsis = "Place a call and connect to the current channel";
-
-static char *descrip =
-" Dial(Technology/resource[&Tech2/resource2...][,timeout][,options][,URL]):\n"
-"This application will place calls to one or more specified channels. As soon\n"
-"as one of the requested channels answers, the originating channel will be\n"
-"answered, if it has not already been answered. These two channels will then\n"
-"be active in a bridged call. All other channels that were requested will then\n"
-"be hung up.\n"
-" Unless there is a timeout specified, the Dial application will wait\n"
-"indefinitely until one of the called channels answers, the user hangs up, or\n"
-"if all of the called channels are busy or unavailable. Dialplan executing will\n"
-"continue if no requested channels can be called, or if the timeout expires.\n\n"
-" This application sets the following channel variables upon completion:\n"
-" DIALEDTIME - This is the time from dialing a channel until when it\n"
-" is disconnected.\n"
-" ANSWEREDTIME - This is the amount of time for actual call.\n"
-" DIALSTATUS - This is the status of the call:\n"
-" CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL\n"
-" DONTCALL | TORTURE | INVALIDARGS\n"
-" For the Privacy and Screening Modes, the DIALSTATUS variable will be set to\n"
-"DONTCALL if the called party chooses to send the calling party to the 'Go Away'\n"
-"script. The DIALSTATUS variable will be set to TORTURE if the called party\n"
-"wants to send the caller to the 'torture' script.\n"
-" This application will report normal termination if the originating channel\n"
-"hangs up, or if the call is bridged and either of the parties in the bridge\n"
-"ends the call.\n"
-" The optional URL will be sent to the called party if the channel supports it.\n"
-" If the OUTBOUND_GROUP variable is set, all peer channels created by this\n"
-"application will be put into that group (as in Set(GROUP()=...).\n"
-" If the OUTBOUND_GROUP_ONCE variable is set, all peer channels created by this\n"
-"application will be put into that group (as in Set(GROUP()=...). Unlike OUTBOUND_GROUP,\n"
-"however, the variable will be unset after use.\n\n"
-" Options:\n"
-" A(x) - Play an announcement to the called party, using 'x' as the file.\n"
-" C - Reset the CDR for this call.\n"
-" c - If DIAL cancels this call, always set the flag to tell the channel\n"
-" driver that the call is answered elsewhere.\n"
-" d - Allow the calling user to dial a 1 digit extension while waiting for\n"
-" a call to be answered. Exit to that extension if it exists in the\n"
-" current context, or the context defined in the EXITCONTEXT variable,\n"
-" if it exists.\n"
-" D([called][:calling]) - Send the specified DTMF strings *after* the called\n"
-" party has answered, but before the call gets bridged. The 'called'\n"
-" DTMF string is sent to the called party, and the 'calling' DTMF\n"
-" string is sent to the calling party. Both parameters can be used\n"
-" alone.\n"
-" e - execute the 'h' extension for peer after the call ends\n"
-" f - Force the callerid of the *calling* channel to be set as the\n"
-" extension associated with the channel using a dialplan 'hint'.\n"
-" For example, some PSTNs do not allow CallerID to be set to anything\n"
-" other than the number assigned to the caller.\n"
-" g - Proceed with dialplan execution at the current extension if the\n"
-" destination channel hangs up.\n"
-" G(context^exten^pri) - If the call is answered, transfer the calling party to\n"
-" the specified priority and the called party to the specified priority+1.\n"
-" Optionally, an extension, or extension and context may be specified. \n"
-" Otherwise, the current extension is used. You cannot use any additional\n"
-" action post answer options in conjunction with this option.\n"
-" h - Allow the called party to hang up by sending the '*' DTMF digit.\n"
-" H - Allow the calling party to hang up by hitting the '*' DTMF digit.\n"
-" i - Asterisk will ignore any forwarding requests it may receive on this\n"
-" dial attempt.\n"
-" k - Allow the called party to enable parking of the call by sending\n"
-" the DTMF sequence defined for call parking in features.conf.\n"
-" K - Allow the calling party to enable parking of the call by sending\n"
-" the DTMF sequence defined for call parking in features.conf.\n"
-" L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are\n"
-" left. Repeat the warning every 'z' ms. The following special\n"
-" variables can be used with this option:\n"
-" * LIMIT_PLAYAUDIO_CALLER yes|no (default yes)\n"
-" Play sounds to the caller.\n"
-" * LIMIT_PLAYAUDIO_CALLEE yes|no\n"
-" Play sounds to the callee.\n"
-" * LIMIT_TIMEOUT_FILE File to play when time is up.\n"
-" * LIMIT_CONNECT_FILE File to play when call begins.\n"
-" * LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
-" The default is to say the time remaining.\n"
-" m([class]) - Provide hold music to the calling party until a requested\n"
-" channel answers. A specific MusicOnHold class can be\n"
-" specified.\n"
-" M(x[^arg]) - Execute the Macro for the *called* channel before connecting\n"
-" to the calling channel. Arguments can be specified to the Macro\n"
-" using '^' as a delimeter. The Macro can set the variable\n"
-" MACRO_RESULT to specify the following actions after the Macro is\n"
-" finished executing.\n"
-" * ABORT Hangup both legs of the call.\n"
-" * CONGESTION Behave as if line congestion was encountered.\n"
-" * BUSY Behave as if a busy signal was encountered.\n"
-" * CONTINUE Hangup the called party and allow the calling party\n"
-" to continue dialplan execution at the next priority.\n"
-" * GOTO:<context>^<exten>^<priority> - Transfer the call to the\n"
-" specified priority. Optionally, an extension, or\n"
-" extension and priority can be specified.\n"
-" You cannot use any additional action post answer options in conjunction\n"
-" with this option. Also, pbx services are not run on the peer (called) channel,\n"
-" so you will not be able to set timeouts via the TIMEOUT() function in this macro.\n"
-" n - This option is a modifier for the screen/privacy mode. It specifies\n"
-" that no introductions are to be saved in the priv-callerintros\n"
-" directory.\n"
-" N - This option is a modifier for the screen/privacy mode. It specifies\n"
-" that if callerID is present, do not screen the call.\n"
-" o - Specify that the CallerID that was present on the *calling* channel\n"
-" be set as the CallerID on the *called* channel. This was the\n"
-" behavior of Asterisk 1.0 and earlier.\n"
-" O([x]) - \"Operator Services\" mode (Zaptel channel to Zaptel channel\n"
-" only, if specified on non-Zaptel interface, it will be ignored).\n"
-" When the destination answers (presumably an operator services\n"
-" station), the originator no longer has control of their line.\n"
-" They may hang up, but the switch will not release their line\n"
-" until the destination party hangs up (the operator). Specified\n"
-" without an arg, or with 1 as an arg, the originator hanging up\n"
-" will cause the phone to ring back immediately. With a 2 specified,\n"
-" when the \"operator\" flashes the trunk, it will ring their phone\n"
-" back.\n"
-" p - This option enables screening mode. This is basically Privacy mode\n"
-" without memory.\n"
-" P([x]) - Enable privacy mode. Use 'x' as the family/key in the database if\n"
-" it is provided. The current extension is used if a database\n"
-" family/key is not specified.\n"
-" r - Indicate ringing to the calling party. Pass no audio to the calling\n"
-" party until the called channel has answered.\n"
-" S(x) - Hang up the call after 'x' seconds *after* the called party has\n"
-" answered the call.\n"
-" t - Allow the called party to transfer the calling party by sending the\n"
-" DTMF sequence defined in features.conf.\n"
-" T - Allow the calling party to transfer the called party by sending the\n"
-" DTMF sequence defined in features.conf.\n"
-" U(x[^arg]) - Execute via Gosub the routine 'x' for the *called* channel before connecting\n"
-" to the calling channel. Arguments can be specified to the Gosub\n"
-" using '^' as a delimeter. The Gosub routine can set the variable\n"
-" GOSUB_RESULT to specify the following actions after the Gosub returns.\n"
-" * ABORT Hangup both legs of the call.\n"
-" * CONGESTION Behave as if line congestion was encountered.\n"
-" * BUSY Behave as if a busy signal was encountered.\n"
-" * CONTINUE Hangup the called party and allow the calling party\n"
-" to continue dialplan execution at the next priority.\n"
-" * GOTO:<context>^<exten>^<priority> - Transfer the call to the\n"
-" specified priority. Optionally, an extension, or\n"
-" extension and priority can be specified.\n"
-" You cannot use any additional action post answer options in conjunction\n"
-" with this option. Also, pbx services are not run on the peer (called) channel,\n"
-" so you will not be able to set timeouts via the TIMEOUT() function in this routine.\n"
-" w - Allow the called party to enable recording of the call by sending\n"
-" the DTMF sequence defined for one-touch recording in features.conf.\n"
-" W - Allow the calling party to enable recording of the call by sending\n"
-" the DTMF sequence defined for one-touch recording in features.conf.\n"
-" x - Allow the called party to enable recording of the call by sending\n"
-" the DTMF sequence defined for one-touch automixmonitor in features.conf\n"
-" X - Allow the calling party to enable recording of the call by sending\n"
-" the DTMF sequence defined for one-touch automixmonitor in features.conf\n";
-
-/* RetryDial App by Anthony Minessale II <anthmct@yahoo.com> Jan/2005 */
-static char *rapp = "RetryDial";
-static char *rsynopsis = "Place a call, retrying on failure allowing optional exit extension.";
-static char *rdescrip =
-" RetryDial(announce,sleep,retries,dialargs): This application will attempt to\n"
-"place a call using the normal Dial application. If no channel can be reached,\n"
-"the 'announce' file will be played. Then, it will wait 'sleep' number of\n"
-"seconds before retying the call. After 'retires' number of attempts, the\n"
-"calling channel will continue at the next priority in the dialplan. If the\n"
-"'retries' setting is set to 0, this application will retry endlessly.\n"
-" While waiting to retry a call, a 1 digit extension may be dialed. If that\n"
-"extension exists in either the context defined in ${EXITCONTEXT} or the current\n"
-"one, The call will jump to that extension immediately.\n"
-" The 'dialargs' are specified in the same format that arguments are provided\n"
-"to the Dial application.\n";
-
-enum {
- OPT_ANNOUNCE = (1 << 0),
- OPT_RESETCDR = (1 << 1),
- OPT_DTMF_EXIT = (1 << 2),
- OPT_SENDDTMF = (1 << 3),
- OPT_FORCECLID = (1 << 4),
- OPT_GO_ON = (1 << 5),
- OPT_CALLEE_HANGUP = (1 << 6),
- OPT_CALLER_HANGUP = (1 << 7),
- OPT_DURATION_LIMIT = (1 << 9),
- OPT_MUSICBACK = (1 << 10),
- OPT_CALLEE_MACRO = (1 << 11),
- OPT_SCREEN_NOINTRO = (1 << 12),
- OPT_SCREEN_NOCLID = (1 << 13),
- OPT_ORIGINAL_CLID = (1 << 14),
- OPT_SCREENING = (1 << 15),
- OPT_PRIVACY = (1 << 16),
- OPT_RINGBACK = (1 << 17),
- OPT_DURATION_STOP = (1 << 18),
- OPT_CALLEE_TRANSFER = (1 << 19),
- OPT_CALLER_TRANSFER = (1 << 20),
- OPT_CALLEE_MONITOR = (1 << 21),
- OPT_CALLER_MONITOR = (1 << 22),
- OPT_GOTO = (1 << 23),
- OPT_OPERMODE = (1 << 24),
- OPT_CALLEE_PARK = (1 << 25),
- OPT_CALLER_PARK = (1 << 26),
- OPT_IGNORE_FORWARDING = (1 << 27),
- OPT_CALLEE_GOSUB = (1 << 28),
- OPT_CALLEE_MIXMONITOR = (1 << 29),
- OPT_CALLER_MIXMONITOR = (1 << 30),
-};
-
-#define DIAL_STILLGOING (1 << 31)
-#define DIAL_NOFORWARDHTML ((uint64_t)1 << 32) /* flags are now 64 bits, so keep it up! */
-#define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 33)
-#define OPT_PEER_H ((uint64_t)1 << 34)
-
-enum {
- OPT_ARG_ANNOUNCE = 0,
- OPT_ARG_SENDDTMF,
- OPT_ARG_GOTO,
- OPT_ARG_DURATION_LIMIT,
- OPT_ARG_MUSICBACK,
- OPT_ARG_CALLEE_MACRO,
- OPT_ARG_CALLEE_GOSUB,
- OPT_ARG_PRIVACY,
- OPT_ARG_DURATION_STOP,
- OPT_ARG_OPERMODE,
- /* note: this entry _MUST_ be the last one in the enum */
- OPT_ARG_ARRAY_SIZE,
-};
-
-AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
- AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
- AST_APP_OPTION('C', OPT_RESETCDR),
- AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE),
- AST_APP_OPTION('d', OPT_DTMF_EXIT),
- AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),
- AST_APP_OPTION('e', OPT_PEER_H),
- AST_APP_OPTION('f', OPT_FORCECLID),
- AST_APP_OPTION('g', OPT_GO_ON),
- AST_APP_OPTION_ARG('G', OPT_GOTO, OPT_ARG_GOTO),
- AST_APP_OPTION('h', OPT_CALLEE_HANGUP),
- AST_APP_OPTION('H', OPT_CALLER_HANGUP),
- AST_APP_OPTION('i', OPT_IGNORE_FORWARDING),
- AST_APP_OPTION('k', OPT_CALLEE_PARK),
- AST_APP_OPTION('K', OPT_CALLER_PARK),
- AST_APP_OPTION('k', OPT_CALLEE_PARK),
- AST_APP_OPTION('K', OPT_CALLER_PARK),
- AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
- AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK),
- AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO),
- AST_APP_OPTION('n', OPT_SCREEN_NOINTRO),
- AST_APP_OPTION('N', OPT_SCREEN_NOCLID),
- AST_APP_OPTION('o', OPT_ORIGINAL_CLID),
- AST_APP_OPTION_ARG('O', OPT_OPERMODE, OPT_ARG_OPERMODE),
- AST_APP_OPTION('p', OPT_SCREENING),
- AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY),
- AST_APP_OPTION('r', OPT_RINGBACK),
- AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
- AST_APP_OPTION('t', OPT_CALLEE_TRANSFER),
- AST_APP_OPTION('T', OPT_CALLER_TRANSFER),
- AST_APP_OPTION_ARG('U', OPT_CALLEE_GOSUB, OPT_ARG_CALLEE_GOSUB),
- AST_APP_OPTION('w', OPT_CALLEE_MONITOR),
- AST_APP_OPTION('W', OPT_CALLER_MONITOR),
- AST_APP_OPTION('x', OPT_CALLEE_MIXMONITOR),
- AST_APP_OPTION('X', OPT_CALLER_MIXMONITOR),
-END_OPTIONS );
-
-#define CAN_EARLY_BRIDGE(flags) (!ast_test_flag64(flags, OPT_CALLEE_HANGUP | \
- OPT_CALLER_HANGUP | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | \
- OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | OPT_CALLER_PARK))
-
-/*
- * The list of active channels
- */
-struct chanlist {
- struct chanlist *next;
- struct ast_channel *chan;
- uint64_t flags;
-};
-
-
-static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere)
-{
- /* Hang up a tree of stuff */
- struct chanlist *oo;
- while (outgoing) {
- /* Hangup any existing lines we have open */
- if (outgoing->chan && (outgoing->chan != exception)) {
- if (answered_elsewhere)
- ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
- ast_hangup(outgoing->chan);
- }
- oo = outgoing;
- outgoing = outgoing->next;
- ast_free(oo);
- }
-}
-
-#define AST_MAX_WATCHERS 256
-
-/*
- * argument to handle_cause() and other functions.
- */
-struct cause_args {
- struct ast_channel *chan;
- int busy;
- int congestion;
- int nochan;
-};
-
-static void handle_cause(int cause, struct cause_args *num)
-{
- struct ast_cdr *cdr = num->chan->cdr;
-
- switch(cause) {
- case AST_CAUSE_BUSY:
- if (cdr)
- ast_cdr_busy(cdr);
- num->busy++;
- break;
-
- case AST_CAUSE_CONGESTION:
- if (cdr)
- ast_cdr_failed(cdr);
- num->congestion++;
- break;
-
- case AST_CAUSE_UNREGISTERED:
- if (cdr)
- ast_cdr_failed(cdr);
- num->nochan++;
- break;
-
- case AST_CAUSE_NORMAL_CLEARING:
- break;
-
- default:
- num->nochan++;
- break;
- }
-}
-
-/* free the buffer if allocated, and set the pointer to the second arg */
-#define S_REPLACE(s, new_val) \
- do { \
- if (s) \
- ast_free(s); \
- s = (new_val); \
- } while (0)
-
-static int onedigit_goto(struct ast_channel *chan, const char *context, char exten, int pri)
-{
- char rexten[2] = { exten, '\0' };
-
- if (context) {
- if (!ast_goto_if_exists(chan, context, rexten, pri))
- return 1;
- } else {
- if (!ast_goto_if_exists(chan, chan->context, rexten, pri))
- return 1;
- else if (!ast_strlen_zero(chan->macrocontext)) {
- if (!ast_goto_if_exists(chan, chan->macrocontext, rexten, pri))
- return 1;
- }
- }
- return 0;
-}
-
-
-static const char *get_cid_name(char *name, int namelen, struct ast_channel *chan)
-{
- const char *context = S_OR(chan->macrocontext, chan->context);
- const char *exten = S_OR(chan->macroexten, chan->exten);
-
- return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : "";
-}
-
-static void senddialevent(struct ast_channel *src, struct ast_channel *dst, const char *dialstring)
-{
- manager_event(EVENT_FLAG_CALL, "Dial",
- "SubEvent: Begin\r\n"
- "Channel: %s\r\n"
- "Destination: %s\r\n"
- "CallerIDNum: %s\r\n"
- "CallerIDName: %s\r\n"
- "UniqueID: %s\r\n"
- "DestUniqueID: %s\r\n"
- "Dialstring: %s\r\n",
- src->name, dst->name, S_OR(src->cid.cid_num, "<unknown>"),
- S_OR(src->cid.cid_name, "<unknown>"), src->uniqueid,
- dst->uniqueid, dialstring ? dialstring : "");
-}
-
-static void senddialendevent(const struct ast_channel *src, const char *dialstatus)
-{
- manager_event(EVENT_FLAG_CALL, "Dial",
- "SubEvent: End\r\n"
- "Channel: %s\r\n"
- "UniqueID: %s\r\n"
- "DialStatus: %s\r\n",
- src->name, src->uniqueid, dialstatus);
-}
-
-/*!
- * helper function for wait_for_answer()
- *
- * XXX this code is highly suspicious, as it essentially overwrites
- * the outgoing channel without properly deleting it.
- */
-static void do_forward(struct chanlist *o,
- struct cause_args *num, struct ast_flags64 *peerflags, int single)
-{
- char tmpchan[256];
- struct ast_channel *original = o->chan;
- struct ast_channel *c = o->chan; /* the winner */
- struct ast_channel *in = num->chan; /* the input channel */
- char *stuff;
- char *tech;
- int cause;
-
- ast_copy_string(tmpchan, c->call_forward, sizeof(tmpchan));
- if ((stuff = strchr(tmpchan, '/'))) {
- *stuff++ = '\0';
- tech = tmpchan;
- } else {
- const char *forward_context = pbx_builtin_getvar_helper(c, "FORWARD_CONTEXT");
- snprintf(tmpchan, sizeof(tmpchan), "%s@%s", c->call_forward, forward_context ? forward_context : c->context);
- stuff = tmpchan;
- tech = "Local";
- }
- /* Before processing channel, go ahead and check for forwarding */
- ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, c->name);
- /* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
- if (ast_test_flag64(peerflags, OPT_IGNORE_FORWARDING)) {
- ast_verb(3, "Forwarding %s to '%s/%s' prevented.\n", in->name, tech, stuff);
- c = o->chan = NULL;
- cause = AST_CAUSE_BUSY;
- } else {
- /* Setup parameters */
- c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause);
- if (c) {
- if (single)
- ast_channel_make_compatible(o->chan, in);
- ast_channel_inherit_variables(in, o->chan);
- ast_channel_datastore_inherit(in, o->chan);
- } else
- ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
- }
- if (!c) {
- ast_clear_flag64(o, DIAL_STILLGOING);
- handle_cause(cause, num);
- } else {
- char *new_cid_num, *new_cid_name;
- struct ast_channel *src;
-
- ast_rtp_make_compatible(c, in, single);
- if (ast_test_flag64(o, OPT_FORCECLID)) {
- new_cid_num = ast_strdup(S_OR(in->macroexten, in->exten));
- new_cid_name = NULL; /* XXX no name ? */
- src = c; /* XXX possible bug in previous code, which used 'winner' ? it may have changed */
- } else {
- new_cid_num = ast_strdup(in->cid.cid_num);
- new_cid_name = ast_strdup(in->cid.cid_name);
- src = in;
- }
- ast_string_field_set(c, accountcode, src->accountcode);
- c->cdrflags = src->cdrflags;
- S_REPLACE(c->cid.cid_num, new_cid_num);
- S_REPLACE(c->cid.cid_name, new_cid_name);
-
- if (in->cid.cid_ani) { /* XXX or maybe unconditional ? */
- S_REPLACE(c->cid.cid_ani, ast_strdup(in->cid.cid_ani));
- }
- S_REPLACE(c->cid.cid_rdnis, ast_strdup(S_OR(in->macroexten, in->exten)));
- if (ast_call(c, tmpchan, 0)) {
- ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
- ast_clear_flag64(o, DIAL_STILLGOING);
- ast_hangup(original);
- c = o->chan = NULL;
- num->nochan++;
- } else {
- senddialevent(in, c, stuff);
- /* After calling, set callerid to extension */
- if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) {
- char cidname[AST_MAX_EXTENSION] = "";
- ast_set_callerid(c, S_OR(in->macroexten, in->exten), get_cid_name(cidname, sizeof(cidname), in), NULL);
- }
- /* Hangup the original channel now, in case we needed it */
- ast_hangup(original);
- }
- }
-}
-
-/* argument used for some functions. */
-struct privacy_args {
- int sentringing;
- int privdb_val;
- char privcid[256];
- char privintro[1024];
- char status[256];
-};
-
-static struct ast_channel *wait_for_answer(struct ast_channel *in,
- struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags,
- struct privacy_args *pa,
- const struct cause_args *num_in, int *result)
-{
- struct cause_args num = *num_in;
- int prestart = num.busy + num.congestion + num.nochan;
- int orig = *to;
- struct ast_channel *peer = NULL;
- /* single is set if only one destination is enabled */
- int single = outgoing && !outgoing->next && !ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
-#ifdef HAVE_EPOLL
- struct chanlist *epollo;
-#endif
-
- if (single) {
- /* Turn off hold music, etc */
- ast_deactivate_generator(in);
- /* If we are calling a single channel, make them compatible for in-band tone purpose */
- ast_channel_make_compatible(outgoing->chan, in);
- }
-
-#ifdef HAVE_EPOLL
- for (epollo = outgoing; epollo; epollo = epollo->next)
- ast_poll_channel_add(in, epollo->chan);
-#endif
-
- while (*to && !peer) {
- struct chanlist *o;
- int pos = 0; /* how many channels do we handle */
- int numlines = prestart;
- struct ast_channel *winner;
- struct ast_channel *watchers[AST_MAX_WATCHERS];
-
- watchers[pos++] = in;
- for (o = outgoing; o; o = o->next) {
- /* Keep track of important channels */
- if (ast_test_flag64(o, DIAL_STILLGOING) && o->chan)
- watchers[pos++] = o->chan;
- numlines++;
- }
- if (pos == 1) { /* only the input channel is available */
- if (numlines == (num.busy + num.congestion + num.nochan)) {
- ast_verb(2, "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
- if (num.busy)
- strcpy(pa->status, "BUSY");
- else if (num.congestion)
- strcpy(pa->status, "CONGESTION");
- else if (num.nochan)
- strcpy(pa->status, "CHANUNAVAIL");
- } else {
- ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
- }
- *to = 0;
- return NULL;
- }
- winner = ast_waitfor_n(watchers, pos, to);
- for (o = outgoing; o; o = o->next) {
- struct ast_frame *f;
- struct ast_channel *c = o->chan;
-
- if (c == NULL)
- continue;
- if (ast_test_flag64(o, DIAL_STILLGOING) && c->_state == AST_STATE_UP) {
- if (!peer) {
- ast_verb(3, "%s answered %s\n", c->name, in->name);
- peer = c;
- ast_copy_flags64(peerflags, o,
- OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
- OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
- OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
- OPT_CALLEE_PARK | OPT_CALLER_PARK |
- OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
- DIAL_NOFORWARDHTML);
- ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext));
- ast_copy_string(c->exten, "", sizeof(c->exten));
- }
- continue;
- }
- if (c != winner)
- continue;
- /* here, o->chan == c == winner */
- if (!ast_strlen_zero(c->call_forward)) {
- do_forward(o, &num, peerflags, single);
- continue;
- }
- f = ast_read(winner);
- if (!f) {
- in->hangupcause = c->hangupcause;
-#ifdef HAVE_EPOLL
- ast_poll_channel_del(in, c);
-#endif
- ast_hangup(c);
- c = o->chan = NULL;
- ast_clear_flag64(o, DIAL_STILLGOING);
- handle_cause(in->hangupcause, &num);
- continue;
- }
- if (f->frametype == AST_FRAME_CONTROL) {
- switch(f->subclass) {
- case AST_CONTROL_ANSWER:
- /* This is our guy if someone answered. */
- if (!peer) {
- ast_verb(3, "%s answered %s\n", c->name, in->name);
- peer = c;
- ast_copy_flags64(peerflags, o,
- OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
- OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
- OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
- OPT_CALLEE_PARK | OPT_CALLER_PARK |
- OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
- DIAL_NOFORWARDHTML);
- ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext));
- ast_copy_string(c->exten, "", sizeof(c->exten));
- if (CAN_EARLY_BRIDGE(peerflags))
- /* Setup early bridge if appropriate */
- ast_channel_early_bridge(in, peer);
- }
- /* If call has been answered, then the eventual hangup is likely to be normal hangup */
- in->hangupcause = AST_CAUSE_NORMAL_CLEARING;
- c->hangupcause = AST_CAUSE_NORMAL_CLEARING;
- break;
- case AST_CONTROL_BUSY:
- ast_verb(3, "%s is busy\n", c->name);
- in->hangupcause = c->hangupcause;
- ast_hangup(c);
- c = o->chan = NULL;
- ast_clear_flag64(o, DIAL_STILLGOING);
- handle_cause(AST_CAUSE_BUSY, &num);
- break;
- case AST_CONTROL_CONGESTION:
- ast_verb(3, "%s is circuit-busy\n", c->name);
- in->hangupcause = c->hangupcause;
- ast_hangup(c);
- c = o->chan = NULL;
- ast_clear_flag64(o, DIAL_STILLGOING);
- handle_cause(AST_CAUSE_CONGESTION, &num);
- break;
- case AST_CONTROL_RINGING:
- ast_verb(3, "%s is ringing\n", c->name);
- /* Setup early media if appropriate */
- if (single && CAN_EARLY_BRIDGE(peerflags))
- ast_channel_early_bridge(in, c);
- if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK)) {
- ast_indicate(in, AST_CONTROL_RINGING);
- pa->sentringing++;
- }
- break;
- case AST_CONTROL_PROGRESS:
- ast_verb(3, "%s is making progress passing it to %s\n", c->name, in->name);
- /* Setup early media if appropriate */
- if (single && CAN_EARLY_BRIDGE(peerflags))
- ast_channel_early_bridge(in, c);
- if (!ast_test_flag64(outgoing, OPT_RINGBACK))
- ast_indicate(in, AST_CONTROL_PROGRESS);
- break;
- case AST_CONTROL_VIDUPDATE:
- ast_verb(3, "%s requested a video update, passing it to %s\n", c->name, in->name);
- ast_indicate(in, AST_CONTROL_VIDUPDATE);
- break;
- case AST_CONTROL_PROCEEDING:
- ast_verb(3, "%s is proceeding passing it to %s\n", c->name, in->name);
- if (single && CAN_EARLY_BRIDGE(peerflags))
- ast_channel_early_bridge(in, c);
- if (!ast_test_flag64(outgoing, OPT_RINGBACK))
- ast_indicate(in, AST_CONTROL_PROCEEDING);
- break;
- case AST_CONTROL_HOLD:
- ast_verb(3, "Call on %s placed on hold\n", c->name);
- ast_indicate(in, AST_CONTROL_HOLD);
- break;
- case AST_CONTROL_UNHOLD:
- ast_verb(3, "Call on %s left from hold\n", c->name);
- ast_indicate(in, AST_CONTROL_UNHOLD);
- break;
- case AST_CONTROL_OFFHOOK:
- case AST_CONTROL_FLASH:
- /* Ignore going off hook and flash */
- break;
- case -1:
- if (!ast_test_flag64(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) {
- ast_verb(3, "%s stopped sounds\n", c->name);
- ast_indicate(in, -1);
- pa->sentringing = 0;
- }
- break;
- default:
- ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
- }
- } else if (single) {
- /* XXX are we sure the logic is correct ? or we should just switch on f->frametype ? */
- if (f->frametype == AST_FRAME_VOICE && !ast_test_flag64(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) {
- if (ast_write(in, f))
- ast_log(LOG_WARNING, "Unable to forward voice frame\n");
- } else if (f->frametype == AST_FRAME_IMAGE && !ast_test_flag64(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) {
- if (ast_write(in, f))
- ast_log(LOG_WARNING, "Unable to forward image\n");
- } else if (f->frametype == AST_FRAME_TEXT && !ast_test_flag64(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) {
- if (ast_write(in, f))
- ast_log(LOG_WARNING, "Unable to send text\n");
- } else if (f->frametype == AST_FRAME_HTML && !ast_test_flag64(outgoing, DIAL_NOFORWARDHTML)) {
- if (ast_channel_sendhtml(in, f->subclass, f->data, f->datalen) == -1)
- ast_log(LOG_WARNING, "Unable to send URL\n");
- }
- }
- ast_frfree(f);
- } /* end for */
- if (winner == in) {
- struct ast_frame *f = ast_read(in);
-#if 0
- if (f && (f->frametype != AST_FRAME_VOICE))
- printf("Frame type: %d, %d\n", f->frametype, f->subclass);
- else if (!f || (f->frametype != AST_FRAME_VOICE))
- printf("Hangup received on %s\n", in->name);
-#endif
- if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
- /* Got hung up */
- *to = -1;
- strcpy(pa->status, "CANCEL");
- ast_cdr_noanswer(in->cdr);
- if (f)
- ast_frfree(f);
- return NULL;
- }
-
- /* now f is guaranteed non-NULL */
- if (f->frametype == AST_FRAME_DTMF) {
- if (ast_test_flag64(peerflags, OPT_DTMF_EXIT)) {
- const char *context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
- if (onedigit_goto(in, context, (char) f->subclass, 1)) {
- ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
- *to = 0;
- ast_cdr_noanswer(in->cdr);
- *result = f->subclass;
- strcpy(pa->status, "CANCEL");
- ast_frfree(f);
- return NULL;
- }
- }
-
- if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) &&
- (f->subclass == '*')) { /* hmm it it not guaranteed to be '*' anymore. */
- ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
- *to = 0;
- strcpy(pa->status, "CANCEL");
- ast_cdr_noanswer(in->cdr);
- ast_frfree(f);
- return NULL;
- }
- }
-
- /* Forward HTML stuff */
- if (single && (f->frametype == AST_FRAME_HTML) && !ast_test_flag64(outgoing, DIAL_NOFORWARDHTML))
- if (ast_channel_sendhtml(outgoing->chan, f->subclass, f->data, f->datalen) == -1)
- ast_log(LOG_WARNING, "Unable to send URL\n");
-
-
- if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF_BEGIN) || (f->frametype == AST_FRAME_DTMF_END))) {
- if (ast_write(outgoing->chan, f))
- ast_log(LOG_WARNING, "Unable to forward voice or dtmf\n");
- }
- if (single && (f->frametype == AST_FRAME_CONTROL) &&
- ((f->subclass == AST_CONTROL_HOLD) ||
- (f->subclass == AST_CONTROL_UNHOLD) ||
- (f->subclass == AST_CONTROL_VIDUPDATE))) {
- ast_verb(3, "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name);
- ast_indicate_data(outgoing->chan, f->subclass, f->data, f->datalen);
- }
- ast_frfree(f);
- }
- if (!*to)
- ast_verb(3, "Nobody picked up in %d ms\n", orig);
- if (!*to || ast_check_hangup(in)) {
- ast_cdr_noanswer(in->cdr);
- }
-
- }
- if (peer && !ast_cdr_log_unanswered()) {
- /* suppress the CDR's that didn't win */
- struct chanlist *o;
- for (o = outgoing; o; o = o->next) {
- struct ast_channel *c = o->chan;
- if (c && c != peer && c->cdr) {
- ast_set_flag(c->cdr, AST_CDR_FLAG_POST_DISABLED);
- }
- }
- } else if (!peer && !ast_cdr_log_unanswered()) {
- /* suppress the CDR's that didn't win */
- struct chanlist *o;
- for (o = outgoing; o; o = o->next) {
- struct ast_channel *c = o->chan;
- if (c && c->cdr) {
- ast_set_flag(c->cdr, AST_CDR_FLAG_POST_DISABLED);
- }
- }
- }
-
-#ifdef HAVE_EPOLL
- for (epollo = outgoing; epollo; epollo = epollo->next) {
- if (epollo->chan)
- ast_poll_channel_del(in, epollo->chan);
- }
-#endif
-
- return peer;
-}
-
-static void replace_macro_delimiter(char *s)
-{
- for (; *s; s++)
- if (*s == '^')
- *s = ',';
-}
-
-
-/* returns true if there is a valid privacy reply */
-static int valid_priv_reply(struct ast_flags64 *opts, int res)
-{
- if (res < '1')
- return 0;
- if (ast_test_flag64(opts, OPT_PRIVACY) && res <= '5')
- return 1;
- if (ast_test_flag64(opts, OPT_SCREENING) && res <= '4')
- return 1;
- return 0;
-}
-
-static int do_timelimit(struct ast_channel *chan, struct ast_bridge_config *config,
- char *parse, unsigned int *calldurationlimit)
-{
- char *stringp = ast_strdupa(parse);
- char *limit_str, *warning_str, *warnfreq_str;
- const char *var;
- int play_to_caller = 0, play_to_callee = 0;
- int delta;
-
- limit_str = strsep(&stringp, ":");
- warning_str = strsep(&stringp, ":");
- warnfreq_str = strsep(&stringp, ":");
-
- config->timelimit = atol(limit_str);
- if (warning_str)
- config->play_warning = atol(warning_str);
- if (warnfreq_str)
- config->warning_freq = atol(warnfreq_str);
-
- if (!config->timelimit) {
- ast_log(LOG_WARNING, "Dial does not accept L(%s), hanging up.\n", limit_str);
- config->timelimit = config->play_warning = config->warning_freq = 0;
- config->warning_sound = NULL;
- return -1; /* error */
- } else if ( (delta = config->play_warning - config->timelimit) > 0) {
- int w = config->warning_freq;
-
- /* If the first warning is requested _after_ the entire call would end,
- and no warning frequency is requested, then turn off the warning. If
- a warning frequency is requested, reduce the 'first warning' time by
- that frequency until it falls within the call's total time limit.
- Graphically:
- timelim->| delta |<-playwarning
- 0__________________|_________________|
- | w | | | |
-
- so the number of intervals to cut is 1+(delta-1)/w
- */
-
- if (w == 0) {
- config->play_warning = 0;
- } else {
- config->play_warning -= w * ( 1 + (delta-1)/w );
- if (config->play_warning < 1)
- config->play_warning = config->warning_freq = 0;
- }
- }
-
- var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLER");
- play_to_caller = var ? ast_true(var) : 1;
-
- var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLEE");
- play_to_callee = var ? ast_true(var) : 0;
-
- if (!play_to_caller && !play_to_callee)
- play_to_caller = 1;
-
- var = pbx_builtin_getvar_helper(chan, "LIMIT_WARNING_FILE");
- config->warning_sound = S_OR(var, "timeleft");
-
- /* The code looking at config wants a NULL, not just "", to decide
- * that the message should not be played, so we replace "" with NULL.
- * Note, pbx_builtin_getvar_helper _can_ return NULL if the variable is
- * not found.
- */
- var = pbx_builtin_getvar_helper(chan, "LIMIT_TIMEOUT_FILE");
- config->end_sound = S_OR(var, NULL);
- var = pbx_builtin_getvar_helper(chan, "LIMIT_CONNECT_FILE");
- config->start_sound = S_OR(var, NULL);
-
- /* undo effect of S(x) in case they are both used */
- *calldurationlimit = 0;
- /* more efficient to do it like S(x) does since no advanced opts */
- if (!config->play_warning && !config->start_sound && !config->end_sound && config->timelimit) {
- *calldurationlimit = config->timelimit / 1000;
- ast_verb(3, "Setting call duration limit to %d seconds.\n",
- *calldurationlimit);
- config->timelimit = play_to_caller = play_to_callee =
- config->play_warning = config->warning_freq = 0;
- } else {
- ast_verb(3, "Limit Data for this call:\n");
- ast_verb(4, "timelimit = %ld\n", config->timelimit);
- ast_verb(4, "play_warning = %ld\n", config->play_warning);
- ast_verb(4, "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
- ast_verb(4, "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
- ast_verb(4, "warning_freq = %ld\n", config->warning_freq);
- ast_verb(4, "start_sound = %s\n", S_OR(config->start_sound, ""));
- ast_verb(4, "warning_sound = %s\n", config->warning_sound);
- ast_verb(4, "end_sound = %s\n", S_OR(config->end_sound, ""));
- }
- if (play_to_caller)
- ast_set_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
- if (play_to_callee)
- ast_set_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
- return 0;
-}
-
-static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
- struct ast_flags64 *opts, char **opt_args, struct privacy_args *pa)
-{
-
- int res2;
- int loopcount = 0;
-
- /* Get the user's intro, store it in priv-callerintros/$CID,
- unless it is already there-- this should be done before the
- call is actually dialed */
-
- /* all ring indications and moh for the caller has been halted as soon as the
- target extension was picked up. We are going to have to kill some
- time and make the caller believe the peer hasn't picked up yet */
-
- if (ast_test_flag64(opts, OPT_MUSICBACK) && !ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
- char *original_moh = ast_strdupa(chan->musicclass);
- ast_indicate(chan, -1);
- ast_string_field_set(chan, musicclass, opt_args[OPT_ARG_MUSICBACK]);
- ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
- ast_string_field_set(chan, musicclass, original_moh);
- } else if (ast_test_flag64(opts, OPT_RINGBACK)) {
- ast_indicate(chan, AST_CONTROL_RINGING);
- pa->sentringing++;
- }
-
- /* Start autoservice on the other chan ?? */
- res2 = ast_autoservice_start(chan);
- /* Now Stream the File */
- for (loopcount = 0; loopcount < 3; loopcount++) {
- if (res2 && loopcount == 0) /* error in ast_autoservice_start() */
- break;
- if (!res2) /* on timeout, play the message again */
- res2 = ast_play_and_wait(peer, "priv-callpending");
- if (!valid_priv_reply(opts, res2))
- res2 = 0;
- /* priv-callpending script:
- "I have a caller waiting, who introduces themselves as:"
- */
- if (!res2)
- res2 = ast_play_and_wait(peer, pa->privintro);
- if (!valid_priv_reply(opts, res2))
- res2 = 0;
- /* now get input from the called party, as to their choice */
- if (!res2) {
- /* XXX can we have both, or they are mutually exclusive ? */
- if (ast_test_flag64(opts, OPT_PRIVACY))
- res2 = ast_play_and_wait(peer, "priv-callee-options");
- if (ast_test_flag64(opts, OPT_SCREENING))
- res2 = ast_play_and_wait(peer, "screen-callee-options");
- }
- /*! \page DialPrivacy Dial Privacy scripts
- \par priv-callee-options script:
- "Dial 1 if you wish this caller to reach you directly in the future,
- and immediately connect to their incoming call
- Dial 2 if you wish to send this caller to voicemail now and
- forevermore.
- Dial 3 to send this caller to the torture menus, now and forevermore.
- Dial 4 to send this caller to a simple "go away" menu, now and forevermore.
- Dial 5 to allow this caller to come straight thru to you in the future,
- but right now, just this once, send them to voicemail."
- \par screen-callee-options script:
- "Dial 1 if you wish to immediately connect to the incoming call
- Dial 2 if you wish to send this caller to voicemail.
- Dial 3 to send this caller to the torture menus.
- Dial 4 to send this caller to a simple "go away" menu.
- */
- if (valid_priv_reply(opts, res2))
- break;
- /* invalid option */
- res2 = ast_play_and_wait(peer, "vm-sorry");
- }
-
- if (ast_test_flag64(opts, OPT_MUSICBACK)) {
- ast_moh_stop(chan);
- } else if (ast_test_flag64(opts, OPT_RINGBACK)) {
- ast_indicate(chan, -1);
- pa->sentringing = 0;
- }
- ast_autoservice_stop(chan);
- if (ast_test_flag64(opts, OPT_PRIVACY) && (res2 >= '1' && res2 <= '5')) {
- /* map keypresses to various things, the index is res2 - '1' */
- static const char *_val[] = { "ALLOW", "DENY", "TORTURE", "KILL", "ALLOW" };
- static const int _flag[] = { AST_PRIVACY_ALLOW, AST_PRIVACY_DENY, AST_PRIVACY_TORTURE, AST_PRIVACY_KILL, AST_PRIVACY_ALLOW};
- int i = res2 - '1';
- ast_verb(3, "--Set privacy database entry %s/%s to %s\n",
- opt_args[OPT_ARG_PRIVACY], pa->privcid, _val[i]);
- ast_privacy_set(opt_args[OPT_ARG_PRIVACY], pa->privcid, _flag[i]);
- }
- switch (res2) {
- case '1':
- break;
- case '2':
- ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
- break;
- case '3':
- ast_copy_string(pa->status, "TORTURE", sizeof(pa->status));
- break;
- case '4':
- ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
- break;
- case '5':
- /* XXX should we set status to DENY ? */
- if (ast_test_flag64(opts, OPT_PRIVACY))
- break;
- /* if not privacy, then 5 is the same as "default" case */
- default: /* bad input or -1 if failure to start autoservice */
- /* well, if the user messes up, ... he had his chance... What Is The Best Thing To Do? */
- /* well, there seems basically two choices. Just patch the caller thru immediately,
- or,... put 'em thru to voicemail. */
- /* since the callee may have hung up, let's do the voicemail thing, no database decision */
- ast_log(LOG_NOTICE, "privacy: no valid response from the callee. Sending the caller to voicemail, the callee isn't responding\n");
- /* XXX should we set status to DENY ? */
- /* XXX what about the privacy flags ? */
- break;
- }
-
- if (res2 == '1') { /* the only case where we actually connect */
- /* if the intro is NOCALLERID, then there's no reason to leave it on disk, it'll
- just clog things up, and it's not useful information, not being tied to a CID */
- if (strncmp(pa->privcid, "NOCALLERID", 10) == 0 || ast_test_flag64(opts, OPT_SCREEN_NOINTRO)) {
- ast_filedelete(pa->privintro, NULL);
- if (ast_fileexists(pa->privintro, NULL, NULL) > 0)
- ast_log(LOG_NOTICE, "privacy: ast_filedelete didn't do its job on %s\n", pa->privintro);
- else
- ast_verb(3, "Successfully deleted %s intro file\n", pa->privintro);
- }
- return 0; /* the good exit path */
- } else {
- ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */
- return -1;
- }
-}
-
-/*! \brief returns 1 if successful, 0 or <0 if the caller should 'goto out' */
-static int setup_privacy_args(struct privacy_args *pa,
- struct ast_flags64 *opts, char *opt_args[], struct ast_channel *chan)
-{
- char callerid[60];
- int res;
- char *l;
-
- if (!ast_strlen_zero(chan->cid.cid_num)) {
- l = ast_strdupa(chan->cid.cid_num);
- ast_shrink_phone_number(l);
- if (ast_test_flag64(opts, OPT_PRIVACY) ) {
- ast_verb(3, "Privacy DB is '%s', clid is '%s'\n", opt_args[OPT_ARG_PRIVACY], l);
- pa->privdb_val = ast_privacy_check(opt_args[OPT_ARG_PRIVACY], l);
- } else {
- ast_verb(3, "Privacy Screening, clid is '%s'\n", l);
- pa->privdb_val = AST_PRIVACY_UNKNOWN;
- }
- } else {
- char *tnam, *tn2;
-
- tnam = ast_strdupa(chan->name);
- /* clean the channel name so slashes don't try to end up in disk file name */
- for (tn2 = tnam; *tn2; tn2++) {
- if (*tn2 == '/') /* any other chars to be afraid of? */
- *tn2 = '=';
- }
- ast_verb(3, "Privacy-- callerid is empty\n");
-
- snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", chan->exten, tnam);
- l = callerid;
- pa->privdb_val = AST_PRIVACY_UNKNOWN;
- }
-
- ast_copy_string(pa->privcid, l, sizeof(pa->privcid));
-
- if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCLID)) {
- /* if callerid is set and OPT_SCREEN_NOCLID is set also */
- ast_verb(3, "CallerID set (%s); N option set; Screening should be off\n", pa->privcid);
- pa->privdb_val = AST_PRIVACY_ALLOW;
- } else if (ast_test_flag64(opts, OPT_SCREEN_NOCLID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) {
- ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val);
- }
-
- if (pa->privdb_val == AST_PRIVACY_DENY) {
- ast_verb(3, "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n");
- ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
- return 0;
- } else if (pa->privdb_val == AST_PRIVACY_KILL) {
- ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
- return 0; /* Is this right? */
- } else if (pa->privdb_val == AST_PRIVACY_TORTURE) {
- ast_copy_string(pa->status, "TORTURE", sizeof(pa->status));
- return 0; /* is this right??? */
- } else if (pa->privdb_val == AST_PRIVACY_UNKNOWN) {
- /* Get the user's intro, store it in priv-callerintros/$CID,
- unless it is already there-- this should be done before the
- call is actually dialed */
-
- /* make sure the priv-callerintros dir actually exists */
- snprintf(pa->privintro, sizeof(pa->privintro), "%s/sounds/priv-callerintros", ast_config_AST_DATA_DIR);
- if ((res = ast_mkdir(pa->privintro, 0755))) {
- ast_log(LOG_WARNING, "privacy: can't create directory priv-callerintros: %s\n", strerror(res));
- return -1;
- }
-
- snprintf(pa->privintro, sizeof(pa->privintro), "priv-callerintros/%s", pa->privcid);
- if (ast_fileexists(pa->privintro, NULL, NULL ) > 0 && strncmp(pa->privcid, "NOCALLERID", 10) != 0) {
- /* the DELUX version of this code would allow this caller the
- option to hear and retape their previously recorded intro.
- */
- } else {
- int duration; /* for feedback from play_and_wait */
- /* the file doesn't exist yet. Let the caller submit his
- vocal intro for posterity */
- /* priv-recordintro script:
-
- "At the tone, please say your name:"
-
- */
- ast_answer(chan);
- res = ast_play_and_record(chan, "priv-recordintro", pa->privintro, 4, "gsm", &duration, 128, 2000, 0); /* NOTE: I've reduced the total time to 4 sec */
- /* don't think we'll need a lock removed, we took care of
- conflicts by naming the pa.privintro file */
- if (res == -1) {
- /* Delete the file regardless since they hung up during recording */
- ast_filedelete(pa->privintro, NULL);
- if (ast_fileexists(pa->privintro, NULL, NULL) > 0)
- ast_log(LOG_NOTICE, "privacy: ast_filedelete didn't do its job on %s\n", pa->privintro);
- else
- ast_verb(3, "Successfully deleted %s intro file\n", pa->privintro);
- return -1;
- }
- if (!ast_streamfile(chan, "vm-dialout", chan->language) )
- ast_waitstream(chan, "");
- }
- }
- return 1; /* success */
-}
-
-static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags64 *peerflags, int *continue_exec)
-{
- int res = -1; /* default: error */
- char *rest, *cur; /* scan the list of destinations */
- struct chanlist *outgoing = NULL; /* list of destinations */
- struct ast_channel *peer;
- int to; /* timeout */
- struct cause_args num = { chan, 0, 0, 0 };
- int cause;
- char numsubst[256];
- char cidname[AST_MAX_EXTENSION] = "";
-
- struct ast_bridge_config config = { { 0, } };
- unsigned int calldurationlimit = 0;
- char *dtmfcalled = NULL, *dtmfcalling = NULL;
- struct privacy_args pa = {
- .sentringing = 0,
- .privdb_val = 0,
- .status = "INVALIDARGS",
- };
- int sentringing = 0, moh = 0;
- const char *outbound_group = NULL;
- int result = 0;
- time_t start_time;
- char *parse;
- int opermode = 0;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(peers);
- AST_APP_ARG(timeout);
- AST_APP_ARG(options);
- AST_APP_ARG(url);
- );
- struct ast_flags64 opts = { 0, };
- char *opt_args[OPT_ARG_ARRAY_SIZE];
- struct ast_datastore *datastore = NULL;
- int fulldial = 0, num_dialed = 0;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
- pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
- return -1;
- }
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (!ast_strlen_zero(args.options) &&
- ast_app_parse_options64(dial_exec_options, &opts, opt_args, args.options)) {
- pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
- goto done;
- }
-
- if (ast_strlen_zero(args.peers)) {
- ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
- pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
- goto done;
- }
-
- if (ast_test_flag64(&opts, OPT_OPERMODE)) {
- opermode = ast_strlen_zero(opt_args[OPT_ARG_OPERMODE]) ? 1 : atoi(opt_args[OPT_ARG_OPERMODE]);
- ast_verb(3, "Setting operator services mode to %d.\n", opermode);
- }
-
- if (ast_test_flag64(&opts, OPT_DURATION_STOP) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_STOP])) {
- calldurationlimit = atoi(opt_args[OPT_ARG_DURATION_STOP]);
- if (!calldurationlimit) {
- ast_log(LOG_WARNING, "Dial does not accept S(%s), hanging up.\n", opt_args[OPT_ARG_DURATION_STOP]);
- pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
- goto done;
- }
- ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
- }
-
- if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {
- dtmfcalling = opt_args[OPT_ARG_SENDDTMF];
- dtmfcalled = strsep(&dtmfcalling, ":");
- }
-
- if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
- if (do_timelimit(chan, &config, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit))
- goto done;
- }
-
- if (ast_test_flag64(&opts, OPT_RESETCDR) && chan->cdr)
- ast_cdr_reset(chan->cdr, NULL);
- if (ast_test_flag64(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY]))
- opt_args[OPT_ARG_PRIVACY] = ast_strdupa(chan->exten);
-
- if (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) {
- res = setup_privacy_args(&pa, &opts, opt_args, chan);
- if (res <= 0)
- goto out;
- res = -1; /* reset default */
- }
-
- if (continue_exec)
- *continue_exec = 0;
-
- /* If a channel group has been specified, get it for use when we create peer channels */
- if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP_ONCE"))) {
- outbound_group = ast_strdupa(outbound_group);
- pbx_builtin_setvar_helper(chan, "OUTBOUND_GROUP_ONCE", NULL);
- } else {
- outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP");
- }
-
- ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING);
- /* loop through the list of dial destinations */
- rest = args.peers;
- while ((cur = strsep(&rest, "&")) ) {
- struct chanlist *tmp;
- struct ast_channel *tc; /* channel for this destination */
- /* Get a technology/[device:]number pair */
- char *number = cur;
- char *interface = ast_strdupa(number);
- char *tech = strsep(&number, "/");
- /* find if we already dialed this interface */
- struct ast_dialed_interface *di;
- AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
- num_dialed++;
- if (!number) {
- ast_log(LOG_WARNING, "Dial argument takes format (technology/[device:]number1)\n");
- goto out;
- }
- if (!(tmp = ast_calloc(1, sizeof(*tmp))))
- goto out;
- if (opts.flags) {
- ast_copy_flags64(tmp, &opts,
- OPT_CANCEL_ELSEWHERE |
- OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
- OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
- OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
- OPT_CALLEE_PARK | OPT_CALLER_PARK |
- OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
- OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID);
- ast_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML);
- }
- ast_copy_string(numsubst, number, sizeof(numsubst));
- /* Request the peer */
-
- ast_channel_lock(chan);
- datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL);
- ast_channel_unlock(chan);
-
- if (datastore)
- dialed_interfaces = datastore->data;
- else {
- if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
- ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n");
- ast_free(tmp);
- goto out;
- }
-
- datastore->inheritance = DATASTORE_INHERIT_FOREVER;
-
- if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
- ast_free(tmp);
- goto out;
- }
-
- datastore->data = dialed_interfaces;
- AST_LIST_HEAD_INIT(dialed_interfaces);
-
- ast_channel_lock(chan);
- ast_channel_datastore_add(chan, datastore);
- ast_channel_unlock(chan);
- }
-
- AST_LIST_LOCK(dialed_interfaces);
- AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
- if (!strcasecmp(di->interface, interface)) {
- ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n",
- di->interface);
- break;
- }
- }
- AST_LIST_UNLOCK(dialed_interfaces);
-
- if (di) {
- fulldial++;
- ast_free(tmp);
- continue;
- }
-
- /* It is always ok to dial a Local interface. We only keep track of
- * which "real" interfaces have been dialed. The Local channel will
- * inherit this list so that if it ends up dialing a real interface,
- * it won't call one that has already been called. */
- if (strcasecmp(tech, "Local")) {
- if (!(di = ast_calloc(1, sizeof(*di) + strlen(interface)))) {
- AST_LIST_UNLOCK(dialed_interfaces);
- ast_free(tmp);
- goto out;
- }
- strcpy(di->interface, interface);
-
- AST_LIST_LOCK(dialed_interfaces);
- AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
- AST_LIST_UNLOCK(dialed_interfaces);
- }
-
- tc = ast_request(tech, chan->nativeformats, numsubst, &cause);
- if (!tc) {
- /* If we can't, just go on to the next call */
- ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n",
- tech, cause, ast_cause2str(cause));
- handle_cause(cause, &num);
- if (!rest) /* we are on the last destination */
- chan->hangupcause = cause;
- ast_free(tmp);
- continue;
- }
- pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst);
-
- /* Setup outgoing SDP to match incoming one */
- ast_rtp_make_compatible(tc, chan, !outgoing && !rest);
-
- /* Inherit specially named variables from parent channel */
- ast_channel_inherit_variables(chan, tc);
-
- tc->appl = "AppDial";
- tc->data = "(Outgoing Line)";
- tc->whentohangup = 0;
-
- S_REPLACE(tc->cid.cid_num, ast_strdup(chan->cid.cid_num));
- S_REPLACE(tc->cid.cid_name, ast_strdup(chan->cid.cid_name));
- S_REPLACE(tc->cid.cid_ani, ast_strdup(chan->cid.cid_ani));
- S_REPLACE(tc->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis));
-
- /* Copy language from incoming to outgoing */
- ast_string_field_set(tc, language, chan->language);
- ast_string_field_set(tc, accountcode, chan->accountcode);
- tc->cdrflags = chan->cdrflags;
- if (ast_strlen_zero(tc->musicclass))
- ast_string_field_set(tc, musicclass, chan->musicclass);
- /* Pass callingpres, type of number, tns, ADSI CPE, transfer capability */
- tc->cid.cid_pres = chan->cid.cid_pres;
- tc->cid.cid_ton = chan->cid.cid_ton;
- tc->cid.cid_tns = chan->cid.cid_tns;
- tc->cid.cid_ani2 = chan->cid.cid_ani2;
- tc->adsicpe = chan->adsicpe;
- tc->transfercapability = chan->transfercapability;
-
- /* If we have an outbound group, set this peer channel to it */
- if (outbound_group)
- ast_app_group_set_channel(tc, outbound_group);
-
- /* Inherit context and extension */
- if (!ast_strlen_zero(chan->macrocontext))
- ast_copy_string(tc->dialcontext, chan->macrocontext, sizeof(tc->dialcontext));
- else
- ast_copy_string(tc->dialcontext, chan->context, sizeof(tc->dialcontext));
- if (!ast_strlen_zero(chan->macroexten))
- ast_copy_string(tc->exten, chan->macroexten, sizeof(tc->exten));
- else
- ast_copy_string(tc->exten, chan->exten, sizeof(tc->exten));
-
- res = ast_call(tc, numsubst, 0); /* Place the call, but don't wait on the answer */
-
- /* Save the info in cdr's that we called them */
- if (chan->cdr)
- ast_cdr_setdestchan(chan->cdr, tc->name);
-
- /* check the results of ast_call */
- if (res) {
- /* Again, keep going even if there's an error */
- ast_debug(1, "ast call on peer returned %d\n", res);
- ast_verb(3, "Couldn't call %s\n", numsubst);
- ast_hangup(tc);
- tc = NULL;
- ast_free(tmp);
- continue;
- } else {
- senddialevent(chan, tc, numsubst);
- ast_verb(3, "Called %s\n", numsubst);
- if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID))
- ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL);
- }
- /* Put them in the list of outgoing thingies... We're ready now.
- XXX If we're forcibly removed, these outgoing calls won't get
- hung up XXX */
- ast_set_flag64(tmp, DIAL_STILLGOING);
- tmp->chan = tc;
- tmp->next = outgoing;
- outgoing = tmp;
- /* If this line is up, don't try anybody else */
- if (outgoing->chan->_state == AST_STATE_UP)
- break;
- }
-
- if (ast_strlen_zero(args.timeout)) {
- to = -1;
- } else {
- to = atoi(args.timeout);
- if (to > 0)
- to *= 1000;
- else
- ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
- }
-
- if (!outgoing) {
- strcpy(pa.status, "CHANUNAVAIL");
- if (fulldial == num_dialed) {
- res = -1;
- goto out;
- }
- } else {
- /* Our status will at least be NOANSWER */
- strcpy(pa.status, "NOANSWER");
- if (ast_test_flag64(outgoing, OPT_MUSICBACK)) {
- moh = 1;
- if (!ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
- char *original_moh = ast_strdupa(chan->musicclass);
- ast_string_field_set(chan, musicclass, opt_args[OPT_ARG_MUSICBACK]);
- ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
- ast_string_field_set(chan, musicclass, original_moh);
- } else {
- ast_moh_start(chan, NULL, NULL);
- }
- ast_indicate(chan, AST_CONTROL_PROGRESS);
- } else if (ast_test_flag64(outgoing, OPT_RINGBACK)) {
- ast_indicate(chan, AST_CONTROL_RINGING);
- sentringing++;
- }
- }
-
- time(&start_time);
- peer = wait_for_answer(chan, outgoing, &to, peerflags, &pa, &num, &result);
-
- ast_channel_datastore_remove(chan, datastore);
- ast_channel_datastore_free(datastore);
- if (!peer) {
- if (result) {
- res = result;
- } else if (to) { /* Musta gotten hung up */
- res = -1;
- } else { /* Nobody answered, next please? */
- res = 0;
- }
- /* almost done, although the 'else' block is 400 lines */
- } else {
- const char *number;
- time_t end_time, answer_time = time(NULL);
- char toast[80]; /* buffer to set variables */
-
- strcpy(pa.status, "ANSWER");
- /* Ah ha! Someone answered within the desired timeframe. Of course after this
- we will always return with -1 so that it is hung up properly after the
- conversation. */
- hanguptree(outgoing, peer, 1);
- outgoing = NULL;
- /* If appropriate, log that we have a destination channel */
- if (chan->cdr)
- ast_cdr_setdestchan(chan->cdr, peer->name);
- if (peer->name)
- pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", peer->name);
-
- number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER");
- if (!number)
- number = numsubst;
- pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
- if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) {
- ast_debug(1, "app_dial: sendurl=%s.\n", args.url);
- ast_channel_sendurl( peer, args.url );
- }
- if ( (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) && pa.privdb_val == AST_PRIVACY_UNKNOWN) {
- if (do_privacy(chan, peer, &opts, opt_args, &pa)) {
- res = 0;
- goto out;
- }
- }
- if (!ast_test_flag64(&opts, OPT_ANNOUNCE) || ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE])) {
- res = 0;
- } else {
- int digit = 0;
- /* Start autoservice on the other chan */
- res = ast_autoservice_start(chan);
- /* Now Stream the File */
- if (!res)
- res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], peer->language);
- if (!res) {
- digit = ast_waitstream(peer, AST_DIGIT_ANY);
- }
- /* Ok, done. stop autoservice */
- res = ast_autoservice_stop(chan);
- if (digit > 0 && !res)
- res = ast_senddigit(chan, digit, 0);
- else
- res = digit;
-
- }
-
- if (chan && peer && ast_test_flag64(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) {
- replace_macro_delimiter(opt_args[OPT_ARG_GOTO]);
- ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]);
- /* peer goes to the same context and extension as chan, so just copy info from chan*/
- ast_copy_string(peer->context, chan->context, sizeof(peer->context));
- ast_copy_string(peer->exten, chan->exten, sizeof(peer->exten));
- peer->priority = chan->priority + 2;
- ast_pbx_start(peer);
- hanguptree(outgoing, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
- if (continue_exec)
- *continue_exec = 1;
- res = 0;
- goto done;
- }
-
- if (ast_test_flag64(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) {
- struct ast_app *theapp;
- const char *macro_result;
-
- res = ast_autoservice_start(chan);
- if (res) {
- ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
- res = -1;
- }
-
- theapp = pbx_findapp("Macro");
-
- if (theapp && !res) { /* XXX why check res here ? */
- replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]);
- res = pbx_exec(peer, theapp, opt_args[OPT_ARG_CALLEE_MACRO]);
- ast_debug(1, "Macro exited with status %d\n", res);
- res = 0;
- } else {
- ast_log(LOG_ERROR, "Could not find application Macro\n");
- res = -1;
- }
-
- if (ast_autoservice_stop(chan) < 0) {
- ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
- res = -1;
- }
-
- if (!res && (macro_result = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
- char *macro_transfer_dest;
-
- if (!strcasecmp(macro_result, "BUSY")) {
- ast_copy_string(pa.status, macro_result, sizeof(pa.status));
- ast_set_flag64(peerflags, OPT_GO_ON);
- res = -1;
- } else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) {
- ast_copy_string(pa.status, macro_result, sizeof(pa.status));
- ast_set_flag64(peerflags, OPT_GO_ON);
- res = -1;
- } else if (!strcasecmp(macro_result, "CONTINUE")) {
- /* hangup peer and keep chan alive assuming the macro has changed
- the context / exten / priority or perhaps
- the next priority in the current exten is desired.
- */
- ast_set_flag64(peerflags, OPT_GO_ON);
- res = -1;
- } else if (!strcasecmp(macro_result, "ABORT")) {
- /* Hangup both ends unless the caller has the g flag */
- res = -1;
- } else if (!strncasecmp(macro_result, "GOTO:", 5) && (macro_transfer_dest = ast_strdupa(macro_result + 5))) {
- res = -1;
- /* perform a transfer to a new extension */
- if (strchr(macro_transfer_dest, '^')) { /* context^exten^priority*/
- replace_macro_delimiter(macro_transfer_dest);
- if (!ast_parseable_goto(chan, macro_transfer_dest))
- ast_set_flag64(peerflags, OPT_GO_ON);
- }
- }
- }
- }
-
- if (ast_test_flag64(&opts, OPT_CALLEE_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GOSUB])) {
- struct ast_app *theapp;
- const char *gosub_result;
- char *gosub_args, *gosub_argstart;
-
- res = ast_autoservice_start(chan);
- if (res) {
- ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
- res = -1;
- }
-
- theapp = pbx_findapp("Gosub");
-
- if (theapp && !res) { /* XXX why check res here ? */
- replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GOSUB]);
-
- /* Set where we came from */
- ast_copy_string(peer->context, "app_dial_gosub_virtual_context", sizeof(peer->context));
- ast_copy_string(peer->exten, "s", sizeof(peer->exten));
- peer->priority = 0;
-
- gosub_argstart = strchr(opt_args[OPT_ARG_CALLEE_GOSUB], '|');
- if (gosub_argstart) {
- *gosub_argstart = 0;
- asprintf(&gosub_args, "%s|s|1(%s)", opt_args[OPT_ARG_CALLEE_GOSUB], gosub_argstart + 1);
- *gosub_argstart = '|';
- } else {
- asprintf(&gosub_args, "%s|s|1", opt_args[OPT_ARG_CALLEE_GOSUB]);
- }
-
- if (gosub_args) {
- res = pbx_exec(peer, theapp, gosub_args);
- ast_pbx_run(peer);
- ast_free(gosub_args);
- if (option_debug)
- ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
- } else
- ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
-
- res = 0;
- } else {
- ast_log(LOG_ERROR, "Could not find application Gosub\n");
- res = -1;
- }
-
- if (ast_autoservice_stop(chan) < 0) {
- ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
- res = -1;
- }
-
- if (!res && (gosub_result = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) {
- char *gosub_transfer_dest;
-
- if (!strcasecmp(gosub_result, "BUSY")) {
- ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
- ast_set_flag64(peerflags, OPT_GO_ON);
- res = -1;
- } else if (!strcasecmp(gosub_result, "CONGESTION") || !strcasecmp(gosub_result, "CHANUNAVAIL")) {
- ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
- ast_set_flag64(peerflags, OPT_GO_ON);
- res = -1;
- } else if (!strcasecmp(gosub_result, "CONTINUE")) {
- /* hangup peer and keep chan alive assuming the macro has changed
- the context / exten / priority or perhaps
- the next priority in the current exten is desired.
- */
- ast_set_flag64(peerflags, OPT_GO_ON);
- res = -1;
- } else if (!strcasecmp(gosub_result, "ABORT")) {
- /* Hangup both ends unless the caller has the g flag */
- res = -1;
- } else if (!strncasecmp(gosub_result, "GOTO:", 5) && (gosub_transfer_dest = ast_strdupa(gosub_result + 5))) {
- res = -1;
- /* perform a transfer to a new extension */
- if (strchr(gosub_transfer_dest, '^')) { /* context^exten^priority*/
- replace_macro_delimiter(gosub_transfer_dest);
- if (!ast_parseable_goto(chan, gosub_transfer_dest))
- ast_set_flag64(peerflags, OPT_GO_ON);
- }
- }
- }
- }
-
- if (!res) {
- if (calldurationlimit > 0) {
- peer->whentohangup = time(NULL) + calldurationlimit;
- }
- if (!ast_strlen_zero(dtmfcalled)) {
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the called party.\n", dtmfcalled);
- res = ast_dtmf_stream(peer, chan, dtmfcalled, 250, 0);
- }
- if (!ast_strlen_zero(dtmfcalling)) {
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the calling party.\n", dtmfcalling);
- res = ast_dtmf_stream(chan, peer, dtmfcalling, 250, 0);
- }
- }
-
- if (res) { /* some error */
- res = -1;
- end_time = time(NULL);
- } else {
- if (ast_test_flag64(peerflags, OPT_CALLEE_TRANSFER))
- ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
- if (ast_test_flag64(peerflags, OPT_CALLER_TRANSFER))
- ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
- if (ast_test_flag64(peerflags, OPT_CALLEE_HANGUP))
- ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
- if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP))
- ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
- if (ast_test_flag64(peerflags, OPT_CALLEE_MONITOR))
- ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
- if (ast_test_flag64(peerflags, OPT_CALLER_MONITOR))
- ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
- if (ast_test_flag64(peerflags, OPT_CALLEE_PARK))
- ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
- if (ast_test_flag64(peerflags, OPT_CALLER_PARK))
- ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
- if (ast_test_flag64(peerflags, OPT_CALLEE_MIXMONITOR))
- ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMIXMON);
- if (ast_test_flag64(peerflags, OPT_CALLER_MIXMONITOR))
- ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMIXMON);
-
- if (moh) {
- moh = 0;
- ast_moh_stop(chan);
- } else if (sentringing) {
- sentringing = 0;
- ast_indicate(chan, -1);
- }
- /* Be sure no generators are left on it */
- ast_deactivate_generator(chan);
- /* Make sure channels are compatible */
- res = ast_channel_make_compatible(chan, peer);
- if (res < 0) {
- ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", chan->name, peer->name);
- ast_hangup(peer);
- res = -1;
- goto done;
- }
- if (opermode && !strncmp(chan->name, "Zap", 3) && !strncmp(peer->name, "Zap", 3)) {
- /* what's this special handling for Zap <-> Zap ?
- * A: Zap to Zap calls are natively bridged at the kernel driver
- * level, so we need to ensure that this mode gets propagated
- * all the way down. */
- struct oprmode oprmode;
-
- oprmode.peer = peer;
- oprmode.mode = opermode;
-
- ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0);
- }
- res = ast_bridge_call(chan, peer, &config);
- end_time = time(NULL);
- snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time));
- pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", toast);
- }
-
- snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time));
- pbx_builtin_setvar_helper(chan, "DIALEDTIME", toast);
-
-
- if (ast_test_flag64(&opts, OPT_PEER_H)) {
- ast_log(LOG_NOTICE, "PEER context: %s; PEER exten: %s; PEER priority: %d\n",
- peer->context, peer->exten, peer->priority);
- }
-
- strcpy(peer->context, chan->context);
-
- if (ast_test_flag64(&opts, OPT_PEER_H) && ast_exists_extension(peer, peer->context, "h", 1, peer->cid.cid_num)) {
- int autoloopflag;
- int found;
- strcpy(peer->exten, "h");
- peer->priority = 1;
- autoloopflag = ast_test_flag(peer, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
- ast_set_flag(peer, AST_FLAG_IN_AUTOLOOP);
-
- while ((res = ast_spawn_extension(peer, peer->context, peer->exten, peer->priority, peer->cid.cid_num, &found, 1))) {
- peer->priority++;
- }
- if (found && res) {
- /* Something bad happened, or a hangup has been requested. */
- ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name);
- ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name);
- }
- ast_set2_flag(peer, autoloopflag, AST_FLAG_IN_AUTOLOOP); /* set it back the way it was */
- }
- if (res != AST_PBX_NO_HANGUP_PEER) {
- if (!ast_check_hangup(chan))
- chan->hangupcause = peer->hangupcause;
- ast_hangup(peer);
- }
- }
-out:
- if (moh) {
- moh = 0;
- ast_moh_stop(chan);
- } else if (sentringing) {
- sentringing = 0;
- ast_indicate(chan, -1);
- }
- ast_channel_early_bridge(chan, NULL);
- hanguptree(outgoing, NULL, 0); /* In this case, there's no answer anywhere */
- pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
- senddialendevent(chan, pa.status);
- ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
-
- if ((ast_test_flag64(peerflags, OPT_GO_ON)) && !ast_check_hangup(chan) && (res != AST_PBX_KEEPALIVE)) {
- if (calldurationlimit)
- chan->whentohangup = 0;
- res = 0;
- }
-
-done:
- return res;
-}
-
-static int dial_exec(struct ast_channel *chan, void *data)
-{
- struct ast_flags64 peerflags;
-
- memset(&peerflags, 0, sizeof(peerflags));
-
- return dial_exec_full(chan, data, &peerflags, NULL);
-}
-
-static int retrydial_exec(struct ast_channel *chan, void *data)
-{
- char *parse;
- const char *context = NULL;
- int sleep = 0, loops = 0, res = -1;
- struct ast_flags64 peerflags = { 0, };
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(announce);
- AST_APP_ARG(sleep);
- AST_APP_ARG(retries);
- AST_APP_ARG(dialdata);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "RetryDial requires an argument!\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
-
- if ((sleep = atoi(args.sleep))) {
- sleep *= 1000;
- }
-
- loops = atoi(args.retries);
-
- if (!args.dialdata) {
- ast_log(LOG_ERROR, "%s requires a 4th argument (dialdata)\n", rapp);
- goto done;
- }
-
- if (sleep < 1000)
- sleep = 10000;
-
- if (!loops)
- loops = -1; /* run forever */
-
- context = pbx_builtin_getvar_helper(chan, "EXITCONTEXT");
-
- res = 0;
- while (loops) {
- int continue_exec;
-
- chan->data = "Retrying";
- if (ast_test_flag(chan, AST_FLAG_MOH))
- ast_moh_stop(chan);
-
- res = dial_exec_full(chan, args.dialdata, &peerflags, &continue_exec);
- if (continue_exec)
- break;
-
- if (res == 0) {
- if (ast_test_flag64(&peerflags, OPT_DTMF_EXIT)) {
- if (!ast_strlen_zero(args.announce)) {
- if (ast_fileexists(args.announce, NULL, chan->language) > 0) {
- if (!(res = ast_streamfile(chan, args.announce, chan->language)))
- ast_waitstream(chan, AST_DIGIT_ANY);
- } else
- ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", args.announce);
- }
- if (!res && sleep) {
- if (!ast_test_flag(chan, AST_FLAG_MOH))
- ast_moh_start(chan, NULL, NULL);
- res = ast_waitfordigit(chan, sleep);
- }
- } else {
- if (!ast_strlen_zero(args.announce)) {
- if (ast_fileexists(args.announce, NULL, chan->language) > 0) {
- if (!(res = ast_streamfile(chan, args.announce, chan->language)))
- res = ast_waitstream(chan, "");
- } else
- ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", args.announce);
- }
- if (sleep) {
- if (!ast_test_flag(chan, AST_FLAG_MOH))
- ast_moh_start(chan, NULL, NULL);
- if (!res)
- res = ast_waitfordigit(chan, sleep);
- }
- }
- }
-
- if (res < 0)
- break;
- else if (res > 0) { /* Trying to send the call elsewhere (1 digit ext) */
- if (onedigit_goto(chan, context, (char) res, 1)) {
- res = 0;
- break;
- }
- }
- loops--;
- }
- if (loops == 0)
- res = 0;
- else if (res == 1)
- res = 0;
-
- if (ast_test_flag(chan, AST_FLAG_MOH))
- ast_moh_stop(chan);
- done:
- return res;
-}
-
-static int unload_module(void)
-{
- int res;
- struct ast_context *con;
-
- res = ast_unregister_application(app);
- res |= ast_unregister_application(rapp);
-
- if ((con = ast_context_find("app_dial_gosub_virtual_context")))
- {
- ast_context_remove_extension2(con, "s", 1, NULL);
- ast_context_destroy(con, "app_dial"); /* leave nothing behind */
- }
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
- struct ast_context *con;
-
- con = ast_context_find("app_dial_gosub_virtual_context");
- if (!con)
- con = ast_context_create(NULL, "app_dial_gosub_virtual_context", "app_dial");
- if (!con)
- ast_log(LOG_ERROR, "Dial virtual context 'app_dial_gosub_virtual_context' does not exist and unable to create\n");
- else
- ast_add_extension2(con, 1, "s", 1, NULL, NULL, "KeepAlive", ast_strdup(""), ast_free_ptr, "app_dial");
-
- res = ast_register_application(app, dial_exec, synopsis, descrip);
- res |= ast_register_application(rapp, retrydial_exec, rsynopsis, rdescrip);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialing Application");
diff --git a/trunk/apps/app_dictate.c b/trunk/apps/app_dictate.c
deleted file mode 100644
index 58d61116a..000000000
--- a/trunk/apps/app_dictate.c
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2005, Anthony Minessale II
- *
- * Anthony Minessale II <anthmct@yahoo.com>
- *
- * Donated by Sangoma Technologies <http://www.samgoma.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Virtual Dictation Machine Application For Asterisk
- *
- * \author Anthony Minessale II <anthmct@yahoo.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <sys/stat.h>
-
-#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
-#include "asterisk/file.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/say.h"
-#include "asterisk/app.h"
-
-static char *app = "Dictate";
-static char *synopsis = "Virtual Dictation Machine";
-static char *desc = " Dictate([<base_dir>[,<filename>]])\n"
-"Start dictation machine using optional base dir for files.\n";
-
-
-typedef enum {
- DFLAG_RECORD = (1 << 0),
- DFLAG_PLAY = (1 << 1),
- DFLAG_TRUNC = (1 << 2),
- DFLAG_PAUSE = (1 << 3),
-} dflags;
-
-typedef enum {
- DMODE_INIT,
- DMODE_RECORD,
- DMODE_PLAY
-} dmodes;
-
-#define ast_toggle_flag(it,flag) if(ast_test_flag(it, flag)) ast_clear_flag(it, flag); else ast_set_flag(it, flag)
-
-static int play_and_wait(struct ast_channel *chan, char *file, char *digits)
-{
- int res = -1;
- if (!ast_streamfile(chan, file, chan->language)) {
- res = ast_waitstream(chan, digits);
- }
- return res;
-}
-
-static int dictate_exec(struct ast_channel *chan, void *data)
-{
- char *path = NULL, filein[256], *filename = "";
- char *parse;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(base);
- AST_APP_ARG(filename);
- );
- char dftbase[256];
- char *base;
- struct ast_flags flags = {0};
- struct ast_filestream *fs;
- struct ast_frame *f = NULL;
- int ffactor = 320 * 80,
- res = 0,
- done = 0,
- oldr = 0,
- lastop = 0,
- samples = 0,
- speed = 1,
- digit = 0,
- len = 0,
- maxlen = 0,
- mode = 0;
-
- snprintf(dftbase, sizeof(dftbase), "%s/dictate", ast_config_AST_SPOOL_DIR);
- if (!ast_strlen_zero(data)) {
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
- } else
- args.argc = 0;
-
- if (args.argc && !ast_strlen_zero(args.base)) {
- base = args.base;
- } else {
- base = dftbase;
- }
- if (args.argc > 1 && args.filename) {
- filename = args.filename;
- }
- oldr = chan->readformat;
- if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR)) < 0) {
- ast_log(LOG_WARNING, "Unable to set to linear mode.\n");
- return -1;
- }
-
- ast_answer(chan);
- ast_safe_sleep(chan, 200);
- for (res = 0; !res;) {
- if (ast_strlen_zero(filename)) {
- if (ast_app_getdata(chan, "dictate/enter_filename", filein, sizeof(filein), 0) ||
- ast_strlen_zero(filein)) {
- res = -1;
- break;
- }
- } else {
- ast_copy_string(filein, filename, sizeof(filein));
- filename = "";
- }
- ast_mkdir(base, 0755);
- len = strlen(base) + strlen(filein) + 2;
- if (!path || len > maxlen) {
- path = alloca(len);
- memset(path, 0, len);
- maxlen = len;
- } else {
- memset(path, 0, maxlen);
- }
-
- snprintf(path, len, "%s/%s", base, filein);
- fs = ast_writefile(path, "raw", NULL, O_CREAT|O_APPEND, 0, AST_FILE_MODE);
- mode = DMODE_PLAY;
- memset(&flags, 0, sizeof(flags));
- ast_set_flag(&flags, DFLAG_PAUSE);
- digit = play_and_wait(chan, "dictate/forhelp", AST_DIGIT_ANY);
- done = 0;
- speed = 1;
- res = 0;
- lastop = 0;
- samples = 0;
- while (!done && ((res = ast_waitfor(chan, -1)) > -1) && fs && (f = ast_read(chan))) {
- if (digit) {
- struct ast_frame fr = {AST_FRAME_DTMF, digit};
- ast_queue_frame(chan, &fr);
- digit = 0;
- }
- if ((f->frametype == AST_FRAME_DTMF)) {
- int got = 1;
- switch(mode) {
- case DMODE_PLAY:
- switch(f->subclass) {
- case '1':
- ast_set_flag(&flags, DFLAG_PAUSE);
- mode = DMODE_RECORD;
- break;
- case '2':
- speed++;
- if (speed > 4) {
- speed = 1;
- }
- res = ast_say_number(chan, speed, AST_DIGIT_ANY, chan->language, NULL);
- break;
- case '7':
- samples -= ffactor;
- if(samples < 0) {
- samples = 0;
- }
- ast_seekstream(fs, samples, SEEK_SET);
- break;
- case '8':
- samples += ffactor;
- ast_seekstream(fs, samples, SEEK_SET);
- break;
-
- default:
- got = 0;
- }
- break;
- case DMODE_RECORD:
- switch(f->subclass) {
- case '1':
- ast_set_flag(&flags, DFLAG_PAUSE);
- mode = DMODE_PLAY;
- break;
- case '8':
- ast_toggle_flag(&flags, DFLAG_TRUNC);
- lastop = 0;
- break;
- default:
- got = 0;
- }
- break;
- default:
- got = 0;
- }
- if (!got) {
- switch(f->subclass) {
- case '#':
- done = 1;
- continue;
- break;
- case '*':
- ast_toggle_flag(&flags, DFLAG_PAUSE);
- if (ast_test_flag(&flags, DFLAG_PAUSE)) {
- digit = play_and_wait(chan, "dictate/pause", AST_DIGIT_ANY);
- } else {
- digit = play_and_wait(chan, mode == DMODE_PLAY ? "dictate/playback" : "dictate/record", AST_DIGIT_ANY);
- }
- break;
- case '0':
- ast_set_flag(&flags, DFLAG_PAUSE);
- digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
- switch(mode) {
- case DMODE_PLAY:
- digit = play_and_wait(chan, "dictate/play_help", AST_DIGIT_ANY);
- break;
- case DMODE_RECORD:
- digit = play_and_wait(chan, "dictate/record_help", AST_DIGIT_ANY);
- break;
- }
- if (digit == 0) {
- digit = play_and_wait(chan, "dictate/both_help", AST_DIGIT_ANY);
- } else if (digit < 0) {
- done = 1;
- break;
- }
- break;
- }
- }
-
- } else if (f->frametype == AST_FRAME_VOICE) {
- switch(mode) {
- struct ast_frame *fr;
- int x;
- case DMODE_PLAY:
- if (lastop != DMODE_PLAY) {
- if (ast_test_flag(&flags, DFLAG_PAUSE)) {
- digit = play_and_wait(chan, "dictate/playback_mode", AST_DIGIT_ANY);
- if (digit == 0) {
- digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
- } else if (digit < 0) {
- break;
- }
- }
- if (lastop != DFLAG_PLAY) {
- lastop = DFLAG_PLAY;
- ast_closestream(fs);
- if (!(fs = ast_openstream(chan, path, chan->language)))
- break;
- ast_seekstream(fs, samples, SEEK_SET);
- chan->stream = NULL;
- }
- lastop = DMODE_PLAY;
- }
-
- if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
- for (x = 0; x < speed; x++) {
- if ((fr = ast_readframe(fs))) {
- ast_write(chan, fr);
- samples += fr->samples;
- ast_frfree(fr);
- fr = NULL;
- } else {
- samples = 0;
- ast_seekstream(fs, 0, SEEK_SET);
- }
- }
- }
- break;
- case DMODE_RECORD:
- if (lastop != DMODE_RECORD) {
- int oflags = O_CREAT | O_WRONLY;
- if (ast_test_flag(&flags, DFLAG_PAUSE)) {
- digit = play_and_wait(chan, "dictate/record_mode", AST_DIGIT_ANY);
- if (digit == 0) {
- digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
- } else if (digit < 0) {
- break;
- }
- }
- lastop = DMODE_RECORD;
- ast_closestream(fs);
- if ( ast_test_flag(&flags, DFLAG_TRUNC)) {
- oflags |= O_TRUNC;
- digit = play_and_wait(chan, "dictate/truncating_audio", AST_DIGIT_ANY);
- } else {
- oflags |= O_APPEND;
- }
- fs = ast_writefile(path, "raw", NULL, oflags, 0, AST_FILE_MODE);
- if (ast_test_flag(&flags, DFLAG_TRUNC)) {
- ast_seekstream(fs, 0, SEEK_SET);
- ast_clear_flag(&flags, DFLAG_TRUNC);
- } else {
- ast_seekstream(fs, 0, SEEK_END);
- }
- }
- if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
- res = ast_writestream(fs, f);
- }
- break;
- }
-
- }
-
- ast_frfree(f);
- }
- }
- if (oldr) {
- ast_set_read_format(chan, oldr);
- }
- return 0;
-}
-
-static int unload_module(void)
-{
- int res;
- res = ast_unregister_application(app);
- return res;
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, dictate_exec, synopsis, desc);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Virtual Dictation Machine");
diff --git a/trunk/apps/app_directed_pickup.c b/trunk/apps/app_directed_pickup.c
deleted file mode 100644
index 07d81c869..000000000
--- a/trunk/apps/app_directed_pickup.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2005, Joshua Colp
- *
- * Joshua Colp <jcolp@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Directed Call Pickup Support
- *
- * \author Joshua Colp <jcolp@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-#include "asterisk/app.h"
-#include "asterisk/features.h"
-
-#define PICKUPMARK "PICKUPMARK"
-
-static const char *app = "Pickup";
-static const char *synopsis = "Directed Call Pickup";
-static const char *descrip =
-" Pickup([extension[@context][&extension2@[context]...]]): This application can\n"
-"pickup any ringing channel that is calling the specified extension. If no\n"
-"context is specified, the current context will be used. If you use the special\n"
-"string \"PICKUPMARK\" for the context parameter, for example 10@PICKUPMARK,\n"
-"this application tries to find a channel which has defined a ${PICKUPMARK}\n"
-"channel variable with the same value as \"extension\" (in this example, \"10\").\n"
-"When no parameter is specified, the application will pickup a channel matching\n"
-"the pickup group of the active channel.";
-
-/* Perform actual pickup between two channels */
-static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
-{
- int res = 0;
-
- ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name);
-
- if ((res = ast_answer(chan))) {
- ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
- return -1;
- }
-
- if ((res = ast_queue_control(chan, AST_CONTROL_ANSWER))) {
- ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
- return -1;
- }
-
- if ((res = ast_channel_masquerade(target, chan))) {
- ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
- return -1;
- }
-
- return res;
-}
-
-/* Helper function that determines whether a channel is capable of being picked up */
-static int can_pickup(struct ast_channel *chan)
-{
- if (!chan->pbx && (chan->_state == AST_STATE_RINGING || chan->_state == AST_STATE_RING))
- return 1;
- else
- return 0;
-}
-
-/* Attempt to pick up specified extension with context */
-static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context)
-{
- int res = -1;
- struct ast_channel *target = NULL;
-
- while ((target = ast_channel_walk_locked(target))) {
- if ((!strcasecmp(target->macroexten, exten) || !strcasecmp(target->exten, exten)) &&
- !strcasecmp(target->dialcontext, context) &&
- can_pickup(target)) {
- res = pickup_do(chan, target);
- ast_channel_unlock(target);
- break;
- }
- ast_channel_unlock(target);
- }
-
- return res;
-}
-
-/* Attempt to pick up specified mark */
-static int pickup_by_mark(struct ast_channel *chan, const char *mark)
-{
- int res = -1;
- const char *tmp = NULL;
- struct ast_channel *target = NULL;
-
- while ((target = ast_channel_walk_locked(target))) {
- if ((tmp = pbx_builtin_getvar_helper(target, PICKUPMARK)) &&
- !strcasecmp(tmp, mark) &&
- can_pickup(target)) {
- res = pickup_do(chan, target);
- ast_channel_unlock(target);
- break;
- }
- ast_channel_unlock(target);
- }
-
- return res;
-}
-
-/* Main application entry point */
-static int pickup_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char *tmp = ast_strdupa(data);
- char *exten = NULL, *context = NULL;
-
- if (ast_strlen_zero(data)) {
- res = ast_pickup_call(chan);
- return res;
- }
-
- /* Parse extension (and context if there) */
- while (!ast_strlen_zero(tmp) && (exten = strsep(&tmp, "&"))) {
- if ((context = strchr(exten, '@')))
- *context++ = '\0';
- if (!ast_strlen_zero(context) && !strcasecmp(context, PICKUPMARK)) {
- if (!pickup_by_mark(chan, exten))
- break;
- } else {
- if (!pickup_by_exten(chan, exten, !ast_strlen_zero(context) ? context : chan->context))
- break;
- }
- ast_log(LOG_NOTICE, "No target channel found for %s.\n", exten);
- }
-
- return res;
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(app);
-
- return res;
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, pickup_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application");
diff --git a/trunk/apps/app_directory.c b/trunk/apps/app_directory.c
deleted file mode 100644
index 08d5cf842..000000000
--- a/trunk/apps/app_directory.c
+++ /dev/null
@@ -1,842 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Provide a directory of extensions
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <ctype.h>
-
-#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
-#include "asterisk/file.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/say.h"
-#include "asterisk/app.h"
-
-#ifdef ODBC_STORAGE
-#include <sys/mman.h>
-#include "asterisk/res_odbc.h"
-
-static char odbc_database[80] = "asterisk";
-static char odbc_table[80] = "voicemessages";
-static char vmfmts[80] = "wav";
-#endif
-
-static char *app = "Directory";
-
-static char *synopsis = "Provide directory of voicemail extensions";
-static char *descrip =
-" Directory(vm-context[,dial-context[,options]]): This application will present\n"
-"the calling channel with a directory of extensions from which they can search\n"
-"by name. The list of names and corresponding extensions is retrieved from the\n"
-"voicemail configuration file, voicemail.conf.\n"
-" This application will immediately exit if one of the following DTMF digits are\n"
-"received and the extension to jump to exists:\n"
-" 0 - Jump to the 'o' extension, if it exists.\n"
-" * - Jump to the 'a' extension, if it exists.\n\n"
-" Parameters:\n"
-" vm-context - This is the context within voicemail.conf to use for the\n"
-" Directory.\n"
-" dial-context - This is the dialplan context to use when looking for an\n"
-" extension that the user has selected, or when jumping to the\n"
-" 'o' or 'a' extension.\n\n"
-" Options:\n"
-" e - In addition to the name, also read the extension number to the\n"
-" caller before presenting dialing options.\n"
-" f - Allow the caller to enter the first name of a user in the directory\n"
-" instead of using the last name.\n"
-" m - Instead of reading each name sequentially and asking for confirmation,\n"
-" create a menu of up to 8 names.\n";
-
-/* For simplicity, I'm keeping the format compatible with the voicemail config,
- but i'm open to suggestions for isolating it */
-
-#define VOICEMAIL_CONFIG "voicemail.conf"
-
-/* How many digits to read in */
-#define NUMDIGITS 3
-
-enum {
- OPT_LISTBYFIRSTNAME = (1 << 0),
- OPT_SAYEXTENSION = (1 << 1),
- OPT_FROMVOICEMAIL = (1 << 2),
- OPT_SELECTFROMMENU = (1 << 3),
-} directory_option_flags;
-
-struct directory_item {
- char exten[AST_MAX_EXTENSION + 1];
- char name[AST_MAX_EXTENSION + 1];
- char key[50]; /* Text to order items. Either lastname+firstname or firstname+lastname */
-
- AST_LIST_ENTRY(directory_item) entry;
-};
-
-AST_APP_OPTIONS(directory_app_options, {
- AST_APP_OPTION('f', OPT_LISTBYFIRSTNAME),
- AST_APP_OPTION('e', OPT_SAYEXTENSION),
- AST_APP_OPTION('v', OPT_FROMVOICEMAIL),
- AST_APP_OPTION('m', OPT_SELECTFROMMENU),
-});
-
-#ifdef ODBC_STORAGE
-struct generic_prepare_struct {
- const char *sql;
- const char *param;
-};
-
-static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
-{
- struct generic_prepare_struct *gps = data;
- SQLHSTMT stmt;
- int res;
-
- res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
- return NULL;
- }
-
- res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *)gps->sql);
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- return NULL;
- }
-
- if (!ast_strlen_zero(gps->param))
- SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->param), 0, (void *)gps->param, 0, NULL);
-
- return stmt;
-}
-
-static void retrieve_file(char *dir)
-{
- int x = 0;
- int res;
- int fd=-1;
- size_t fdlen = 0;
- void *fdm = MAP_FAILED;
- SQLHSTMT stmt;
- char sql[256];
- char fmt[80]="", empty[10] = "";
- char *c;
- SQLLEN colsize;
- char full_fn[256];
- struct odbc_obj *obj;
- struct generic_prepare_struct gps = { .sql = sql, .param = dir };
-
- obj = ast_odbc_request_obj(odbc_database, 1);
- if (obj) {
- do {
- ast_copy_string(fmt, vmfmts, sizeof(fmt));
- c = strchr(fmt, '|');
- if (c)
- *c = '\0';
- if (!strcasecmp(fmt, "wav49"))
- strcpy(fmt, "WAV");
- snprintf(full_fn, sizeof(full_fn), "%s.%s", dir, fmt);
- snprintf(sql, sizeof(sql), "SELECT recording FROM %s WHERE dir=? AND msgnum=-1", odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-
- if (!stmt) {
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- break;
- }
- res = SQLFetch(stmt);
- if (res == SQL_NO_DATA) {
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- break;
- } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- break;
- }
- fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, AST_FILE_MODE);
- if (fd < 0) {
- ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- break;
- }
-
- res = SQLGetData(stmt, 1, SQL_BINARY, empty, 0, &colsize);
- fdlen = colsize;
- if (fd > -1) {
- char tmp[1]="";
- lseek(fd, fdlen - 1, SEEK_SET);
- if (write(fd, tmp, 1) != 1) {
- close(fd);
- fd = -1;
- break;
- }
- if (fd > -1)
- fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- }
- if (fdm != MAP_FAILED) {
- memset(fdm, 0, fdlen);
- res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- break;
- }
- }
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- } while (0);
- ast_odbc_release_obj(obj);
- } else
- ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
- if (fdm != MAP_FAILED)
- munmap(fdm, fdlen);
- if (fd > -1)
- close(fd);
- return;
-}
-#endif
-
-static int compare(const char *text, const char *template)
-{
- char digit;
-
- while (*template) {
- digit = toupper(*text++);
- switch (digit) {
- case 0:
- return -1;
- case '1':
- digit = '1';
- break;
- case '2':
- case 'A':
- case 'B':
- case 'C':
- digit = '2';
- break;
- case '3':
- case 'D':
- case 'E':
- case 'F':
- digit = '3';
- break;
- case '4':
- case 'G':
- case 'H':
- case 'I':
- digit = '4';
- break;
- case '5':
- case 'J':
- case 'K':
- case 'L':
- digit = '5';
- break;
- case '6':
- case 'M':
- case 'N':
- case 'O':
- digit = '6';
- break;
- case '7':
- case 'P':
- case 'Q':
- case 'R':
- case 'S':
- digit = '7';
- break;
- case '8':
- case 'T':
- case 'U':
- case 'V':
- digit = '8';
- break;
- case '9':
- case 'W':
- case 'X':
- case 'Y':
- case 'Z':
- digit = '9';
- break;
-
- default:
- if (digit > ' ')
- return -1;
- continue;
- }
-
- if (*template++ != digit)
- return -1;
- }
-
- return 0;
-}
-
-/* play name of mailbox owner.
- * returns: -1 for bad or missing extension
- * '1' for selected entry from directory
- * '*' for skipped entry from directory
- */
-static int play_mailbox_owner(struct ast_channel *chan, const char *context,
- const char *ext, const char *name, struct ast_flags *flags)
-{
- int res = 0;
- char fn[256];
-
- /* Check for the VoiceMail2 greeting first */
- snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
- ast_config_AST_SPOOL_DIR, context, ext);
-#ifdef ODBC_STORAGE
- retrieve_file(fn);
-#endif
-
- if (ast_fileexists(fn, NULL, chan->language) <= 0) {
- /* no file, check for an old-style Voicemail greeting */
- snprintf(fn, sizeof(fn), "%s/vm/%s/greet",
- ast_config_AST_SPOOL_DIR, ext);
- }
-#ifdef ODBC_STORAGE
- retrieve_file(fn);
-#endif
-
- if (ast_fileexists(fn, NULL, chan->language) > 0) {
- res = ast_stream_and_wait(chan, fn, AST_DIGIT_ANY);
- ast_stopstream(chan);
- /* If Option 'e' was specified, also read the extension number with the name */
- if (ast_test_flag(flags, OPT_SAYEXTENSION)) {
- ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
- res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
- }
- } else {
- res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, chan->language);
- if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) {
- ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
- res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
- }
- }
-#ifdef ODBC_STORAGE
- ast_filedelete(fn, NULL);
-#endif
-
- return res;
-}
-
-static int select_entry(struct ast_channel *chan, const char *context, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags)
-{
- ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, dialcontext);
-
- if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
- /* We still want to set the exten though */
- ast_copy_string(chan->exten, item->exten, sizeof(chan->exten));
- } else if (ast_goto_if_exists(chan, dialcontext, item->exten, 1)) {
- ast_log(LOG_WARNING,
- "Can't find extension '%s' in context '%s'. "
- "Did you pass the wrong context to Directory?\n",
- item->exten, dialcontext);
- return -1;
- }
-
- return 0;
-}
-
-static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
-{
- struct directory_item *item, **ptr;
- int i, res, loop;
-
- for (ptr = items, i = 0; i < count; i++, ptr++) {
- item = *ptr;
-
- for (loop = 3 ; loop > 0; loop--) {
- res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
-
- if (!res)
- res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
- if (!res)
- res = ast_waitfordigit(chan, 3000);
- ast_stopstream(chan);
-
- if (res == '1') { /* Name selected */
- return select_entry(chan, context, dialcontext, item, flags) ? -1 : 1;
- } else if (res == '*') {
- /* Skip to next match in list */
- break;
- }
-
- if (res < 0)
- return -1;
-
- res = 0;
- }
- }
-
- /* Nothing was selected */
- return 0;
-}
-
-static int select_item_menu(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
-{
- struct directory_item **block, *item;
- int i, limit, res = 0;
- char buf[9];
-
- for (block = items; count; block += limit, count -= limit) {
- limit = count;
- if (limit > 8)
- limit = 8;
-
- for (i = 0; i < limit && !res; i++) {
- item = block[i];
-
- snprintf(buf, sizeof(buf), "digits/%d", i + 1);
- /* Press <num> for <name>, [ extension <ext> ] */
- res = ast_streamfile(chan, "dir-multi1", chan->language);
- if (!res)
- res = ast_waitstream(chan, AST_DIGIT_ANY);
- if (!res)
- res = ast_streamfile(chan, buf, chan->language);
- if (!res)
- res = ast_waitstream(chan, AST_DIGIT_ANY);
- if (!res)
- res = ast_streamfile(chan, "dir-multi2", chan->language);
- if (!res)
- res = ast_waitstream(chan, AST_DIGIT_ANY);
- if (!res)
- res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
- if (!res)
- res = ast_waitstream(chan, AST_DIGIT_ANY);
- if (!res)
- res = ast_waitfordigit(chan, 800);
- }
-
- /* Press "9" for more names. */
- if (!res && count > limit) {
- res = ast_streamfile(chan, "dir-multi9", chan->language);
- if (!res)
- res = ast_waitstream(chan, AST_DIGIT_ANY);
- }
-
- if (!res) {
- res = ast_waitfordigit(chan, 3000);
- }
-
- if (res && res > '0' && res < '1' + limit) {
- return select_entry(chan, context, dialcontext, block[res - '1'], flags) ? -1 : 1;
- }
-
- if (res < 0)
- return -1;
-
- res = 0;
- }
-
- /* Nothing was selected */
- return 0;
-}
-
-static struct ast_config *realtime_directory(char *context)
-{
- struct ast_config *cfg;
- struct ast_config *rtdata;
- struct ast_category *cat;
- struct ast_variable *var;
- char *mailbox;
- const char *fullname;
- const char *hidefromdir;
- char tmp[100];
- struct ast_flags config_flags = { 0 };
-
- /* Load flat file config. */
- cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
-
- if (!cfg) {
- /* Loading config failed. */
- ast_log(LOG_WARNING, "Loading config failed.\n");
- return NULL;
- }
-
- /* Get realtime entries, categorized by their mailbox number
- and present in the requested context */
- rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, NULL);
-
- /* if there are no results, just return the entries from the config file */
- if (!rtdata)
- return cfg;
-
- /* Does the context exist within the config file? If not, make one */
- cat = ast_category_get(cfg, context);
- if (!cat) {
- cat = ast_category_new(context, "", 99999);
- if (!cat) {
- ast_log(LOG_WARNING, "Out of memory\n");
- ast_config_destroy(cfg);
- return NULL;
- }
- ast_category_append(cfg, cat);
- }
-
- mailbox = NULL;
- while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) {
- fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
- hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
- snprintf(tmp, sizeof(tmp), "no-password,%s,hidefromdir=%s",
- fullname ? fullname : "",
- hidefromdir ? hidefromdir : "no");
- var = ast_variable_new(mailbox, tmp, "");
- if (var)
- ast_variable_append(cat, var);
- else
- ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
- }
- ast_config_destroy(rtdata);
-
- return cfg;
-}
-
-static int check_match(struct directory_item **result, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name)
-{
- struct directory_item *item;
- const char *key = NULL;
- int namelen;
-
-
- /* Set key to last name or first name depending on search mode */
- if (!use_first_name)
- key = strchr(item_fullname, ' ');
-
- if (key)
- key++;
- else
- key = item_fullname;
-
- if (compare(key, pattern_ext))
- return 0;
-
- /* Match */
- item = ast_calloc(1, sizeof(*item));
- if (!item)
- return -1;
- ast_copy_string(item->name, item_fullname, sizeof(item->name));
- ast_copy_string(item->exten, item_ext, sizeof(item->exten));
-
- ast_copy_string(item->key, key, sizeof(item->key));
- if (key != item_fullname) {
- /* Key is the last name. Append first name to key in order to sort Last,First */
- namelen = key - item_fullname - 1;
- if (namelen > sizeof(item->key) - strlen(item->key) - 1)
- namelen = sizeof(item->key) - strlen(item->key) - 1;
- strncat(item->key, item_fullname, namelen);
- }
-
- *result = item;
- return 1;
-}
-
-typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist;
-
-static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, int use_first_name, itemlist *alist)
-{
- struct ast_variable *v;
- char buf[AST_MAX_EXTENSION + 1], *pos, *bufptr, *cat;
- struct directory_item *item;
- int res;
-
- ast_debug(2, "Pattern: %s\n", ext);
-
- for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
-
- /* Ignore hidden */
- if (strcasestr(v->value, "hidefromdir=yes"))
- continue;
-
- ast_copy_string(buf, v->value, sizeof(buf));
- bufptr = buf;
-
- /* password,Full Name,email,pager,options */
- strsep(&bufptr, ",");
- pos = strsep(&bufptr, ",");
-
- res = check_match(&item, pos, v->name, ext, use_first_name);
- if (!res)
- continue;
- else if (res < 0)
- return -1;
-
- AST_LIST_INSERT_TAIL(alist, item, entry);
- }
-
-
- if (ucfg) {
- for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
- const char *pos;
- if (!strcasecmp(cat, "general"))
- continue;
- if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
- continue;
-
- /* Find all candidate extensions */
- pos = ast_variable_retrieve(ucfg, cat, "fullname");
- if (!pos)
- continue;
-
- res = check_match(&item, pos, cat, ext, use_first_name);
- if (!res)
- continue;
- else if (res < 0)
- return -1;
-
- AST_LIST_INSERT_TAIL(alist, item, entry);
- }
- }
-
- return 0;
-}
-
-static void sort_items(struct directory_item **sorted, int count)
-{
- int reordered, i;
- struct directory_item **ptr, *tmp;
-
- if (count < 2)
- return;
-
- /* Bubble-sort items by the key */
- do {
- reordered = 0;
- for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
- if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
- tmp = ptr[0];
- ptr[0] = ptr[1];
- ptr[1] = tmp;
- reordered++;
- }
- }
- } while (reordered);
-}
-
-static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
-{
- if (!ast_goto_if_exists(chan, dialcontext, ext, 1) ||
- (!ast_strlen_zero(chan->macrocontext) &&
- !ast_goto_if_exists(chan, chan->macrocontext, ext, 1))) {
- return 0;
- } else {
- ast_log(LOG_WARNING, "Can't find extension '%s' in current context. "
- "Not Exiting the Directory!\n", ext);
- return -1;
- }
-}
-
-static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, struct ast_flags *flags)
-{
- /* Read in the first three digits.. "digit" is the first digit, already read */
- int res = 0;
- itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
- struct directory_item *item, **ptr, **sorted = NULL;
- int count, i;
- char ext[NUMDIGITS + 1] = "";
-
- if (ast_strlen_zero(context)) {
- ast_log(LOG_WARNING,
- "Directory must be called with an argument "
- "(context in which to interpret extensions)\n");
- return -1;
- }
-
- if (digit == '0' && !goto_exten(chan, dialcontext, "o")) {
- return 0;
- }
-
- if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
- return 0;
- }
-
- ext[0] = digit;
- if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0)
- return -1;
-
- res = search_directory(context, vmcfg, ucfg, ext, ast_test_flag(flags, OPT_LISTBYFIRSTNAME), &alist);
- if (res)
- goto exit;
-
- /* Count items in the list */
- count = 0;
- AST_LIST_TRAVERSE(&alist, item, entry) {
- count++;
- }
-
- if (count < 1) {
- res = ast_streamfile(chan, "dir-nomatch", chan->language);
- goto exit;
- }
-
-
- /* Create plain array of pointers to items (for sorting) */
- sorted = ast_calloc(count, sizeof(*sorted));
-
- ptr = sorted;
- AST_LIST_TRAVERSE(&alist, item, entry) {
- *ptr++ = item;
- }
-
- /* Sort items */
- sort_items(sorted, count);
-
- if (option_debug) {
- ast_debug(2, "Listing matching entries:\n");
- for (ptr = sorted, i = 0; i < count; i++, ptr++) {
- ast_log(LOG_DEBUG, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
- }
- }
-
- if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
- /* Offer multiple entries at the same time */
- res = select_item_menu(chan, sorted, count, context, dialcontext, flags);
- } else {
- /* Offer entries one by one */
- res = select_item_seq(chan, sorted, count, context, dialcontext, flags);
- }
-
- if (!res) {
- res = ast_streamfile(chan, "dir-nomore", chan->language);
- }
-
-exit:
- if (sorted)
- ast_free(sorted);
-
- while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
- ast_free(item);
-
- return res;
-}
-
-static int directory_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_config *cfg, *ucfg;
- const char *dirintro;
- char *parse, *opts[0];
- struct ast_flags flags = { 0 };
- struct ast_flags config_flags = { 0 };
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(vmcontext);
- AST_APP_ARG(dialcontext);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
- return -1;
-
- if (ast_strlen_zero(args.dialcontext))
- args.dialcontext = args.vmcontext;
-
- cfg = realtime_directory(args.vmcontext);
- if (!cfg) {
- ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
- return -1;
- }
-
- ucfg = ast_config_load("users.conf", config_flags);
-
- dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
- if (ast_strlen_zero(dirintro))
- dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
- if (ast_strlen_zero(dirintro))
- dirintro = ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) ? "dir-intro-fn" : "dir-intro";
-
- if (chan->_state != AST_STATE_UP)
- res = ast_answer(chan);
-
- for (;;) {
- if (!res)
- res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
- ast_stopstream(chan);
- if (!res)
- res = ast_waitfordigit(chan, 5000);
-
- if (res <= 0)
- break;
-
- res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, &flags);
- if (res)
- break;
-
- res = ast_waitstream(chan, AST_DIGIT_ANY);
- ast_stopstream(chan);
-
- if (res)
- break;
- }
-
- if (ucfg)
- ast_config_destroy(ucfg);
- ast_config_destroy(cfg);
-
- return res < 0 ? -1 : 0;
-}
-
-static int unload_module(void)
-{
- int res;
- res = ast_unregister_application(app);
- return res;
-}
-
-static int load_module(void)
-{
-#ifdef ODBC_STORAGE
- struct ast_flags config_flags = { 0 };
- struct ast_config *cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
- const char *tmp;
-
- if (cfg) {
- if ((tmp = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
- ast_copy_string(odbc_database, tmp, sizeof(odbc_database));
- }
- if ((tmp = ast_variable_retrieve(cfg, "general", "odbctable"))) {
- ast_copy_string(odbc_table, tmp, sizeof(odbc_table));
- }
- if ((tmp = ast_variable_retrieve(cfg, "general", "format"))) {
- ast_copy_string(vmfmts, tmp, sizeof(vmfmts));
- }
- ast_config_destroy(cfg);
- } else
- ast_log(LOG_WARNING, "Unable to load " VOICEMAIL_CONFIG " - ODBC defaults will be used\n");
-#endif
-
- return ast_register_application(app, directory_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Directory");
diff --git a/trunk/apps/app_disa.c b/trunk/apps/app_disa.c
deleted file mode 100644
index ac74c78a4..000000000
--- a/trunk/apps/app_disa.c
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- *
- * Made only slightly more sane by Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief DISA -- Direct Inward System Access Application
- *
- * \author Jim Dixon <jim@lambdatel.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <math.h>
-#include <sys/time.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/app.h"
-#include "asterisk/indications.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/ulaw.h"
-#include "asterisk/callerid.h"
-#include "asterisk/stringfields.h"
-
-static char *app = "DISA";
-
-static char *synopsis = "DISA (Direct Inward System Access)";
-
-static char *descrip =
-"DISA(<numeric passcode>[,<context>[,<cid>[,mailbox[,options]]]]) or\n"
-"DISA(<filename>[,,,,options])\n"
-"The DISA, Direct Inward System Access, application allows someone from \n"
-"outside the telephone switch (PBX) to obtain an \"internal\" system \n"
-"dialtone and to place calls from it as if they were placing a call from \n"
-"within the switch.\n"
-"DISA plays a dialtone. The user enters their numeric passcode, followed by\n"
-"the pound sign (#). If the passcode is correct, the user is then given\n"
-"system dialtone within <context> on which a call may be placed. If the user\n"
-"enters an invalid extension and extension \"i\" exists in the specified\n"
-"context, it will be used.\n"
-"\n"
-"If you need to present a DISA dialtone without entering a password, simply\n"
-"set <passcode> to \"no-password\".\n"
-"\n"
-"Be aware that using this may compromise the security of your PBX.\n"
-"\n"
-"The arguments to this application (in extensions.conf) allow either\n"
-"specification of a single global passcode (that everyone uses), or\n"
-"individual passcodes contained in a file.\n"
-"\n"
-"The file that contains the passcodes (if used) allows a complete\n"
-"specification of all of the same arguments available on the command\n"
-"line, with the sole exception of the options. The file may contain blank\n"
-"lines, or comments starting with \"#\" or \";\".\n"
-"\n"
-"<context> specifies the dialplan context in which the user-entered extension\n"
-"will be matched. If no context is specified, the DISA application defaults\n"
-"the context to \"disa\". Presumably a normal system will have a special\n"
-"context set up for DISA use with some or a lot of restrictions.\n"
-"\n"
-"<cid> specifies a new (different) callerid to be used for this call.\n"
-"\n"
-"<mailbox[@context]> will cause a stutter-dialtone (indication \"dialrecall\")\n"
-"to be used, if the specified mailbox contains any new messages.\n"
-"\n"
-"The following options are available:\n"
-" n - the DISA application will not answer initially.\n"
-" p - the extension entered will be considered complete when a '#' is entered.\n";
-
-enum {
- NOANSWER_FLAG = (1 << 0),
- POUND_TO_END_FLAG = (1 << 1),
-} option_flags;
-
-AST_APP_OPTIONS(app_opts, {
- AST_APP_OPTION('n', NOANSWER_FLAG),
- AST_APP_OPTION('p', POUND_TO_END_FLAG),
-});
-
-static void play_dialtone(struct ast_channel *chan, char *mailbox)
-{
- const struct ind_tone_zone_sound *ts = NULL;
- if(ast_app_has_voicemail(mailbox, NULL))
- ts = ast_get_indication_tone(chan->zone, "dialrecall");
- else
- ts = ast_get_indication_tone(chan->zone, "dial");
- if (ts)
- ast_playtones_start(chan, 0, ts->data, 0);
- else
- ast_tonepair_start(chan, 350, 440, 0, 0);
-}
-
-static int disa_exec(struct ast_channel *chan, void *data)
-{
- int i = 0, j, k = 0, did_ignore = 0, special_noanswer = 0;
- int firstdigittimeout = (chan->pbx ? chan->pbx->rtimeout * 1000 : 20000);
- int digittimeout = (chan->pbx ? chan->pbx->dtimeout * 1000 : 10000);
- struct ast_flags flags;
- char *tmp, exten[AST_MAX_EXTENSION] = "", acctcode[20]="";
- char pwline[256];
- char ourcidname[256],ourcidnum[256];
- struct ast_frame *f;
- struct timeval lastdigittime;
- int res;
- FILE *fp;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(passcode);
- AST_APP_ARG(context);
- AST_APP_ARG(cid);
- AST_APP_ARG(mailbox);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
- return -1;
- }
-
- ast_debug(1, "Digittimeout: %d\n", digittimeout);
- ast_debug(1, "Responsetimeout: %d\n", firstdigittimeout);
-
- tmp = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, tmp);
-
- if (ast_strlen_zero(args.context))
- args.context = "disa";
- if (ast_strlen_zero(args.mailbox))
- args.mailbox = "";
- if (!ast_strlen_zero(args.options))
- ast_app_parse_options(app_opts, &flags, NULL, args.options);
-
- ast_debug(1, "Mailbox: %s\n",args.mailbox);
-
- if (!ast_test_flag(&flags, NOANSWER_FLAG)) {
- if (chan->_state != AST_STATE_UP) {
- /* answer */
- ast_answer(chan);
- }
- } else
- special_noanswer = 1;
-
- ast_debug(1, "Context: %s\n",args.context);
-
- if (!strcasecmp(args.passcode, "no-password")) {
- k |= 1; /* We have the password */
- ast_debug(1, "DISA no-password login success\n");
- }
-
- lastdigittime = ast_tvnow();
-
- play_dialtone(chan, args.mailbox);
-
- ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
-
- for (;;) {
- /* if outa time, give em reorder */
- if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((k&2) ? digittimeout : firstdigittimeout)) {
- ast_debug(1,"DISA %s entry timeout on chan %s\n",
- ((k&1) ? "extension" : "password"),chan->name);
- break;
- }
-
- if ((res = ast_waitfor(chan, -1) < 0)) {
- ast_debug(1, "Waitfor returned %d\n", res);
- continue;
- }
-
- if (!(f = ast_read(chan))) {
- ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
- return -1;
- }
-
- if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
- ast_frfree(f);
- ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
- return -1;
- }
-
- /* If the frame coming in is not DTMF, just drop it and continue */
- if (f->frametype != AST_FRAME_DTMF) {
- ast_frfree(f);
- continue;
- }
-
- j = f->subclass; /* save digit */
- ast_frfree(f);
-
- if (!i) {
- k |= 2; /* We have the first digit */
- ast_playtones_stop(chan);
- }
-
- lastdigittime = ast_tvnow();
-
- /* got a DTMF tone */
- if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
- if (!(k&1)) { /* if in password state */
- if (j == '#') { /* end of password */
- /* see if this is an integer */
- if (sscanf(args.passcode,"%d",&j) < 1) { /* nope, it must be a filename */
- fp = fopen(args.passcode,"r");
- if (!fp) {
- ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
- ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
- return -1;
- }
- pwline[0] = 0;
- while(fgets(pwline,sizeof(pwline) - 1,fp)) {
- if (!pwline[0])
- continue;
- if (pwline[strlen(pwline) - 1] == '\n')
- pwline[strlen(pwline) - 1] = 0;
- if (!pwline[0])
- continue;
- /* skip comments */
- if (pwline[0] == '#')
- continue;
- if (pwline[0] == ';')
- continue;
-
- AST_STANDARD_APP_ARGS(args, pwline);
-
- ast_debug(1, "Mailbox: %s\n",args.mailbox);
-
- /* password must be in valid format (numeric) */
- if (sscanf(args.passcode,"%d", &j) < 1)
- continue;
- /* if we got it */
- if (!strcmp(exten,args.passcode)) {
- if (ast_strlen_zero(args.context))
- args.context = "disa";
- if (ast_strlen_zero(args.mailbox))
- args.mailbox = "";
- break;
- }
- }
- fclose(fp);
- }
- /* compare the two */
- if (strcmp(exten,args.passcode)) {
- ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
- goto reorder;
-
- }
- /* password good, set to dial state */
- ast_debug(1,"DISA on chan %s password is good\n",chan->name);
- play_dialtone(chan, args.mailbox);
-
- k|=1; /* In number mode */
- i = 0; /* re-set buffer pointer */
- exten[sizeof(acctcode)] = 0;
- ast_copy_string(acctcode, exten, sizeof(acctcode));
- exten[0] = 0;
- ast_debug(1,"Successful DISA log-in on chan %s\n", chan->name);
- continue;
- }
- } else {
- if (j == '#') { /* end of extension */
- break;
- }
- }
-
- exten[i++] = j; /* save digit */
- exten[i] = 0;
- if (!(k&1))
- continue; /* if getting password, continue doing it */
- /* if this exists */
-
- /* user wants end of number, remove # */
- if (ast_test_flag(&flags, POUND_TO_END_FLAG) && j == '#') {
- exten[--i] = 0;
- break;
- }
-
- if (ast_ignore_pattern(args.context, exten)) {
- play_dialtone(chan, "");
- did_ignore = 1;
- } else
- if (did_ignore) {
- ast_playtones_stop(chan);
- did_ignore = 0;
- }
-
- /* if can do some more, do it */
- if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
- break;
- }
- }
- }
-
- ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
-
- if (k == 3) {
- int recheck = 0;
- struct ast_flags flags = { AST_CDR_FLAG_POSTED };
-
- if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
- pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
- exten[0] = 'i';
- exten[1] = '\0';
- recheck = 1;
- }
- if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
- ast_playtones_stop(chan);
- /* We're authenticated and have a target extension */
- if (!ast_strlen_zero(args.cid)) {
- ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
- ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
- }
-
- if (!ast_strlen_zero(acctcode))
- ast_string_field_set(chan, accountcode, acctcode);
-
- if (special_noanswer) flags.flags = 0;
- ast_cdr_reset(chan->cdr, &flags);
- ast_explicit_goto(chan, args.context, exten, 1);
- return 0;
- }
- }
-
- /* Received invalid, but no "i" extension exists in the given context */
-
-reorder:
- /* Play congestion for a bit */
- ast_indicate(chan, AST_CONTROL_CONGESTION);
- ast_safe_sleep(chan, 10*1000);
-
- ast_playtones_stop(chan);
-
- return -1;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, disa_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");
diff --git a/trunk/apps/app_dumpchan.c b/trunk/apps/app_dumpchan.c
deleted file mode 100644
index 48fb6215d..000000000
--- a/trunk/apps/app_dumpchan.c
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2004 - 2005, Anthony Minessale II.
- *
- * Anthony Minessale <anthmct@yahoo.com>
- *
- * A license has been granted to Digium (via disclaimer) for the use of
- * this code.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Application to dump channel variables
- *
- * \author Anthony Minessale <anthmct@yahoo.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/channel.h"
-
-static char *app = "DumpChan";
-static char *synopsis = "Dump Info About The Calling Channel";
-static char *desc =
- " DumpChan([<min_verbose_level>])\n"
- "Displays information on channel and listing of all channel\n"
- "variables. If min_verbose_level is specified, output is only\n"
- "displayed when the verbose level is currently set to that number\n"
- "or greater. \n";
-
-
-static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
-{
- struct timeval now;
- long elapsed_seconds = 0;
- int hour = 0, min = 0, sec = 0;
- char cgrp[BUFSIZ/2];
- char pgrp[BUFSIZ/2];
- char formatbuf[BUFSIZ/2];
-
- now = ast_tvnow();
- memset(buf, 0, size);
- if (!c)
- return 0;
-
- if (c->cdr) {
- elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
- hour = elapsed_seconds / 3600;
- min = (elapsed_seconds % 3600) / 60;
- sec = elapsed_seconds % 60;
- }
-
- snprintf(buf,size,
- "Name= %s\n"
- "Type= %s\n"
- "UniqueID= %s\n"
- "CallerIDNum= %s\n"
- "CallerIDName= %s\n"
- "DNIDDigits= %s\n"
- "RDNIS= %s\n"
- "Language= %s\n"
- "State= %s (%d)\n"
- "Rings= %d\n"
- "NativeFormat= %s\n"
- "WriteFormat= %s\n"
- "ReadFormat= %s\n"
- "RawWriteFormat= %s\n"
- "RawReadFormat= %s\n"
- "1stFileDescriptor= %d\n"
- "Framesin= %d %s\n"
- "Framesout= %d %s\n"
- "TimetoHangup= %ld\n"
- "ElapsedTime= %dh%dm%ds\n"
- "Context= %s\n"
- "Extension= %s\n"
- "Priority= %d\n"
- "CallGroup= %s\n"
- "PickupGroup= %s\n"
- "Application= %s\n"
- "Data= %s\n"
- "Blocking_in= %s\n",
- c->name,
- c->tech->type,
- c->uniqueid,
- S_OR(c->cid.cid_num, "(N/A)"),
- S_OR(c->cid.cid_name, "(N/A)"),
- S_OR(c->cid.cid_dnid, "(N/A)"),
- S_OR(c->cid.cid_rdnis, "(N/A)"),
- c->language,
- ast_state2str(c->_state),
- c->_state,
- c->rings,
- ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->nativeformats),
- ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->writeformat),
- ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->readformat),
- ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->rawwriteformat),
- ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->rawreadformat),
- c->fds[0], c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
- c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "", (long)c->whentohangup,
- hour,
- min,
- sec,
- c->context,
- c->exten,
- c->priority,
- ast_print_group(cgrp, sizeof(cgrp), c->callgroup),
- ast_print_group(pgrp, sizeof(pgrp), c->pickupgroup),
- ( c->appl ? c->appl : "(N/A)" ),
- ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
- (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
-
- return 0;
-}
-
-static int dumpchan_exec(struct ast_channel *chan, void *data)
-{
- struct ast_str *vars = ast_str_alloca(BUFSIZ * 4); /* XXX very large! */
- char info[1024];
- int level = 0;
- static char *line = "================================================================================";
-
- if (!ast_strlen_zero(data))
- level = atoi(data);
-
- pbx_builtin_serialize_variables(chan, &vars);
- serialize_showchan(chan, info, sizeof(info));
- if (option_verbose >= level)
- ast_verbose("\nDumping Info For Channel: %s:\n%s\nInfo:\n%s\nVariables:\n%s%s\n", chan->name, line, info, vars->str, line);
-
- return 0;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, dumpchan_exec, synopsis, desc);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dump Info About The Calling Channel");
diff --git a/trunk/apps/app_echo.c b/trunk/apps/app_echo.c
deleted file mode 100644
index 89242ffa0..000000000
--- a/trunk/apps/app_echo.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Echo application -- play back what you hear to evaluate latency
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/module.h"
-#include "asterisk/channel.h"
-
-static char *app = "Echo";
-
-static char *synopsis = "Echo audio, video, or DTMF back to the calling party";
-
-static char *descrip =
-" Echo(): This application will echo any audio, video, or DTMF frames read from\n"
-"the calling channel back to itself. If the DTMF digit '#' is received, the\n"
-"application will exit.\n";
-
-
-static int echo_exec(struct ast_channel *chan, void *data)
-{
- int res = -1;
- int format;
-
- format = ast_best_codec(chan->nativeformats);
- ast_set_write_format(chan, format);
- ast_set_read_format(chan, format);
-
- while (ast_waitfor(chan, -1) > -1) {
- struct ast_frame *f = ast_read(chan);
- if (!f)
- break;
- f->delivery.tv_sec = 0;
- f->delivery.tv_usec = 0;
- if (ast_write(chan, f)) {
- ast_frfree(f);
- goto end;
- }
- if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
- res = 0;
- ast_frfree(f);
- goto end;
- }
- ast_frfree(f);
- }
-end:
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, echo_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Echo Application");
diff --git a/trunk/apps/app_exec.c b/trunk/apps/app_exec.c
deleted file mode 100644
index 441e81458..000000000
--- a/trunk/apps/app_exec.c
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (c) 2004 - 2005, Tilghman Lesher. All rights reserved.
- * Portions copyright (c) 2006, Philipp Dunkel.
- *
- * Tilghman Lesher <app_exec__v002@the-tilghman.com>
- *
- * This code is released by the author with no restrictions on usage.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- */
-
-/*! \file
- *
- * \brief Exec application
- *
- * \author Tilghman Lesher <app_exec__v002@the-tilghman.com>
- * \author Philipp Dunkel <philipp.dunkel@ebox.at>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-
-/* Maximum length of any variable */
-#define MAXRESULT 1024
-
-/*! Note
- *
- * The key difference between these two apps is exit status. In a
- * nutshell, Exec tries to be transparent as possible, behaving
- * in exactly the same way as if the application it calls was
- * directly invoked from the dialplan.
- *
- * TryExec, on the other hand, provides a way to execute applications
- * and catch any possible fatal error without actually fatally
- * affecting the dialplan.
- */
-
-static char *app_exec = "Exec";
-static char *exec_synopsis = "Executes dialplan application";
-static char *exec_descrip =
-" Exec(appname(arguments)):\n"
-"Allows an arbitrary application to be invoked even when not\n"
-"hardcoded into the dialplan. If the underlying application\n"
-"terminates the dialplan, or if the application cannot be found,\n"
-"Exec will terminate the dialplan.\n"
-" To invoke external applications, see the application System.\n"
-" If you would like to catch any error instead, see TryExec.\n";
-
-static char *app_tryexec = "TryExec";
-static char *tryexec_synopsis = "Executes dialplan application, always returning";
-static char *tryexec_descrip =
-" TryExec(appname(arguments)):\n"
-"Allows an arbitrary application to be invoked even when not\n"
-"hardcoded into the dialplan. To invoke external applications\n"
-"see the application System. Always returns to the dialplan.\n"
-"The channel variable TRYSTATUS will be set to one of:\n"
-" SUCCESS if the application returned zero\n"
-" FAILED if the application returned non-zero\n"
-" NOAPP if the application was not found or was not specified\n";
-
-static char *app_execif = "ExecIf";
-static char *execif_synopsis = "Executes dialplan application, conditionally";
-static char *execif_descrip =
-" ExecIF (<expr>?<appiftrue>(<args>)[:<appiffalse>(<args>)])\n"
-"If <expr> is true, execute and return the result of <appiftrue>(<args>).\n"
-"If <expr> is true, but <appiftrue> is not found, then the application\n"
-"will return a non-zero value.\n";
-
-static int exec_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char *s, *appname, *endargs, args[MAXRESULT];
- struct ast_app *app;
-
- if (ast_strlen_zero(data))
- return 0;
-
- s = ast_strdupa(data);
- args[0] = 0;
- appname = strsep(&s, "(");
- if (s) {
- endargs = strrchr(s, ')');
- if (endargs)
- *endargs = '\0';
- pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1);
- }
- if (appname) {
- app = pbx_findapp(appname);
- if (app) {
- res = pbx_exec(chan, app, args);
- } else {
- ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
- res = -1;
- }
- }
-
- return res;
-}
-
-static int tryexec_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char *s, *appname, *endargs, args[MAXRESULT];
- struct ast_app *app;
-
- if (ast_strlen_zero(data))
- return 0;
-
- s = ast_strdupa(data);
- args[0] = 0;
- appname = strsep(&s, "(");
- if (s) {
- endargs = strrchr(s, ')');
- if (endargs)
- *endargs = '\0';
- pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1);
- }
- if (appname) {
- app = pbx_findapp(appname);
- if (app) {
- res = pbx_exec(chan, app, args);
- pbx_builtin_setvar_helper(chan, "TRYSTATUS", res ? "FAILED" : "SUCCESS");
- } else {
- ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
- pbx_builtin_setvar_helper(chan, "TRYSTATUS", "NOAPP");
- }
- }
-
- return 0;
-}
-
-static int execif_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char *truedata = NULL, *falsedata = NULL, *end;
- struct ast_app *app = NULL;
- AST_DECLARE_APP_ARGS(expr,
- AST_APP_ARG(expr);
- AST_APP_ARG(remainder);
- );
- AST_DECLARE_APP_ARGS(apps,
- AST_APP_ARG(t);
- AST_APP_ARG(f);
- );
- char *parse = ast_strdupa(data);
-
- AST_NONSTANDARD_APP_ARGS(expr, parse, '?');
- if (ast_strlen_zero(expr.remainder)) {
- ast_log(LOG_ERROR, "Usage: ExecIf(<expr>?<appiftrue>(<args>)[:<appiffalse>(<args)])\n");
- return -1;
- }
-
- AST_NONSTANDARD_APP_ARGS(apps, expr.remainder, ':');
-
- if (apps.t && (truedata = strchr(apps.t, '('))) {
- *truedata++ = '\0';
- if ((end = strrchr(truedata, ')')))
- *end = '\0';
- }
-
- if (apps.f && (falsedata = strchr(apps.f, '('))) {
- *falsedata++ = '\0';
- if ((end = strrchr(falsedata, ')')))
- *end = '\0';
- }
-
- if (pbx_checkcondition(expr.expr)) {
- if (!ast_strlen_zero(apps.t) && (app = pbx_findapp(apps.t))) {
- res = pbx_exec(chan, app, S_OR(truedata, ""));
- } else {
- ast_log(LOG_WARNING, "Could not find application! (%s)\n", apps.t);
- res = -1;
- }
- } else if (!ast_strlen_zero(apps.f)) {
- if ((app = pbx_findapp(apps.f))) {
- res = pbx_exec(chan, app, S_OR(falsedata, ""));
- } else {
- ast_log(LOG_WARNING, "Could not find application! (%s)\n", apps.f);
- res = -1;
- }
- }
-
- return res;
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(app_exec);
- res |= ast_unregister_application(app_tryexec);
- res |= ast_unregister_application(app_execif);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res = ast_register_application(app_exec, exec_exec, exec_synopsis, exec_descrip);
- res |= ast_register_application(app_tryexec, tryexec_exec, tryexec_synopsis, tryexec_descrip);
- res |= ast_register_application(app_execif, execif_exec, execif_synopsis, execif_descrip);
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Executes dialplan applications");
diff --git a/trunk/apps/app_externalivr.c b/trunk/apps/app_externalivr.c
deleted file mode 100644
index 443d8e0c7..000000000
--- a/trunk/apps/app_externalivr.c
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Kevin P. Fleming <kpfleming@digium.com>
- *
- * Portions taken from the file-based music-on-hold work
- * created by Anthony Minessale II in res_musiconhold.c
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief External IVR application interface
- *
- * \author Kevin P. Fleming <kpfleming@digium.com>
- *
- * \note Portions taken from the file-based music-on-hold work
- * created by Anthony Minessale II in res_musiconhold.c
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <signal.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/linkedlists.h"
-#include "asterisk/app.h"
-#include "asterisk/utils.h"
-
-static const char *app = "ExternalIVR";
-
-static const char *synopsis = "Interfaces with an external IVR application";
-
-static const char *descrip =
-" ExternalIVR(command[,arg[,arg...]]): Forks a process to run the supplied command,\n"
-"and starts a generator on the channel. The generator's play list is\n"
-"controlled by the external application, which can add and clear entries\n"
-"via simple commands issued over its stdout. The external application\n"
-"will receive all DTMF events received on the channel, and notification\n"
-"if the channel is hung up. The application will not be forcibly terminated\n"
-"when the channel is hung up.\n"
-"See doc/externalivr.txt for a protocol specification.\n";
-
-/* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
-#define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
-
-struct playlist_entry {
- AST_LIST_ENTRY(playlist_entry) list;
- char filename[1];
-};
-
-struct ivr_localuser {
- struct ast_channel *chan;
- AST_LIST_HEAD(playlist, playlist_entry) playlist;
- AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
- int abort_current_sound;
- int playing_silence;
- int option_autoclear;
-};
-
-
-struct gen_state {
- struct ivr_localuser *u;
- struct ast_filestream *stream;
- struct playlist_entry *current;
- int sample_queue;
-};
-
-static void send_child_event(FILE *handle, const char event, const char *data,
- const struct ast_channel *chan)
-{
- char tmp[256];
-
- if (!data) {
- snprintf(tmp, sizeof(tmp), "%c,%10d", event, (int)time(NULL));
- } else {
- snprintf(tmp, sizeof(tmp), "%c,%10d,%s", event, (int)time(NULL), data);
- }
-
- fprintf(handle, "%s\n", tmp);
- if (option_debug)
- ast_chan_log(LOG_DEBUG, chan, "sent '%s'\n", tmp);
-}
-
-static void *gen_alloc(struct ast_channel *chan, void *params)
-{
- struct ivr_localuser *u = params;
- struct gen_state *state;
-
- if (!(state = ast_calloc(1, sizeof(*state))))
- return NULL;
-
- state->u = u;
-
- return state;
-}
-
-static void gen_closestream(struct gen_state *state)
-{
- if (!state->stream)
- return;
-
- ast_closestream(state->stream);
- state->u->chan->stream = NULL;
- state->stream = NULL;
-}
-
-static void gen_release(struct ast_channel *chan, void *data)
-{
- struct gen_state *state = data;
-
- gen_closestream(state);
- ast_free(data);
-}
-
-/* caller has the playlist locked */
-static int gen_nextfile(struct gen_state *state)
-{
- struct ivr_localuser *u = state->u;
- char *file_to_stream;
-
- u->abort_current_sound = 0;
- u->playing_silence = 0;
- gen_closestream(state);
-
- while (!state->stream) {
- state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list);
- if (state->current) {
- file_to_stream = state->current->filename;
- } else {
- file_to_stream = "silence/10";
- u->playing_silence = 1;
- }
-
- if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
- ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
- if (!u->playing_silence) {
- continue;
- } else {
- break;
- }
- }
- }
-
- return (!state->stream);
-}
-
-static struct ast_frame *gen_readframe(struct gen_state *state)
-{
- struct ast_frame *f = NULL;
- struct ivr_localuser *u = state->u;
-
- if (u->abort_current_sound ||
- (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
- gen_closestream(state);
- AST_LIST_LOCK(&u->playlist);
- gen_nextfile(state);
- AST_LIST_UNLOCK(&u->playlist);
- }
-
- if (!(state->stream && (f = ast_readframe(state->stream)))) {
- if (state->current) {
- AST_LIST_LOCK(&u->finishlist);
- AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
- AST_LIST_UNLOCK(&u->finishlist);
- state->current = NULL;
- }
- if (!gen_nextfile(state))
- f = ast_readframe(state->stream);
- }
-
- return f;
-}
-
-static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
-{
- struct gen_state *state = data;
- struct ast_frame *f = NULL;
- int res = 0;
-
- state->sample_queue += samples;
-
- while (state->sample_queue > 0) {
- if (!(f = gen_readframe(state)))
- return -1;
-
- res = ast_write(chan, f);
- ast_frfree(f);
- if (res < 0) {
- ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
- return -1;
- }
- state->sample_queue -= f->samples;
- }
-
- return res;
-}
-
-static struct ast_generator gen =
-{
- alloc: gen_alloc,
- release: gen_release,
- generate: gen_generate,
-};
-
-static struct playlist_entry *make_entry(const char *filename)
-{
- struct playlist_entry *entry;
-
- if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
- return NULL;
-
- strcpy(entry->filename, filename);
-
- return entry;
-}
-
-static int app_exec(struct ast_channel *chan, void *data)
-{
- struct playlist_entry *entry;
- int child_stdin[2] = { 0,0 };
- int child_stdout[2] = { 0,0 };
- int child_stderr[2] = { 0,0 };
- int res = -1;
- int gen_active = 0;
- int pid;
- char *buf, *command;
- FILE *child_commands = NULL;
- FILE *child_errors = NULL;
- FILE *child_events = NULL;
- struct ivr_localuser foo = {
- .playlist = AST_LIST_HEAD_INIT_VALUE,
- .finishlist = AST_LIST_HEAD_INIT_VALUE,
- };
- struct ivr_localuser *u = &foo;
- sigset_t fullset, oldset;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(cmd)[32];
- );
-
- sigfillset(&fullset);
- pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
- u->abort_current_sound = 0;
- u->chan = chan;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
- return -1;
- }
-
- buf = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, buf);
-
- if (pipe(child_stdin)) {
- ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
- goto exit;
- }
-
- if (pipe(child_stdout)) {
- ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
- goto exit;
- }
-
- if (pipe(child_stderr)) {
- ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
- goto exit;
- }
-
- if (chan->_state != AST_STATE_UP) {
- ast_answer(chan);
- }
-
- if (ast_activate_generator(chan, &gen, u) < 0) {
- ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
- goto exit;
- } else
- gen_active = 1;
-
- pid = fork();
- if (pid < 0) {
- ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
- goto exit;
- }
-
- if (!pid) {
- /* child process */
- int i;
-
- signal(SIGPIPE, SIG_DFL);
- pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
-
- if (ast_opt_high_priority)
- ast_set_priority(0);
-
- dup2(child_stdin[0], STDIN_FILENO);
- dup2(child_stdout[1], STDOUT_FILENO);
- dup2(child_stderr[1], STDERR_FILENO);
- for (i = STDERR_FILENO + 1; i < 1024; i++)
- close(i);
- execv(args.cmd[0], args.cmd);
- fprintf(stderr, "Failed to execute '%s': %s\n", args.cmd[0], strerror(errno));
- _exit(1);
- } else {
- /* parent process */
- int child_events_fd = child_stdin[1];
- int child_commands_fd = child_stdout[0];
- int child_errors_fd = child_stderr[0];
- struct ast_frame *f;
- int ms;
- int exception;
- int ready_fd;
- int waitfds[2] = { child_errors_fd, child_commands_fd };
- struct ast_channel *rchan;
-
- pthread_sigmask(SIG_SETMASK, &oldset, NULL);
-
- close(child_stdin[0]);
- child_stdin[0] = 0;
- close(child_stdout[1]);
- child_stdout[1] = 0;
- close(child_stderr[1]);
- child_stderr[1] = 0;
-
- if (!(child_events = fdopen(child_events_fd, "w"))) {
- ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n");
- goto exit;
- }
-
- if (!(child_commands = fdopen(child_commands_fd, "r"))) {
- ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n");
- goto exit;
- }
-
- if (!(child_errors = fdopen(child_errors_fd, "r"))) {
- ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n");
- goto exit;
- }
-
- setvbuf(child_events, NULL, _IONBF, 0);
- setvbuf(child_commands, NULL, _IONBF, 0);
- setvbuf(child_errors, NULL, _IONBF, 0);
-
- res = 0;
-
- while (1) {
- if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
- ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
- res = -1;
- break;
- }
-
- if (ast_check_hangup(chan)) {
- ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
- send_child_event(child_events, 'H', NULL, chan);
- res = -1;
- break;
- }
-
- ready_fd = 0;
- ms = 100;
- errno = 0;
- exception = 0;
-
- rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms);
-
- if (!AST_LIST_EMPTY(&u->finishlist)) {
- AST_LIST_LOCK(&u->finishlist);
- while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
- send_child_event(child_events, 'F', entry->filename, chan);
- ast_free(entry);
- }
- AST_LIST_UNLOCK(&u->finishlist);
- }
-
- if (rchan) {
- /* the channel has something */
- f = ast_read(chan);
- if (!f) {
- ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
- send_child_event(child_events, 'H', NULL, chan);
- res = -1;
- break;
- }
-
- if (f->frametype == AST_FRAME_DTMF) {
- send_child_event(child_events, f->subclass, NULL, chan);
- if (u->option_autoclear) {
- if (!u->abort_current_sound && !u->playing_silence)
- send_child_event(child_events, 'T', NULL, chan);
- AST_LIST_LOCK(&u->playlist);
- while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
- send_child_event(child_events, 'D', entry->filename, chan);
- ast_free(entry);
- }
- if (!u->playing_silence)
- u->abort_current_sound = 1;
- AST_LIST_UNLOCK(&u->playlist);
- }
- } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
- ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
- send_child_event(child_events, 'H', NULL, chan);
- ast_frfree(f);
- res = -1;
- break;
- }
- ast_frfree(f);
- } else if (ready_fd == child_commands_fd) {
- char input[1024];
-
- if (exception || feof(child_commands)) {
- ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
- res = -1;
- break;
- }
-
- if (!fgets(input, sizeof(input), child_commands))
- continue;
-
- command = ast_strip(input);
-
- if (option_debug)
- ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input);
-
- if (strlen(input) < 4)
- continue;
-
- if (input[0] == 'S') {
- if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
- ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
- send_child_event(child_events, 'Z', NULL, chan);
- strcpy(&input[2], "exception");
- }
- if (!u->abort_current_sound && !u->playing_silence)
- send_child_event(child_events, 'T', NULL, chan);
- AST_LIST_LOCK(&u->playlist);
- while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
- send_child_event(child_events, 'D', entry->filename, chan);
- ast_free(entry);
- }
- if (!u->playing_silence)
- u->abort_current_sound = 1;
- entry = make_entry(&input[2]);
- if (entry)
- AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
- AST_LIST_UNLOCK(&u->playlist);
- } else if (input[0] == 'A') {
- if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
- ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
- send_child_event(child_events, 'Z', NULL, chan);
- strcpy(&input[2], "exception");
- }
- entry = make_entry(&input[2]);
- if (entry) {
- AST_LIST_LOCK(&u->playlist);
- AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
- AST_LIST_UNLOCK(&u->playlist);
- }
- } else if (input[0] == 'E') {
- ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
- send_child_event(child_events, 'E', NULL, chan);
- res = 0;
- break;
- } else if (input[0] == 'H') {
- ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
- send_child_event(child_events, 'H', NULL, chan);
- res = -1;
- break;
- } else if (input[0] == 'O') {
- if (!strcasecmp(&input[2], "autoclear"))
- u->option_autoclear = 1;
- else if (!strcasecmp(&input[2], "noautoclear"))
- u->option_autoclear = 0;
- else
- ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
- } else if (input[0] == 'V') {
- char *c;
- c = strchr(&input[2], '=');
- if (!c) {
- send_child_event(child_events, 'Z', NULL, chan);
- } else {
- *c++ = '\0';
- pbx_builtin_setvar_helper(chan, &input[2], c);
- }
- }
- } else if (ready_fd == child_errors_fd) {
- char input[1024];
-
- if (exception || feof(child_errors)) {
- ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
- res = -1;
- break;
- }
-
- if (fgets(input, sizeof(input), child_errors)) {
- command = ast_strip(input);
- ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
- }
- } else if ((ready_fd < 0) && ms) {
- if (errno == 0 || errno == EINTR)
- continue;
-
- ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
- break;
- }
- }
- }
-
- exit:
- if (gen_active)
- ast_deactivate_generator(chan);
-
- if (child_events)
- fclose(child_events);
-
- if (child_commands)
- fclose(child_commands);
-
- if (child_errors)
- fclose(child_errors);
-
- if (child_stdin[0])
- close(child_stdin[0]);
-
- if (child_stdin[1])
- close(child_stdin[1]);
-
- if (child_stdout[0])
- close(child_stdout[0]);
-
- if (child_stdout[1])
- close(child_stdout[1]);
-
- if (child_stderr[0])
- close(child_stderr[0]);
-
- if (child_stderr[1])
- close(child_stderr[1]);
-
- while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
- ast_free(entry);
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, app_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");
diff --git a/trunk/apps/app_festival.c b/trunk/apps/app_festival.c
deleted file mode 100644
index 926638e57..000000000
--- a/trunk/apps/app_festival.c
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2002, Christos Ricudis
- *
- * Christos Ricudis <ricudis@itc.auth.gr>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Connect to festival
- *
- * \author Christos Ricudis <ricudis@itc.auth.gr>
- *
- * \extref The Festival Speech Synthesis System - http://www.cstr.ed.ac.uk/projects/festival/
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <ctype.h>
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/md5.h"
-#include "asterisk/config.h"
-#include "asterisk/utils.h"
-#include "asterisk/lock.h"
-#include "asterisk/app.h"
-
-#define FESTIVAL_CONFIG "festival.conf"
-#define MAXLEN 180
-#define MAXFESTLEN 2048
-
-static char *app = "Festival";
-
-static char *synopsis = "Say text to the user";
-
-static char *descrip =
-" Festival(text[,intkeys]): Connect to Festival, send the argument, get back the waveform,\n"
-"play it to the user, allowing any given interrupt keys to immediately terminate and return\n"
-"the value, or 'any' to allow any number back (useful in dialplan)\n";
-
-
-static char *socket_receive_file_to_buff(int fd, int *size)
-{
- /* Receive file (probably a waveform file) from socket using
- * Festival key stuff technique, but long winded I know, sorry
- * but will receive any file without closing the stream or
- * using OOB data
- */
- static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */
- char *buff, *tmp;
- int bufflen;
- int n,k,i;
- char c;
-
- bufflen = 1024;
- if (!(buff = ast_malloc(bufflen)))
- return NULL;
- *size = 0;
-
- for (k = 0; file_stuff_key[k] != '\0';) {
- n = read(fd, &c, 1);
- if (n == 0)
- break; /* hit stream eof before end of file */
- if ((*size) + k + 1 >= bufflen) {
- /* +1 so you can add a terminating NULL if you want */
- bufflen += bufflen / 4;
- if (!(tmp = ast_realloc(buff, bufflen))) {
- ast_free(buff);
- return NULL;
- }
- buff = tmp;
- }
- if (file_stuff_key[k] == c)
- k++;
- else if ((c == 'X') && (file_stuff_key[k+1] == '\0')) {
- /* It looked like the key but wasn't */
- for (i = 0; i < k; i++, (*size)++)
- buff[*size] = file_stuff_key[i];
- k = 0;
- /* omit the stuffed 'X' */
- } else {
- for (i = 0; i < k; i++, (*size)++)
- buff[*size] = file_stuff_key[i];
- k = 0;
- buff[*size] = c;
- (*size)++;
- }
- }
-
- return buff;
-}
-
-static int send_waveform_to_fd(char *waveform, int length, int fd)
-{
- int res;
- int x;
-#ifdef __PPC__
- char c;
-#endif
- sigset_t fullset, oldset;
-
- sigfillset(&fullset);
- pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
- res = fork();
- if (res < 0)
- ast_log(LOG_WARNING, "Fork failed\n");
- if (res) {
- pthread_sigmask(SIG_SETMASK, &oldset, NULL);
- return res;
- }
- for (x = 0; x < 256; x++) {
- if (x != fd)
- close(x);
- }
- if (ast_opt_high_priority)
- ast_set_priority(0);
- signal(SIGPIPE, SIG_DFL);
- pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
-#ifdef __PPC__
- for (x = 0; x < length; x += 2) {
- c = *(waveform + x + 1);
- *(waveform + x + 1) = *(waveform + x);
- *(waveform + x) = c;
- }
-#endif
- write(fd, waveform, length);
- close(fd);
- exit(0);
-}
-
-static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, int length, char *intkeys)
-{
- int res = 0;
- int fds[2];
- int pid = -1;
- int needed = 0;
- int owriteformat;
- struct ast_frame *f;
- struct myframe {
- struct ast_frame f;
- char offset[AST_FRIENDLY_OFFSET];
- char frdata[2048];
- } myf = {
- .f = { 0, },
- };
-
- if (pipe(fds)) {
- ast_log(LOG_WARNING, "Unable to create pipe\n");
- return -1;
- }
-
- /* Answer if it's not already going */
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
- ast_stopstream(chan);
- ast_indicate(chan, -1);
-
- owriteformat = chan->writeformat;
- res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
- if (res < 0) {
- ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
- return -1;
- }
-
- res = send_waveform_to_fd(waveform, length, fds[1]);
- if (res >= 0) {
- pid = res;
- /* Order is important -- there's almost always going to be mp3... we want to prioritize the
- user */
- for (;;) {
- res = ast_waitfor(chan, 1000);
- if (res < 1) {
- res = -1;
- break;
- }
- f = ast_read(chan);
- if (!f) {
- ast_log(LOG_WARNING, "Null frame == hangup() detected\n");
- res = -1;
- break;
- }
- if (f->frametype == AST_FRAME_DTMF) {
- ast_debug(1, "User pressed a key\n");
- if (intkeys && strchr(intkeys, f->subclass)) {
- res = f->subclass;
- ast_frfree(f);
- break;
- }
- }
- if (f->frametype == AST_FRAME_VOICE) {
- /* Treat as a generator */
- needed = f->samples * 2;
- if (needed > sizeof(myf.frdata)) {
- ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n",
- (int)sizeof(myf.frdata) / 2, needed/2);
- needed = sizeof(myf.frdata);
- }
- res = read(fds[0], myf.frdata, needed);
- if (res > 0) {
- myf.f.frametype = AST_FRAME_VOICE;
- myf.f.subclass = AST_FORMAT_SLINEAR;
- myf.f.datalen = res;
- myf.f.samples = res / 2;
- myf.f.offset = AST_FRIENDLY_OFFSET;
- myf.f.src = __PRETTY_FUNCTION__;
- myf.f.data = myf.frdata;
- if (ast_write(chan, &myf.f) < 0) {
- res = -1;
- ast_frfree(f);
- break;
- }
- if (res < needed) { /* last frame */
- ast_debug(1, "Last frame\n");
- res = 0;
- ast_frfree(f);
- break;
- }
- } else {
- ast_debug(1, "No more waveform\n");
- res = 0;
- }
- }
- ast_frfree(f);
- }
- }
- close(fds[0]);
- close(fds[1]);
-
-#if 0
- if (pid > -1)
- kill(pid, SIGKILL);
-#endif
- if (!res && owriteformat)
- ast_set_write_format(chan, owriteformat);
- return res;
-}
-
-static int festival_exec(struct ast_channel *chan, void *vdata)
-{
- int usecache;
- int res = 0;
- struct sockaddr_in serv_addr;
- struct hostent *serverhost;
- struct ast_hostent ahp;
- int fd;
- FILE *fs;
- const char *host;
- const char *cachedir;
- const char *temp;
- const char *festivalcommand;
- int port = 1314;
- int n;
- char ack[4];
- char *waveform;
- int filesize;
- int wave;
- char bigstring[MAXFESTLEN];
- int i;
- struct MD5Context md5ctx;
- unsigned char MD5Res[16];
- char MD5Hex[33] = "";
- char koko[4] = "";
- char cachefile[MAXFESTLEN]="";
- int readcache = 0;
- int writecache = 0;
- int strln;
- int fdesc = -1;
- char buffer[16384];
- int seekpos = 0;
- char *data;
- struct ast_config *cfg;
- char *newfestivalcommand;
- struct ast_flags config_flags = { 0 };
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(text);
- AST_APP_ARG(interrupt);
- );
-
- if (ast_strlen_zero(vdata)) {
- ast_log(LOG_WARNING, "festival requires an argument (text)\n");
- return -1;
- }
-
- cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
- if (!cfg) {
- ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
- return -1;
- }
- if (!(host = ast_variable_retrieve(cfg, "general", "host"))) {
- host = "localhost";
- }
- if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) {
- port = 1314;
- } else {
- port = atoi(temp);
- }
- if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) {
- usecache = 0;
- } else {
- usecache = ast_true(temp);
- }
- if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) {
- cachedir = "/tmp/";
- }
- if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) {
- festivalcommand = "(tts_textasterisk \"%s\" 'file)(quit)\n";
- } else { /* This else parses the festivalcommand that we're sent from the config file for \n's, etc */
- int i, j;
- newfestivalcommand = alloca(strlen(festivalcommand) + 1);
-
- for (i = 0, j = 0; i < strlen(festivalcommand); i++) {
- if (festivalcommand[i] == '\\' && festivalcommand[i + 1] == 'n') {
- newfestivalcommand[j++] = '\n';
- i++;
- } else if (festivalcommand[i] == '\\') {
- newfestivalcommand[j++] = festivalcommand[i + 1];
- i++;
- } else
- newfestivalcommand[j++] = festivalcommand[i];
- }
- newfestivalcommand[j] = '\0';
- festivalcommand = newfestivalcommand;
- }
-
- data = ast_strdupa(vdata);
- AST_STANDARD_APP_ARGS(args, data);
-
- if (args.interrupt && !strcasecmp(args.interrupt, "any"))
- args.interrupt = AST_DIGIT_ANY;
-
- ast_debug(1, "Text passed to festival server : %s\n", args.text);
- /* Connect to local festival server */
-
- fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-
- if (fd < 0) {
- ast_log(LOG_WARNING, "festival_client: can't get socket\n");
- ast_config_destroy(cfg);
- return -1;
- }
-
- memset(&serv_addr, 0, sizeof(serv_addr));
-
- if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) {
- /* its a name rather than an ipnum */
- serverhost = ast_gethostbyname(host, &ahp);
-
- if (serverhost == NULL) {
- ast_log(LOG_WARNING, "festival_client: gethostbyname failed\n");
- ast_config_destroy(cfg);
- return -1;
- }
- memmove(&serv_addr.sin_addr, serverhost->h_addr, serverhost->h_length);
- }
-
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_port = htons(port);
-
- if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) {
- ast_log(LOG_WARNING, "festival_client: connect to server failed\n");
- ast_config_destroy(cfg);
- return -1;
- }
-
- /* Compute MD5 sum of string */
- MD5Init(&md5ctx);
- MD5Update(&md5ctx, (unsigned char *)args.text, strlen(args.text));
- MD5Final(MD5Res, &md5ctx);
- MD5Hex[0] = '\0';
-
- /* Convert to HEX and look if there is any matching file in the cache
- directory */
- for (i = 0; i < 16; i++) {
- snprintf(koko, sizeof(koko), "%X", MD5Res[i]);
- strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1);
- }
- readcache = 0;
- writecache = 0;
- if (strlen(cachedir) + strlen(MD5Hex) + 1 <= MAXFESTLEN && (usecache == -1)) {
- snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex);
- fdesc = open(cachefile, O_RDWR);
- if (fdesc == -1) {
- fdesc = open(cachefile, O_CREAT | O_RDWR, AST_FILE_MODE);
- if (fdesc != -1) {
- writecache = 1;
- strln = strlen(args.text);
- ast_debug(1, "line length : %d\n", strln);
- write(fdesc, &strln, sizeof(strln));
- write(fdesc, args.text, strln);
- seekpos = lseek(fdesc, 0, SEEK_CUR);
- ast_debug(1, "Seek position : %d\n", seekpos);
- }
- } else {
- read(fdesc, &strln, sizeof(strln));
- ast_debug(1, "Cache file exists, strln=%d, strlen=%d\n", strln, (int)strlen(args.text));
- if (strlen(args.text) == strln) {
- ast_debug(1, "Size OK\n");
- read(fdesc, &bigstring, strln);
- bigstring[strln] = 0;
- if (strcmp(bigstring, args.text) == 0) {
- readcache = 1;
- } else {
- ast_log(LOG_WARNING, "Strings do not match\n");
- }
- } else {
- ast_log(LOG_WARNING, "Size mismatch\n");
- }
- }
- }
-
- if (readcache == 1) {
- close(fd);
- fd = fdesc;
- ast_debug(1, "Reading from cache...\n");
- } else {
- ast_debug(1, "Passing text to festival...\n");
- fs = fdopen(dup(fd), "wb");
- fprintf(fs, festivalcommand, args.text);
- fflush(fs);
- fclose(fs);
- }
-
- /* Write to cache and then pass it down */
- if (writecache == 1) {
- ast_debug(1, "Writing result to cache...\n");
- while ((strln = read(fd, buffer, 16384)) != 0) {
- write(fdesc, buffer, strln);
- }
- close(fd);
- close(fdesc);
- fd = open(cachefile, O_RDWR);
- lseek(fd, seekpos, SEEK_SET);
- }
-
- ast_debug(1, "Passing data to channel...\n");
-
- /* Read back info from server */
- /* This assumes only one waveform will come back, also LP is unlikely */
- wave = 0;
- do {
- int read_data;
- for (n = 0; n < 3; ) {
- read_data = read(fd, ack + n, 3 - n);
- /* this avoids falling in infinite loop
- * in case that festival server goes down
- */
- if (read_data == -1) {
- ast_log(LOG_WARNING, "Unable to read from cache/festival fd\n");
- close(fd);
- ast_config_destroy(cfg);
- return -1;
- }
- n += read_data;
- }
- ack[3] = '\0';
- if (strcmp(ack, "WV\n") == 0) { /* receive a waveform */
- ast_debug(1, "Festival WV command\n");
- if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
- res = send_waveform_to_channel(chan, waveform, filesize, args.interrupt);
- ast_free(waveform);
- }
- break;
- } else if (strcmp(ack, "LP\n") == 0) { /* receive an s-expr */
- ast_debug(1, "Festival LP command\n");
- if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
- waveform[filesize] = '\0';
- ast_log(LOG_WARNING, "Festival returned LP : %s\n", waveform);
- ast_free(waveform);
- }
- } else if (strcmp(ack, "ER\n") == 0) { /* server got an error */
- ast_log(LOG_WARNING, "Festival returned ER\n");
- res = -1;
- break;
- }
- } while (strcmp(ack, "OK\n") != 0);
- close(fd);
- ast_config_destroy(cfg);
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- struct ast_flags config_flags = { 0 };
- struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
- if (!cfg) {
- ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
- return AST_MODULE_LOAD_DECLINE;
- }
- ast_config_destroy(cfg);
- return ast_register_application(app, festival_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Festival Interface");
diff --git a/trunk/apps/app_flash.c b/trunk/apps/app_flash.c
deleted file mode 100644
index d57feeb91..000000000
--- a/trunk/apps/app_flash.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief App to flash a zap trunk
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>zaptel</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/zapata.h"
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/image.h"
-
-static char *app = "Flash";
-
-static char *synopsis = "Flashes a Zap Trunk";
-
-static char *descrip =
-"Performs a flash on a zap trunk. This can be used\n"
-"to access features provided on an incoming analogue circuit\n"
-"such as conference and call waiting. Use with SendDTMF() to\n"
-"perform external transfers\n";
-
-
-static inline int zt_wait_event(int fd)
-{
- /* Avoid the silly zt_waitevent which ignores a bunch of events */
- int i,j=0;
- i = ZT_IOMUX_SIGEVENT;
- if (ioctl(fd, ZT_IOMUX, &i) == -1) return -1;
- if (ioctl(fd, ZT_GETEVENT, &j) == -1) return -1;
- return j;
-}
-
-static int flash_exec(struct ast_channel *chan, void *data)
-{
- int res = -1;
- int x;
- struct zt_params ztp;
-
- if (strcasecmp(chan->tech->type, "Zap")) {
- ast_log(LOG_WARNING, "%s is not a Zap channel\n", chan->name);
- return -1;
- }
-
- memset(&ztp, 0, sizeof(ztp));
- res = ioctl(chan->fds[0], ZT_GET_PARAMS, &ztp);
- if (!res) {
- if (ztp.sigtype & __ZT_SIG_FXS) {
- x = ZT_FLASH;
- res = ioctl(chan->fds[0], ZT_HOOK, &x);
- if (!res || (errno == EINPROGRESS)) {
- if (res) {
- /* Wait for the event to finish */
- zt_wait_event(chan->fds[0]);
- }
- res = ast_safe_sleep(chan, 1000);
- ast_verb(3, "Flashed channel %s\n", chan->name);
- } else
- ast_log(LOG_WARNING, "Unable to flash channel %s: %s\n", chan->name, strerror(errno));
- } else
- ast_log(LOG_WARNING, "%s is not an FXO Channel\n", chan->name);
- } else
- ast_log(LOG_WARNING, "Unable to get parameters of %s: %s\n", chan->name, strerror(errno));
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, flash_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Flash channel application");
-
diff --git a/trunk/apps/app_followme.c b/trunk/apps/app_followme.c
deleted file mode 100644
index 2586637fa..000000000
--- a/trunk/apps/app_followme.c
+++ /dev/null
@@ -1,1063 +0,0 @@
-/*
- * Asterisk -- A telephony toolkit for Linux.
- *
- * A full-featured Find-Me/Follow-Me Application
- *
- * Copyright (C) 2005-2006, BJ Weschke All Rights Reserved.
- *
- * BJ Weschke <bweschke@btwtech.com>
- *
- * This code is released by the author with no restrictions on usage.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- */
-
-/*! \file
- *
- * \brief Find-Me Follow-Me application
- *
- * \author BJ Weschke <bweschke@btwtech.com>
- *
- * \arg See \ref Config_followme
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>chan_local</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <signal.h>
-
-#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/say.h"
-#include "asterisk/features.h"
-#include "asterisk/musiconhold.h"
-#include "asterisk/cli.h"
-#include "asterisk/manager.h"
-#include "asterisk/config.h"
-#include "asterisk/monitor.h"
-#include "asterisk/utils.h"
-#include "asterisk/causes.h"
-#include "asterisk/astdb.h"
-#include "asterisk/app.h"
-
-static char *app = "FollowMe";
-static char *synopsis = "Find-Me/Follow-Me application";
-static char *descrip =
-" FollowMe(followmeid[,options]):\n"
-"This application performs Find-Me/Follow-Me functionality for the caller\n"
-"as defined in the profile matching the <followmeid> parameter in\n"
-"followme.conf. If the specified <followmeid> profile doesn't exist in\n"
-"followme.conf, execution will be returned to the dialplan and call\n"
-"execution will continue at the next priority.\n\n"
-" Options:\n"
-" s - Playback the incoming status message prior to starting the follow-me step(s)\n"
-" a - Record the caller's name so it can be announced to the callee on each step\n"
-" n - Playback the unreachable status message if we've run out of steps to reach the\n"
-" or the callee has elected not to be reachable.\n"
-"Returns -1 on hangup\n";
-
-/*! \brief Number structure */
-struct number {
- char number[512]; /*!< Phone Number(s) and/or Extension(s) */
- long timeout; /*!< Dial Timeout, if used. */
- int order; /*!< The order to dial in */
- AST_LIST_ENTRY(number) entry; /*!< Next Number record */
-};
-
-/*! \brief Data structure for followme scripts */
-struct call_followme {
- ast_mutex_t lock;
- char name[AST_MAX_EXTENSION]; /*!< Name - FollowMeID */
- char moh[AST_MAX_CONTEXT]; /*!< Music On Hold Class to be used */
- char context[AST_MAX_CONTEXT]; /*!< Context to dial from */
- unsigned int active; /*!< Profile is active (1), or disabled (0). */
- char takecall[20]; /*!< Digit mapping to take a call */
- char nextindp[20]; /*!< Digit mapping to decline a call */
- char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
- char norecordingprompt[PATH_MAX]; /*!< Sound prompt name and path */
- char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
- char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
- char statusprompt[PATH_MAX]; /*!< Sound prompt name and path */
- char sorryprompt[PATH_MAX]; /*!< Sound prompt name and path */
-
- AST_LIST_HEAD_NOLOCK(numbers, number) numbers; /*!< Head of the list of follow-me numbers */
- AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers; /*!< Head of the list of black-listed numbers */
- AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers; /*!< Head of the list of white-listed numbers */
- AST_LIST_ENTRY(call_followme) entry; /*!< Next Follow-Me record */
-};
-
-struct fm_args {
- struct ast_channel *chan;
- char *mohclass;
- AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
- int status;
- char context[AST_MAX_CONTEXT];
- char namerecloc[AST_MAX_CONTEXT];
- struct ast_channel *outbound;
- char takecall[20]; /*!< Digit mapping to take a call */
- char nextindp[20]; /*!< Digit mapping to decline a call */
- char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
- char norecordingprompt[PATH_MAX]; /*!< Sound prompt name and path */
- char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
- char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
- char statusprompt[PATH_MAX]; /*!< Sound prompt name and path */
- char sorryprompt[PATH_MAX]; /*!< Sound prompt name and path */
- struct ast_flags followmeflags;
-};
-
-struct findme_user {
- struct ast_channel *ochan;
- int state;
- char dialarg[256];
- char yn[10];
- int ynidx;
- long digts;
- int cleared;
- AST_LIST_ENTRY(findme_user) entry;
-};
-
-enum {
- FOLLOWMEFLAG_STATUSMSG = (1 << 0),
- FOLLOWMEFLAG_RECORDNAME = (1 << 1),
- FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2)
-};
-
-AST_APP_OPTIONS(followme_opts, {
- AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG ),
- AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME ),
- AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG ),
-});
-
-static int ynlongest = 0;
-static time_t start_time, answer_time, end_time;
-
-static const char *featuredigittostr;
-static int featuredigittimeout = 5000; /*!< Feature Digit Timeout */
-static const char *defaultmoh = "default"; /*!< Default Music-On-Hold Class */
-
-static char takecall[20] = "1", nextindp[20] = "2";
-static char callfromprompt[PATH_MAX] = "followme/call-from";
-static char norecordingprompt[PATH_MAX] = "followme/no-recording";
-static char optionsprompt[PATH_MAX] = "followme/options";
-static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
-static char statusprompt[PATH_MAX] = "followme/status";
-static char sorryprompt[PATH_MAX] = "followme/sorry";
-
-
-static AST_RWLIST_HEAD_STATIC(followmes, call_followme);
-AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
-
-static void free_numbers(struct call_followme *f)
-{
- /* Free numbers attached to the profile */
- struct number *prev;
-
- while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
- /* Free the number */
- ast_free(prev);
- AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
-
- while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
- /* Free the blacklisted number */
- ast_free(prev);
- AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
-
- while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
- /* Free the whitelisted number */
- ast_free(prev);
- AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
-
-}
-
-
-/*! \brief Allocate and initialize followme profile */
-static struct call_followme *alloc_profile(const char *fmname)
-{
- struct call_followme *f;
-
- if (!(f = ast_calloc(1, sizeof(*f))))
- return NULL;
-
- ast_mutex_init(&f->lock);
- ast_copy_string(f->name, fmname, sizeof(f->name));
- f->moh[0] = '\0';
- f->context[0] = '\0';
- ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
- ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
- ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
- ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
- ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
- ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
- ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
- ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
- AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
- AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
- AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
- return f;
-}
-
-static void init_profile(struct call_followme *f)
-{
- f->active = 1;
- ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
-}
-
-
-
-/*! \brief Set parameter in profile from configuration file */
-static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
-{
-
- if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
- ast_copy_string(f->moh, val, sizeof(f->moh));
- else if (!strcasecmp(param, "context"))
- ast_copy_string(f->context, val, sizeof(f->context));
- else if (!strcasecmp(param, "takecall"))
- ast_copy_string(f->takecall, val, sizeof(f->takecall));
- else if (!strcasecmp(param, "declinecall"))
- ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
- else if (!strcasecmp(param, "call-from-prompt"))
- ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
- else if (!strcasecmp(param, "followme-norecording-prompt"))
- ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
- else if (!strcasecmp(param, "followme-options-prompt"))
- ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
- else if (!strcasecmp(param, "followme-pls-hold-prompt"))
- ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
- else if (!strcasecmp(param, "followme-status-prompt"))
- ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
- else if (!strcasecmp(param, "followme-sorry-prompt"))
- ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
- else if (failunknown) {
- if (linenum >= 0)
- ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
- else
- ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
- }
-}
-
-/*! \brief Add a new number */
-static struct number *create_followme_number(char *number, int timeout, int numorder)
-{
- struct number *cur;
- char *tmp;
-
-
- if (!(cur = ast_calloc(1, sizeof(*cur))))
- return NULL;
-
- cur->timeout = timeout;
- if ((tmp = strchr(number, ',')))
- *tmp = '\0';
- ast_copy_string(cur->number, number, sizeof(cur->number));
- cur->order = numorder;
- ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
-
- return cur;
-}
-
-/*! \brief Reload followme application module */
-static int reload_followme(int reload)
-{
- struct call_followme *f;
- struct ast_config *cfg;
- char *cat = NULL, *tmp;
- struct ast_variable *var;
- struct number *cur, *nm;
- char numberstr[90];
- int timeout;
- char *timeoutstr;
- int numorder;
- const char *takecallstr;
- const char *declinecallstr;
- const char *tmpstr;
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
-
- if (!(cfg = ast_config_load("followme.conf", config_flags))) {
- ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
- return 0;
- } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
-
- AST_RWLIST_WRLOCK(&followmes);
-
- /* Reset Global Var Values */
- featuredigittimeout = 5000;
-
- /* Mark all profiles as inactive for the moment */
- AST_RWLIST_TRAVERSE(&followmes, f, entry) {
- f->active = 0;
- }
-
- featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
-
- if (!ast_strlen_zero(featuredigittostr)) {
- if (!sscanf(featuredigittostr, "%d", &featuredigittimeout))
- featuredigittimeout = 5000;
- }
-
- takecallstr = ast_variable_retrieve(cfg, "general", "takecall");
- if (!ast_strlen_zero(takecallstr))
- ast_copy_string(takecall, takecallstr, sizeof(takecall));
-
- declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall");
- if (!ast_strlen_zero(declinecallstr))
- ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
-
- tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt");
- if (!ast_strlen_zero(tmpstr))
- ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
-
- tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt");
- if (!ast_strlen_zero(tmpstr))
- ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
-
- tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt");
- if (!ast_strlen_zero(tmpstr))
- ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
-
- tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt");
- if (!ast_strlen_zero(tmpstr))
- ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
-
- tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt");
- if (!ast_strlen_zero(tmpstr))
- ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
-
- tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt");
- if (!ast_strlen_zero(tmpstr))
- ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
-
- /* Chug through config file */
- while ((cat = ast_category_browse(cfg, cat))) {
- int new = 0;
-
- if (!strcasecmp(cat, "general"))
- continue;
-
- /* Look for an existing one */
- AST_LIST_TRAVERSE(&followmes, f, entry) {
- if (!strcasecmp(f->name, cat))
- break;
- }
-
- ast_debug(1, "New profile %s.\n", cat);
-
- if (!f) {
- /* Make one then */
- f = alloc_profile(cat);
- new = 1;
- }
-
- /* Totally fail if we fail to find/create an entry */
- if (!f)
- continue;
-
- if (!new)
- ast_mutex_lock(&f->lock);
- /* Re-initialize the profile */
- init_profile(f);
- free_numbers(f);
- var = ast_variable_browse(cfg, cat);
- while(var) {
- if (!strcasecmp(var->name, "number")) {
- int idx = 0;
-
- /* Add a new number */
- ast_copy_string(numberstr, var->value, sizeof(numberstr));
- if ((tmp = strchr(numberstr, ','))) {
- *tmp++ = '\0';
- timeoutstr = ast_strdupa(tmp);
- if ((tmp = strchr(timeoutstr, ','))) {
- *tmp++ = '\0';
- numorder = atoi(tmp);
- if (numorder < 0)
- numorder = 0;
- } else
- numorder = 0;
- timeout = atoi(timeoutstr);
- if (timeout < 0)
- timeout = 25;
- } else {
- timeout = 25;
- numorder = 0;
- }
-
- if (!numorder) {
- idx = 1;
- AST_LIST_TRAVERSE(&f->numbers, nm, entry)
- idx++;
- numorder = idx;
- }
- cur = create_followme_number(numberstr, timeout, numorder);
- AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
- } else {
- profile_set_param(f, var->name, var->value, var->lineno, 1);
- ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
- }
- var = var->next;
- } /* End while(var) loop */
-
- if (!new)
- ast_mutex_unlock(&f->lock);
- else
- AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
- }
-
- ast_config_destroy(cfg);
-
- AST_RWLIST_UNLOCK(&followmes);
-
- return 1;
-}
-
-static void clear_caller(struct findme_user *tmpuser)
-{
- struct ast_channel *outbound;
-
- if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
- outbound = tmpuser->ochan;
- if (!outbound->cdr) {
- outbound->cdr = ast_cdr_alloc();
- if (outbound->cdr)
- ast_cdr_init(outbound->cdr, outbound);
- }
- if (outbound->cdr) {
- char tmp[256];
-
- snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
- ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
- ast_cdr_update(outbound);
- ast_cdr_start(outbound->cdr);
- ast_cdr_end(outbound->cdr);
- /* If the cause wasn't handled properly */
- if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause))
- ast_cdr_failed(outbound->cdr);
- } else
- ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
- ast_hangup(tmpuser->ochan);
- }
-
-}
-
-static void clear_calling_tree(struct findme_user_listptr *findme_user_list)
-{
- struct findme_user *tmpuser;
-
- AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
- clear_caller(tmpuser);
- tmpuser->cleared = 1;
- }
-
-}
-
-
-
-static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, int *status, struct fm_args *tpargs)
-{
- struct ast_channel *watchers[256];
- int pos;
- struct ast_channel *winner;
- struct ast_frame *f;
- int ctstatus = 0;
- int dg;
- struct findme_user *tmpuser;
- int to = 0;
- int livechannels = 0;
- int tmpto;
- long totalwait = 0, wtd = 0, towas = 0;
- char *callfromname;
- char *pressbuttonname;
-
- /* ------------ wait_for_winner_channel start --------------- */
-
- callfromname = ast_strdupa(tpargs->callfromprompt);
- pressbuttonname = ast_strdupa(tpargs->optionsprompt);
-
- if (AST_LIST_EMPTY(findme_user_list)) {
- ast_verb(3, "couldn't reach at this number.\n");
- return NULL;
- }
-
- if (!caller) {
- ast_verb(3, "Original caller hungup. Cleanup.\n");
- clear_calling_tree(findme_user_list);
- return NULL;
- }
-
- totalwait = nm->timeout * 1000;
-
- while (!ctstatus) {
- to = 1000;
- pos = 1;
- livechannels = 0;
- watchers[0] = caller;
-
- dg = 0;
- winner = NULL;
- AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
- if (tmpuser->state >= 0 && tmpuser->ochan) {
- if (tmpuser->state == 3)
- tmpuser->digts += (towas - wtd);
- if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
- ast_verb(3, "We've been waiting for digits longer than we should have.\n");
- if (!ast_strlen_zero(namerecloc)) {
- tmpuser->state = 1;
- tmpuser->digts = 0;
- if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
- ast_sched_runq(tmpuser->ochan->sched);
- } else {
- ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
- return NULL;
- }
- } else {
- tmpuser->state = 2;
- tmpuser->digts = 0;
- if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
- ast_sched_runq(tmpuser->ochan->sched);
- else {
- ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
- return NULL;
- }
- }
- }
- if (tmpuser->ochan->stream) {
- ast_sched_runq(tmpuser->ochan->sched);
- tmpto = ast_sched_wait(tmpuser->ochan->sched);
- if (tmpto > 0 && tmpto < to)
- to = tmpto;
- else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
- ast_stopstream(tmpuser->ochan);
- if (tmpuser->state == 1) {
- ast_verb(3, "Playback of the call-from file appears to be done.\n");
- if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
- tmpuser->state = 2;
- } else {
- ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
- memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
- tmpuser->ynidx = 0;
- if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
- tmpuser->state = 3;
- else {
- ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
- return NULL;
- }
- }
- } else if (tmpuser->state == 2) {
- ast_verb(3, "Playback of name file appears to be done.\n");
- memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
- tmpuser->ynidx = 0;
- if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
- tmpuser->state = 3;
-
- } else {
- return NULL;
- }
- } else if (tmpuser->state == 3) {
- ast_verb(3, "Playback of the next step file appears to be done.\n");
- tmpuser->digts = 0;
- }
- }
- }
- watchers[pos++] = tmpuser->ochan;
- livechannels++;
- }
- }
-
- tmpto = to;
- if (to < 0) {
- to = 1000;
- tmpto = 1000;
- }
- towas = to;
- winner = ast_waitfor_n(watchers, pos, &to);
- tmpto -= to;
- totalwait -= tmpto;
- wtd = to;
- if (totalwait <= 0) {
- ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
- clear_calling_tree(findme_user_list);
- return NULL;
- }
- if (winner) {
- /* Need to find out which channel this is */
- dg = 0;
- while ((winner != watchers[dg]) && (dg < 256))
- dg++;
- AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry)
- if (tmpuser->ochan == winner)
- break;
- f = ast_read(winner);
- if (f) {
- if (f->frametype == AST_FRAME_CONTROL) {
- switch(f->subclass) {
- case AST_CONTROL_HANGUP:
- if (option_verbose > 2)
- ast_verb(3, "%s received a hangup frame.\n", winner->name);
- if (dg == 0) {
- ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
- clear_calling_tree(findme_user_list);
- ctstatus = -1;
- }
- break;
- case AST_CONTROL_ANSWER:
- if (option_verbose > 2)
- ast_verb(3, "%s answered %s\n", winner->name, caller->name);
- /* If call has been answered, then the eventual hangup is likely to be normal hangup */
- winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
- caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
- ast_verb(3, "Starting playback of %s\n", callfromname);
- if (dg > 0) {
- if (!ast_strlen_zero(namerecloc)) {
- if (!ast_streamfile(winner, callfromname, winner->language)) {
- ast_sched_runq(winner->sched);
- tmpuser->state = 1;
- } else {
- ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
- ast_frfree(f);
- return NULL;
- }
- } else {
- tmpuser->state = 2;
- if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
- ast_sched_runq(tmpuser->ochan->sched);
- else {
- ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
- ast_frfree(f);
- return NULL;
- }
- }
- }
- break;
- case AST_CONTROL_BUSY:
- ast_verb(3, "%s is busy\n", winner->name);
- break;
- case AST_CONTROL_CONGESTION:
- ast_verb(3, "%s is circuit-busy\n", winner->name);
- break;
- case AST_CONTROL_RINGING:
- ast_verb(3, "%s is ringing\n", winner->name);
- break;
- case AST_CONTROL_PROGRESS:
- ast_verb(3, "%s is making progress passing it to %s\n", winner->name, caller->name);
- break;
- case AST_CONTROL_VIDUPDATE:
- ast_verb(3, "%s requested a video update, passing it to %s\n", winner->name, caller->name);
- break;
- case AST_CONTROL_PROCEEDING:
- ast_verb(3, "%s is proceeding passing it to %s\n", winner->name,caller->name);
- break;
- case AST_CONTROL_HOLD:
- ast_verb(3, "Call on %s placed on hold\n", winner->name);
- break;
- case AST_CONTROL_UNHOLD:
- ast_verb(3, "Call on %s left from hold\n", winner->name);
- break;
- case AST_CONTROL_OFFHOOK:
- case AST_CONTROL_FLASH:
- /* Ignore going off hook and flash */
- break;
- case -1:
- ast_verb(3, "%s stopped sounds\n", winner->name);
- break;
- default:
- ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
- break;
- }
- }
- if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
- if (winner->stream)
- ast_stopstream(winner);
- tmpuser->digts = 0;
- ast_debug(1, "DTMF received: %c\n",(char) f->subclass);
- tmpuser->yn[tmpuser->ynidx] = (char) f->subclass;
- tmpuser->ynidx++;
- ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
- if (tmpuser->ynidx >= ynlongest) {
- ast_debug(1, "reached longest possible match - doing evals\n");
- if (!strcmp(tmpuser->yn, tpargs->takecall)) {
- ast_debug(1, "Match to take the call!\n");
- ast_frfree(f);
- return tmpuser->ochan;
- }
- if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
- ast_debug(1, "Next in dial plan step requested.\n");
- *status = 1;
- ast_frfree(f);
- return NULL;
- }
-
- }
- }
-
- ast_frfree(f);
- } else {
- if (winner) {
- ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n",dg);
- if (!dg) {
- clear_calling_tree(findme_user_list);
- return NULL;
- } else {
- tmpuser->state = -1;
- ast_hangup(winner);
- livechannels--;
- ast_debug(1, "live channels left %d\n", livechannels);
- if (!livechannels) {
- ast_verb(3, "no live channels left. exiting.\n");
- return NULL;
- }
- }
- }
- }
-
- } else
- ast_debug(1, "timed out waiting for action\n");
- }
-
- /* --- WAIT FOR WINNER NUMBER END! -----------*/
- return NULL;
-}
-
-static void findmeexec(struct fm_args *tpargs)
-{
- struct number *nm;
- struct ast_channel *outbound;
- struct ast_channel *caller;
- struct ast_channel *winner = NULL;
- char dialarg[512];
- int dg, idx;
- char *rest, *number;
- struct findme_user *tmpuser;
- struct findme_user *fmuser;
- struct findme_user *headuser;
- struct findme_user_listptr *findme_user_list;
- int status;
-
- findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
- AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
-
- /* We're going to figure out what the longest possible string of digits to collect is */
- ynlongest = 0;
- if (strlen(tpargs->takecall) > ynlongest)
- ynlongest = strlen(tpargs->takecall);
- if (strlen(tpargs->nextindp) > ynlongest)
- ynlongest = strlen(tpargs->nextindp);
-
- idx = 1;
- caller = tpargs->chan;
- AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
- if (nm->order == idx)
- break;
-
- while (nm) {
-
- ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);
- time(&start_time);
-
- number = ast_strdupa(nm->number);
- ast_debug(3, "examining %s\n", number);
- do {
- rest = strchr(number, '&');
- if (rest) {
- *rest = 0;
- rest++;
- }
-
- if (!strcmp(tpargs->context, ""))
- sprintf(dialarg, "%s", number);
- else
- sprintf(dialarg, "%s@%s", number, tpargs->context);
-
- tmpuser = ast_calloc(1, sizeof(*tmpuser));
- if (!tmpuser) {
- ast_log(LOG_WARNING, "Out of memory!\n");
- ast_free(findme_user_list);
- return;
- }
-
- outbound = ast_request("Local", ast_best_codec(caller->nativeformats), dialarg, &dg);
- if (outbound) {
- ast_set_callerid(outbound, caller->cid.cid_num, caller->cid.cid_name, caller->cid.cid_num);
- ast_channel_inherit_variables(tpargs->chan, outbound);
- ast_verb(3, "calling %s\n", dialarg);
- if (!ast_call(outbound,dialarg,0)) {
- tmpuser->ochan = outbound;
- tmpuser->state = 0;
- tmpuser->cleared = 0;
- ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
- AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
- } else {
- ast_verb(3, "couldn't reach at this number.\n");
- if (outbound) {
- if (!outbound->cdr)
- outbound->cdr = ast_cdr_alloc();
- if (outbound->cdr) {
- char tmp[256];
-
- ast_cdr_init(outbound->cdr, outbound);
- snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
- ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
- ast_cdr_update(outbound);
- ast_cdr_start(outbound->cdr);
- ast_cdr_end(outbound->cdr);
- /* If the cause wasn't handled properly */
- if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause))
- ast_cdr_failed(outbound->cdr);
- } else {
- ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
- ast_hangup(outbound);
- outbound = NULL;
- }
- }
-
- }
- } else
- ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
-
- number = rest;
- } while (number);
-
- status = 0;
- if (!AST_LIST_EMPTY(findme_user_list))
- winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs);
-
-
- while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
- if (!fmuser->cleared && fmuser->ochan != winner)
- clear_caller(fmuser);
- ast_free(fmuser);
- }
-
- fmuser = NULL;
- tmpuser = NULL;
- headuser = NULL;
- if (winner)
- break;
-
- if (!caller) {
- tpargs->status = 1;
- ast_free(findme_user_list);
- return;
- }
-
- idx++;
- AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
- if (nm->order == idx)
- break;
-
- }
- ast_free(findme_user_list);
- if (!winner)
- tpargs->status = 1;
- else {
- tpargs->status = 100;
- tpargs->outbound = winner;
- }
-
-
- return;
-
-}
-
-static int app_exec(struct ast_channel *chan, void *data)
-{
- struct fm_args targs;
- struct ast_bridge_config config;
- struct call_followme *f;
- struct number *nm, *newnm;
- int res = 0;
- char *argstr;
- char namerecloc[255];
- int duration = 0;
- struct ast_channel *caller;
- struct ast_channel *outbound;
- static char toast[80];
-
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(followmeid);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
- return -1;
- }
-
- if (!(argstr = ast_strdupa((char *)data))) {
- ast_log(LOG_ERROR, "Out of memory!\n");
- return -1;
- }
-
- AST_STANDARD_APP_ARGS(args, argstr);
-
- if (ast_strlen_zero(args.followmeid)) {
- ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
- return -1;
- }
-
- AST_RWLIST_RDLOCK(&followmes);
- AST_RWLIST_TRAVERSE(&followmes, f, entry) {
- if (!strcasecmp(f->name, args.followmeid) && (f->active))
- break;
- }
- AST_RWLIST_UNLOCK(&followmes);
-
- ast_debug(1, "New profile %s.\n", args.followmeid);
-
- if (!f) {
- ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
- return 0;
- }
-
- /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
- if (args.options)
- ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
-
- /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
- ast_mutex_lock(&f->lock);
- targs.mohclass = ast_strdupa(f->moh);
- ast_copy_string(targs.context, f->context, sizeof(targs.context));
- ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
- ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
- ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
- ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
- ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
- ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
- ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
- ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
- /* Copy the numbers we're going to use into another list in case the master list should get modified
- (and locked) while we're trying to do a follow-me */
- AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
- AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
- newnm = create_followme_number(nm->number, nm->timeout, nm->order);
- AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
- }
- ast_mutex_unlock(&f->lock);
-
- if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG))
- ast_stream_and_wait(chan, targs.statusprompt, "");
-
- snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
- duration = 5;
-
- if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME))
- if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, 128, 0, NULL) < 0)
- goto outrun;
-
- if (!ast_fileexists(namerecloc, NULL, chan->language))
- ast_copy_string(namerecloc, "", sizeof(namerecloc));
-
- if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
- goto outrun;
- if (ast_waitstream(chan, "") < 0)
- goto outrun;
- ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
-
- targs.status = 0;
- targs.chan = chan;
- ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
-
- findmeexec(&targs);
-
- while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry)))
- ast_free(nm);
-
- if (!ast_strlen_zero(namerecloc))
- unlink(namerecloc);
-
- if (targs.status != 100) {
- ast_moh_stop(chan);
- if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG))
- ast_stream_and_wait(chan, targs.sorryprompt, "");
- res = 0;
- } else {
- caller = chan;
- outbound = targs.outbound;
- /* Bridge the two channels. */
-
- memset(&config,0,sizeof(struct ast_bridge_config));
- ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
- ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
- ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
-
- ast_moh_stop(caller);
- /* Be sure no generators are left on it */
- ast_deactivate_generator(caller);
- /* Make sure channels are compatible */
- res = ast_channel_make_compatible(caller, outbound);
- if (res < 0) {
- ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
- ast_hangup(outbound);
- goto outrun;
- }
- time(&answer_time);
- res = ast_bridge_call(caller,outbound,&config);
- time(&end_time);
- snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time));
- pbx_builtin_setvar_helper(caller, "DIALEDTIME", toast);
- snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time));
- pbx_builtin_setvar_helper(caller, "ANSWEREDTIME", toast);
- if (outbound)
- ast_hangup(outbound);
- }
-
- outrun:
-
- return res;
-}
-
-static int unload_module(void)
-{
- struct call_followme *f;
-
- ast_unregister_application(app);
-
- /* Free Memory. Yeah! I'm free! */
- AST_RWLIST_WRLOCK(&followmes);
- while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
- free_numbers(f);
- ast_free(f);
- }
-
- AST_RWLIST_UNLOCK(&followmes);
-
- return 0;
-}
-
-static int load_module(void)
-{
- if(!reload_followme(0))
- return AST_MODULE_LOAD_DECLINE;
-
- return ast_register_application(app, app_exec, synopsis, descrip);
-}
-
-static int reload(void)
-{
- reload_followme(1);
-
- return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
diff --git a/trunk/apps/app_forkcdr.c b/trunk/apps/app_forkcdr.c
deleted file mode 100644
index c74de67b7..000000000
--- a/trunk/apps/app_forkcdr.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Anthony Minessale anthmct@yahoo.com
- * Development of this app Sponsered/Funded by TAAN Softworks Corp
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Fork CDR application
- *
- * \author Anthony Minessale anthmct@yahoo.com
- *
- * \note Development of this app Sponsored/Funded by TAAN Softworks Corp
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/cdr.h"
-#include "asterisk/module.h"
-
-static char *app = "ForkCDR";
-static char *synopsis =
-"Forks the Call Data Record";
-static char *descrip =
-" ForkCDR([options]): Causes the Call Data Record to fork an additional\n"
-"cdr record starting from the time of the fork call\n"
-" Options:\n"
-" v - If the option is passed all cdr variables will be passed along also.\n";
-
-
-static void ast_cdr_fork(struct ast_channel *chan)
-{
- struct ast_cdr *cdr;
- struct ast_cdr *newcdr;
- struct ast_flags flags = { AST_CDR_FLAG_KEEP_VARS };
-
- cdr = chan->cdr;
-
- while (cdr->next)
- cdr = cdr->next;
-
- if (!(newcdr = ast_cdr_dup(cdr)))
- return;
-
- ast_cdr_append(cdr, newcdr);
- ast_cdr_reset(newcdr, &flags);
-
- if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS))
- ast_cdr_free_vars(cdr, 0);
-
- ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED);
-}
-
-static int forkcdr_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
-
- if (!chan->cdr) {
- ast_log(LOG_WARNING, "Channel does not have a CDR\n");
- return 0;
- }
-
- if (!ast_strlen_zero(data))
- ast_set2_flag(chan->cdr, strchr(data, 'v'), AST_CDR_FLAG_KEEP_VARS);
-
- ast_cdr_fork(chan);
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, forkcdr_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Fork The CDR into 2 separate entities");
diff --git a/trunk/apps/app_getcpeid.c b/trunk/apps/app_getcpeid.c
deleted file mode 100644
index 1bab819b7..000000000
--- a/trunk/apps/app_getcpeid.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Get ADSI CPE ID
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/adsi.h"
-
-static char *app = "GetCPEID";
-
-static char *synopsis = "Get ADSI CPE ID";
-
-static char *descrip =
-" GetCPEID(): Obtains and displays ADSI CPE ID and other information in order\n"
-"to properly setup zapata.conf for on-hook operations.\n";
-
-
-static int cpeid_setstatus(struct ast_channel *chan, char *stuff[], int voice)
-{
- int justify[5] = { ADSI_JUST_CENT, ADSI_JUST_LEFT, ADSI_JUST_LEFT, ADSI_JUST_LEFT };
- char *tmp[5];
- int x;
- for (x=0;x<4;x++)
- tmp[x] = stuff[x];
- tmp[4] = NULL;
- return ast_adsi_print(chan, tmp, justify, voice);
-}
-
-static int cpeid_exec(struct ast_channel *chan, void *idata)
-{
- int res=0;
- unsigned char cpeid[4];
- int gotgeometry = 0;
- int gotcpeid = 0;
- int width, height, buttons;
- char *data[4];
- unsigned int x;
-
- for (x = 0; x < 4; x++)
- data[x] = alloca(80);
-
- strcpy(data[0], "** CPE Info **");
- strcpy(data[1], "Identifying CPE...");
- strcpy(data[2], "Please wait...");
- res = ast_adsi_load_session(chan, NULL, 0, 1);
- if (res > 0) {
- cpeid_setstatus(chan, data, 0);
- res = ast_adsi_get_cpeid(chan, cpeid, 0);
- if (res > 0) {
- gotcpeid = 1;
- ast_verb(3, "Got CPEID of '%02x:%02x:%02x:%02x' on '%s'\n", cpeid[0], cpeid[1], cpeid[2], cpeid[3], chan->name);
- }
- if (res > -1) {
- strcpy(data[1], "Measuring CPE...");
- strcpy(data[2], "Please wait...");
- cpeid_setstatus(chan, data, 0);
- res = ast_adsi_get_cpeinfo(chan, &width, &height, &buttons, 0);
- if (res > -1) {
- ast_verb(3, "CPE has %d lines, %d columns, and %d buttons on '%s'\n", height, width, buttons, chan->name);
- gotgeometry = 1;
- }
- }
- if (res > -1) {
- if (gotcpeid)
- snprintf(data[1], 80, "CPEID: %02x:%02x:%02x:%02x", cpeid[0], cpeid[1], cpeid[2], cpeid[3]);
- else
- strcpy(data[1], "CPEID Unknown");
- if (gotgeometry)
- snprintf(data[2], 80, "Geom: %dx%d, %d buttons", width, height, buttons);
- else
- strcpy(data[2], "Geometry unknown");
- strcpy(data[3], "Press # to exit");
- cpeid_setstatus(chan, data, 1);
- for(;;) {
- res = ast_waitfordigit(chan, 1000);
- if (res < 0)
- break;
- if (res == '#') {
- res = 0;
- break;
- }
- }
- ast_adsi_unload_session(chan);
- }
- }
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, cpeid_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Get ADSI CPE ID");
diff --git a/trunk/apps/app_ices.c b/trunk/apps/app_ices.c
deleted file mode 100644
index b2a4e9b91..000000000
--- a/trunk/apps/app_ices.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Stream to an icecast server via ICES (see contrib/asterisk-ices.xml)
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \extref ICES - http://www.icecast.org/ices.php
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <signal.h>
-#include <fcntl.h>
-#include <sys/time.h>
-
-#include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/frame.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-
-#define ICES "/usr/bin/ices"
-#define LOCAL_ICES "/usr/local/bin/ices"
-
-static char *app = "ICES";
-
-static char *synopsis = "Encode and stream using 'ices'";
-
-static char *descrip =
-" ICES(config.xml) Streams to an icecast server using ices\n"
-"(available separately). A configuration file must be supplied\n"
-"for ices (see examples/asterisk-ices.conf). \n";
-
-
-static int icesencode(char *filename, int fd)
-{
- int res;
- int x;
- sigset_t fullset, oldset;
-
- sigfillset(&fullset);
- pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
- res = fork();
- if (res < 0)
- ast_log(LOG_WARNING, "Fork failed\n");
- if (res) {
- pthread_sigmask(SIG_SETMASK, &oldset, NULL);
- return res;
- }
-
- /* Stop ignoring PIPE */
- signal(SIGPIPE, SIG_DFL);
- pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
-
- if (ast_opt_high_priority)
- ast_set_priority(0);
- dup2(fd, STDIN_FILENO);
- for (x=STDERR_FILENO + 1;x<1024;x++) {
- if ((x != STDIN_FILENO) && (x != STDOUT_FILENO))
- close(x);
- }
- /* Most commonly installed in /usr/local/bin */
- execl(ICES, "ices", filename, (char *)NULL);
- /* But many places has it in /usr/bin */
- execl(LOCAL_ICES, "ices", filename, (char *)NULL);
- /* As a last-ditch effort, try to use PATH */
- execlp("ices", "ices", filename, (char *)NULL);
- ast_log(LOG_WARNING, "Execute of ices failed\n");
- _exit(0);
-}
-
-static int ices_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- int fds[2];
- int ms = -1;
- int pid = -1;
- int flags;
- int oreadformat;
- struct timeval last;
- struct ast_frame *f;
- char filename[256]="";
- char *c;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "ICES requires an argument (configfile.xml)\n");
- return -1;
- }
-
- last = ast_tv(0, 0);
-
- if (pipe(fds)) {
- ast_log(LOG_WARNING, "Unable to create pipe\n");
- return -1;
- }
- flags = fcntl(fds[1], F_GETFL);
- fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
-
- ast_stopstream(chan);
-
- if (chan->_state != AST_STATE_UP)
- res = ast_answer(chan);
-
- if (res) {
- close(fds[0]);
- close(fds[1]);
- ast_log(LOG_WARNING, "Answer failed!\n");
- return -1;
- }
-
- oreadformat = chan->readformat;
- res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
- if (res < 0) {
- close(fds[0]);
- close(fds[1]);
- ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
- return -1;
- }
- if (((char *)data)[0] == '/')
- ast_copy_string(filename, (char *) data, sizeof(filename));
- else
- snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_CONFIG_DIR, (char *)data);
- /* Placeholder for options */
- c = strchr(filename, '|');
- if (c)
- *c = '\0';
- res = icesencode(filename, fds[0]);
- close(fds[0]);
- if (res >= 0) {
- pid = res;
- for (;;) {
- /* Wait for audio, and stream */
- ms = ast_waitfor(chan, -1);
- if (ms < 0) {
- ast_debug(1, "Hangup detected\n");
- res = -1;
- break;
- }
- f = ast_read(chan);
- if (!f) {
- ast_debug(1, "Null frame == hangup() detected\n");
- res = -1;
- break;
- }
- if (f->frametype == AST_FRAME_VOICE) {
- res = write(fds[1], f->data, f->datalen);
- if (res < 0) {
- if (errno != EAGAIN) {
- ast_log(LOG_WARNING, "Write failed to pipe: %s\n", strerror(errno));
- res = -1;
- ast_frfree(f);
- break;
- }
- }
- }
- ast_frfree(f);
- }
- }
- close(fds[1]);
-
- if (pid > -1)
- kill(pid, SIGKILL);
- if (!res && oreadformat)
- ast_set_read_format(chan, oreadformat);
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, ices_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Encode and Stream via icecast and ices");
diff --git a/trunk/apps/app_image.c b/trunk/apps/app_image.c
deleted file mode 100644
index 1e5b0c857..000000000
--- a/trunk/apps/app_image.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief App to transmit an image
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/image.h"
-
-static char *app = "SendImage";
-
-static char *synopsis = "Send an image file";
-
-static char *descrip =
-" SendImage(filename): Sends an image on a channel.\n"
-"If the channel supports image transport but the image send fails, the channel\n"
-"will be hung up. Otherwise, the dialplan continues execution. This\n"
-"application sets the following channel variable upon completion:\n"
-" SENDIMAGESTATUS The status is the result of the attempt, one of:\n"
-" OK | NOSUPPORT \n";
-
-
-static int sendimage_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "SendImage requires an argument (filename)\n");
- return -1;
- }
-
- if (!ast_supports_images(chan)) {
- /* Does not support transport */
- pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "NOSUPPORT");
- return 0;
- }
-
- if (!(res = ast_send_image(chan, data)))
- pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "OK");
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, sendimage_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Image Transmission Application");
diff --git a/trunk/apps/app_ivrdemo.c b/trunk/apps/app_ivrdemo.c
deleted file mode 100644
index 6dbbe7191..000000000
--- a/trunk/apps/app_ivrdemo.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief IVR Demo application
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <defaultenabled>no</defaultenabled>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-#include "asterisk/app.h"
-
-static char *tdesc = "IVR Demo Application";
-static char *app = "IVRDemo";
-static char *synopsis =
-" This is a skeleton application that shows you the basic structure to create your\n"
-"own asterisk applications and demonstrates the IVR demo.\n";
-
-static int ivr_demo_func(struct ast_channel *chan, void *data)
-{
- ast_verbose("IVR Demo, data is %s!\n", (char *)data);
- return 0;
-}
-
-AST_IVR_DECLARE_MENU(ivr_submenu, "IVR Demo Sub Menu", 0,
-{
- { "s", AST_ACTION_BACKGROUND, "demo-abouttotry" },
- { "s", AST_ACTION_WAITOPTION },
- { "1", AST_ACTION_PLAYBACK, "digits/1" },
- { "1", AST_ACTION_PLAYBACK, "digits/1" },
- { "1", AST_ACTION_RESTART },
- { "2", AST_ACTION_PLAYLIST, "digits/2;digits/3" },
- { "3", AST_ACTION_CALLBACK, ivr_demo_func },
- { "4", AST_ACTION_TRANSFER, "demo|s|1" },
- { "*", AST_ACTION_REPEAT },
- { "#", AST_ACTION_UPONE },
- { NULL }
-});
-
-AST_IVR_DECLARE_MENU(ivr_demo, "IVR Demo Main Menu", 0,
-{
- { "s", AST_ACTION_BACKGROUND, "demo-congrats" },
- { "g", AST_ACTION_BACKGROUND, "demo-instruct" },
- { "g", AST_ACTION_WAITOPTION },
- { "1", AST_ACTION_PLAYBACK, "digits/1" },
- { "1", AST_ACTION_RESTART },
- { "2", AST_ACTION_MENU, &ivr_submenu },
- { "2", AST_ACTION_RESTART },
- { "i", AST_ACTION_PLAYBACK, "invalid" },
- { "i", AST_ACTION_REPEAT, (void *)(unsigned long)2 },
- { "#", AST_ACTION_EXIT },
- { NULL },
-});
-
-
-static int skel_exec(struct ast_channel *chan, void *data)
-{
- int res=0;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "skel requires an argument (filename)\n");
- return -1;
- }
-
- /* Do our thing here */
-
- if (chan->_state != AST_STATE_UP)
- res = ast_answer(chan);
- if (!res)
- res = ast_ivr_menu_run(chan, &ivr_demo, data);
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, skel_exec, tdesc, synopsis);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "IVR Demo Application");
diff --git a/trunk/apps/app_jack.c b/trunk/apps/app_jack.c
deleted file mode 100644
index 8faecd1f2..000000000
--- a/trunk/apps/app_jack.c
+++ /dev/null
@@ -1,971 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2007 - 2008, Russell Bryant
- *
- * Russell Bryant <russell@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*!
- * \file
- * \brief Jack Application
- *
- * \author Russell Bryant <russell@digium.com>
- *
- * This is an application to connect an Asterisk channel to an input
- * and output jack port so that the audio can be processed through
- * another application, or to play audio from another application.
- *
- * \arg http://www.jackaudio.org/
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>jack</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <limits.h>
-
-#include <jack/jack.h>
-#include <jack/ringbuffer.h>
-
-#include "asterisk/module.h"
-#include "asterisk/channel.h"
-#include "asterisk/strings.h"
-#include "asterisk/lock.h"
-#include "asterisk/libresample.h"
-#include "asterisk/app.h"
-#include "asterisk/pbx.h"
-#include "asterisk/audiohook.h"
-
-#define RESAMPLE_QUALITY 0
-
-#define RINGBUFFER_SIZE 16384
-
-/*! \brief Common options between the Jack() app and JACK_HOOK() function */
-#define COMMON_OPTIONS \
-" s(<name>) - Connect to the specified jack server name.\n" \
-" i(<name>) - Connect the output port that gets created to the specified\n" \
-" jack input port.\n" \
-" o(<name>) - Connect the input port that gets created to the specified\n" \
-" jack output port.\n" \
-" n - Do not automatically start the JACK server if it is not already\n" \
-" running.\n"
-
-static char *jack_app = "JACK";
-static char *jack_synopsis =
-"JACK (Jack Audio Connection Kit) Application";
-static char *jack_desc =
-"JACK([options])\n"
-" When this application is executed, two jack ports will be created; one input\n"
-"and one output. Other applications can be hooked up to these ports to access\n"
-"the audio coming from, or being sent to the channel.\n"
-" Valid options:\n"
-COMMON_OPTIONS
-"";
-
-struct jack_data {
- AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(server_name);
- AST_STRING_FIELD(connect_input_port);
- AST_STRING_FIELD(connect_output_port);
- );
- jack_client_t *client;
- jack_port_t *input_port;
- jack_port_t *output_port;
- jack_ringbuffer_t *input_rb;
- jack_ringbuffer_t *output_rb;
- void *output_resampler;
- double output_resample_factor;
- void *input_resampler;
- double input_resample_factor;
- unsigned int stop:1;
- unsigned int has_audiohook:1;
- unsigned int no_start_server:1;
- /*! Only used with JACK_HOOK */
- struct ast_audiohook audiohook;
-};
-
-static const struct {
- jack_status_t status;
- const char *str;
-} jack_status_table[] = {
- { JackFailure, "Failure" },
- { JackInvalidOption, "Invalid Option" },
- { JackNameNotUnique, "Name Not Unique" },
- { JackServerStarted, "Server Started" },
- { JackServerFailed, "Server Failed" },
- { JackServerError, "Server Error" },
- { JackNoSuchClient, "No Such Client" },
- { JackLoadFailure, "Load Failure" },
- { JackInitFailure, "Init Failure" },
- { JackShmFailure, "Shared Memory Access Failure" },
- { JackVersionError, "Version Mismatch" },
-};
-
-static const char *jack_status_to_str(jack_status_t status)
-{
- int i;
-
- for (i = 0; i < ARRAY_LEN(jack_status_table); i++) {
- if (jack_status_table[i].status == status)
- return jack_status_table[i].str;
- }
-
- return "Unknown Error";
-}
-
-static void log_jack_status(const char *prefix, jack_status_t status)
-{
- struct ast_str *str = ast_str_alloca(512);
- int i, first = 0;
-
- for (i = 0; i < (sizeof(status) * 8); i++) {
- if (!(status & (1 << i)))
- continue;
-
- if (!first) {
- ast_str_set(&str, 0, "%s", jack_status_to_str((1 << i)));
- first = 1;
- } else
- ast_str_append(&str, 0, ", %s", jack_status_to_str((1 << i)));
- }
-
- ast_log(LOG_NOTICE, "%s: %s\n", prefix, str->str);
-}
-
-static int alloc_resampler(struct jack_data *jack_data, int input)
-{
- double from_srate, to_srate, jack_srate;
- void **resampler;
- double *resample_factor;
-
- if (input && jack_data->input_resampler)
- return 0;
-
- if (!input && jack_data->output_resampler)
- return 0;
-
- jack_srate = jack_get_sample_rate(jack_data->client);
-
- /* XXX Hard coded 8 kHz */
-
- to_srate = input ? 8000.0 : jack_srate;
- from_srate = input ? jack_srate : 8000.0;
-
- resample_factor = input ? &jack_data->input_resample_factor :
- &jack_data->output_resample_factor;
-
- if (from_srate == to_srate) {
- /* Awesome! The jack sample rate is the same as ours.
- * Resampling isn't needed. */
- *resample_factor = 1.0;
- return 0;
- }
-
- *resample_factor = to_srate / from_srate;
-
- resampler = input ? &jack_data->input_resampler :
- &jack_data->output_resampler;
-
- if (!(*resampler = resample_open(RESAMPLE_QUALITY,
- *resample_factor, *resample_factor))) {
- ast_log(LOG_ERROR, "Failed to open %s resampler\n",
- input ? "input" : "output");
- return -1;
- }
-
- return 0;
-}
-
-/*!
- * \brief Handle jack input port
- *
- * Read nframes number of samples from the input buffer, resample it
- * if necessary, and write it into the appropriate ringbuffer.
- */
-static void handle_input(void *buf, jack_nframes_t nframes,
- struct jack_data *jack_data)
-{
- short s_buf[nframes];
- float *in_buf = buf;
- size_t res;
- int i;
- size_t write_len = sizeof(s_buf);
-
- if (jack_data->input_resampler) {
- int total_in_buf_used = 0;
- int total_out_buf_used = 0;
- float f_buf[nframes + 1];
-
- memset(f_buf, 0, sizeof(f_buf));
-
- while (total_in_buf_used < nframes) {
- int in_buf_used;
- int out_buf_used;
-
- out_buf_used = resample_process(jack_data->input_resampler,
- jack_data->input_resample_factor,
- &in_buf[total_in_buf_used], nframes - total_in_buf_used,
- 0, &in_buf_used,
- &f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used);
-
- if (out_buf_used < 0)
- break;
-
- total_out_buf_used += out_buf_used;
- total_in_buf_used += in_buf_used;
-
- if (total_out_buf_used == ARRAY_LEN(f_buf)) {
- ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size, "
- "nframes '%d', total_out_buf_used '%d'\n", nframes, total_out_buf_used);
- break;
- }
- }
-
- for (i = 0; i < total_out_buf_used; i++)
- s_buf[i] = f_buf[i] * (SHRT_MAX / 1.0);
-
- write_len = total_out_buf_used * sizeof(int16_t);
- } else {
- /* No resampling needed */
-
- for (i = 0; i < nframes; i++)
- s_buf[i] = in_buf[i] * (SHRT_MAX / 1.0);
- }
-
- res = jack_ringbuffer_write(jack_data->input_rb, (const char *) s_buf, write_len);
- if (res != write_len) {
- ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n",
- (int) sizeof(s_buf), (int) res);
- }
-}
-
-/*!
- * \brief Handle jack output port
- *
- * Read nframes number of samples from the ringbuffer and write it out to the
- * output port buffer.
- */
-static void handle_output(void *buf, jack_nframes_t nframes,
- struct jack_data *jack_data)
-{
- size_t res, len;
-
- len = nframes * sizeof(float);
-
- res = jack_ringbuffer_read(jack_data->output_rb, buf, len);
-
- if (len != res) {
- ast_debug(2, "Wanted %d bytes to send to the output port, "
- "but only got %d\n", (int) len, (int) res);
- }
-}
-
-static int jack_process(jack_nframes_t nframes, void *arg)
-{
- struct jack_data *jack_data = arg;
- void *input_port_buf, *output_port_buf;
-
- if (!jack_data->input_resample_factor)
- alloc_resampler(jack_data, 1);
-
- input_port_buf = jack_port_get_buffer(jack_data->input_port, nframes);
- handle_input(input_port_buf, nframes, jack_data);
-
- output_port_buf = jack_port_get_buffer(jack_data->output_port, nframes);
- handle_output(output_port_buf, nframes, jack_data);
-
- return 0;
-}
-
-static void jack_shutdown(void *arg)
-{
- struct jack_data *jack_data = arg;
-
- jack_data->stop = 1;
-}
-
-static struct jack_data *destroy_jack_data(struct jack_data *jack_data)
-{
- if (jack_data->input_port) {
- jack_port_unregister(jack_data->client, jack_data->input_port);
- jack_data->input_port = NULL;
- }
-
- if (jack_data->output_port) {
- jack_port_unregister(jack_data->client, jack_data->output_port);
- jack_data->output_port = NULL;
- }
-
- if (jack_data->client) {
- jack_client_close(jack_data->client);
- jack_data->client = NULL;
- }
-
- if (jack_data->input_rb) {
- jack_ringbuffer_free(jack_data->input_rb);
- jack_data->input_rb = NULL;
- }
-
- if (jack_data->output_rb) {
- jack_ringbuffer_free(jack_data->output_rb);
- jack_data->output_rb = NULL;
- }
-
- if (jack_data->output_resampler) {
- resample_close(jack_data->output_resampler);
- jack_data->output_resampler = NULL;
- }
-
- if (jack_data->input_resampler) {
- resample_close(jack_data->input_resampler);
- jack_data->input_resampler = NULL;
- }
-
- if (jack_data->has_audiohook)
- ast_audiohook_destroy(&jack_data->audiohook);
-
- ast_string_field_free_memory(jack_data);
-
- ast_free(jack_data);
-
- return NULL;
-}
-
-static int init_jack_data(struct ast_channel *chan, struct jack_data *jack_data)
-{
- const char *chan_name;
- jack_status_t status = 0;
- jack_options_t jack_options = JackNullOption;
-
- ast_channel_lock(chan);
- chan_name = ast_strdupa(chan->name);
- ast_channel_unlock(chan);
-
- if (!(jack_data->output_rb = jack_ringbuffer_create(RINGBUFFER_SIZE)))
- return -1;
-
- if (!(jack_data->input_rb = jack_ringbuffer_create(RINGBUFFER_SIZE)))
- return -1;
-
- if (jack_data->no_start_server)
- jack_options |= JackNoStartServer;
-
- if (!ast_strlen_zero(jack_data->server_name)) {
- jack_options |= JackServerName;
- jack_data->client = jack_client_open(chan_name, jack_options, &status,
- jack_data->server_name);
- } else {
- jack_data->client = jack_client_open(chan_name, jack_options, &status);
- }
-
- if (status)
- log_jack_status("Client Open Status", status);
-
- if (!jack_data->client)
- return -1;
-
- jack_data->input_port = jack_port_register(jack_data->client, "input",
- JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0);
- if (!jack_data->input_port) {
- ast_log(LOG_ERROR, "Failed to create input port for jack port\n");
- return -1;
- }
-
- jack_data->output_port = jack_port_register(jack_data->client, "output",
- JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0);
- if (!jack_data->output_port) {
- ast_log(LOG_ERROR, "Failed to create output port for jack port\n");
- return -1;
- }
-
- if (jack_set_process_callback(jack_data->client, jack_process, jack_data)) {
- ast_log(LOG_ERROR, "Failed to register process callback with jack client\n");
- return -1;
- }
-
- jack_on_shutdown(jack_data->client, jack_shutdown, jack_data);
-
- if (jack_activate(jack_data->client)) {
- ast_log(LOG_ERROR, "Unable to activate jack client\n");
- return -1;
- }
-
- while (!ast_strlen_zero(jack_data->connect_input_port)) {
- const char **ports;
- int i;
-
- ports = jack_get_ports(jack_data->client, jack_data->connect_input_port,
- NULL, JackPortIsInput);
-
- if (!ports) {
- ast_log(LOG_ERROR, "No input port matching '%s' was found\n",
- jack_data->connect_input_port);
- break;
- }
-
- for (i = 0; ports[i]; i++) {
- ast_debug(1, "Found port '%s' that matched specified input port '%s'\n",
- ports[i], jack_data->connect_input_port);
- }
-
- if (jack_connect(jack_data->client, jack_port_name(jack_data->output_port), ports[0])) {
- ast_log(LOG_ERROR, "Failed to connect '%s' to '%s'\n", ports[0],
- jack_port_name(jack_data->output_port));
- } else {
- ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
- jack_port_name(jack_data->output_port));
- }
-
- free((void *) ports);
-
- break;
- }
-
- while (!ast_strlen_zero(jack_data->connect_output_port)) {
- const char **ports;
- int i;
-
- ports = jack_get_ports(jack_data->client, jack_data->connect_output_port,
- NULL, JackPortIsOutput);
-
- if (!ports) {
- ast_log(LOG_ERROR, "No output port matching '%s' was found\n",
- jack_data->connect_output_port);
- break;
- }
-
- for (i = 0; ports[i]; i++) {
- ast_debug(1, "Found port '%s' that matched specified output port '%s'\n",
- ports[i], jack_data->connect_output_port);
- }
-
- if (jack_connect(jack_data->client, ports[0], jack_port_name(jack_data->input_port))) {
- ast_log(LOG_ERROR, "Failed to connect '%s' to '%s'\n", ports[0],
- jack_port_name(jack_data->input_port));
- } else {
- ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
- jack_port_name(jack_data->input_port));
- }
-
- free((void *) ports);
-
- break;
- }
-
- return 0;
-}
-
-static int queue_voice_frame(struct jack_data *jack_data, struct ast_frame *f)
-{
- float f_buf[f->samples * 8];
- size_t f_buf_used = 0;
- int i;
- int16_t *s_buf = f->data;
- size_t res;
-
- memset(f_buf, 0, sizeof(f_buf));
-
- if (!jack_data->output_resample_factor)
- alloc_resampler(jack_data, 0);
-
- if (jack_data->output_resampler) {
- float in_buf[f->samples];
- int total_in_buf_used = 0;
- int total_out_buf_used = 0;
-
- memset(in_buf, 0, sizeof(in_buf));
-
- for (i = 0; i < f->samples; i++)
- in_buf[i] = s_buf[i] * (1.0 / SHRT_MAX);
-
- while (total_in_buf_used < ARRAY_LEN(in_buf)) {
- int in_buf_used;
- int out_buf_used;
-
- out_buf_used = resample_process(jack_data->output_resampler,
- jack_data->output_resample_factor,
- &in_buf[total_in_buf_used], ARRAY_LEN(in_buf) - total_in_buf_used,
- 0, &in_buf_used,
- &f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used);
-
- if (out_buf_used < 0)
- break;
-
- total_out_buf_used += out_buf_used;
- total_in_buf_used += in_buf_used;
-
- if (total_out_buf_used == ARRAY_LEN(f_buf)) {
- ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size\n");
- break;
- }
- }
-
- f_buf_used = total_out_buf_used;
- if (f_buf_used > ARRAY_LEN(f_buf))
- f_buf_used = ARRAY_LEN(f_buf);
- } else {
- /* No resampling needed */
-
- for (i = 0; i < f->samples; i++)
- f_buf[i] = s_buf[i] * (1.0 / SHRT_MAX);
-
- f_buf_used = f->samples;
- }
-
- res = jack_ringbuffer_write(jack_data->output_rb, (const char *) f_buf, f_buf_used * sizeof(float));
- if (res != (f_buf_used * sizeof(float))) {
- ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n",
- (int) (f_buf_used * sizeof(float)), (int) res);
- }
-
- return 0;
-}
-
-/*!
- * \brief handle jack audio
- *
- * \param[in] chan The Asterisk channel to write the frames to if no output frame
- * is provided.
- * \param[in] jack_data This is the jack_data struct that contains the input
- * ringbuffer that audio will be read from.
- * \param[out] out_frame If this argument is non-NULL, then assuming there is
- * enough data avilable in the ringbuffer, the audio in this frame
- * will get replaced with audio from the input buffer. If there is
- * not enough data available to read at this time, then the frame
- * data gets zeroed out.
- *
- * Read data from the input ringbuffer, which is the properly resampled audio
- * that was read from the jack input port. Write it to the channel in 20 ms frames,
- * or fill up an output frame instead if one is provided.
- *
- * \return Nothing.
- */
-static void handle_jack_audio(struct ast_channel *chan, struct jack_data *jack_data,
- struct ast_frame *out_frame)
-{
- short buf[160];
- struct ast_frame f = {
- .frametype = AST_FRAME_VOICE,
- .subclass = AST_FORMAT_SLINEAR,
- .src = "JACK",
- .data = buf,
- .datalen = sizeof(buf),
- .samples = ARRAY_LEN(buf),
- };
-
- for (;;) {
- size_t res, read_len;
- char *read_buf;
-
- read_len = out_frame ? out_frame->datalen : sizeof(buf);
- read_buf = out_frame ? out_frame->data : buf;
-
- res = jack_ringbuffer_read_space(jack_data->input_rb);
-
- if (res < read_len) {
- /* Not enough data ready for another frame, move on ... */
- if (out_frame) {
- ast_debug(1, "Sending an empty frame for the JACK_HOOK\n");
- memset(out_frame->data, 0, out_frame->datalen);
- }
- break;
- }
-
- res = jack_ringbuffer_read(jack_data->input_rb, (char *) read_buf, read_len);
-
- if (res < read_len) {
- ast_log(LOG_ERROR, "Error reading from ringbuffer, even though it said there was enough data\n");
- break;
- }
-
- if (out_frame) {
- /* If an output frame was provided, then we just want to fill up the
- * buffer in that frame and return. */
- break;
- }
-
- ast_write(chan, &f);
- }
-}
-
-enum {
- OPT_SERVER_NAME = (1 << 0),
- OPT_INPUT_PORT = (1 << 1),
- OPT_OUTPUT_PORT = (1 << 2),
- OPT_NOSTART_SERVER = (1 << 3),
-};
-
-enum {
- OPT_ARG_SERVER_NAME,
- OPT_ARG_INPUT_PORT,
- OPT_ARG_OUTPUT_PORT,
- /* Must be the last element */
- OPT_ARG_ARRAY_SIZE,
-};
-
-AST_APP_OPTIONS(jack_exec_options, BEGIN_OPTIONS
- AST_APP_OPTION_ARG('s', OPT_SERVER_NAME, OPT_ARG_SERVER_NAME),
- AST_APP_OPTION_ARG('i', OPT_INPUT_PORT, OPT_ARG_INPUT_PORT),
- AST_APP_OPTION_ARG('o', OPT_OUTPUT_PORT, OPT_ARG_OUTPUT_PORT),
- AST_APP_OPTION('n', OPT_NOSTART_SERVER),
-END_OPTIONS );
-
-static struct jack_data *jack_data_alloc(void)
-{
- struct jack_data *jack_data;
-
- if (!(jack_data = ast_calloc(1, sizeof(*jack_data))))
- return NULL;
-
- if (ast_string_field_init(jack_data, 32)) {
- ast_free(jack_data);
- return NULL;
- }
-
- return jack_data;
-}
-
-/*!
- * \note This must be done before calling init_jack_data().
- */
-static int handle_options(struct jack_data *jack_data, const char *__options_str)
-{
- struct ast_flags options = { 0, };
- char *option_args[OPT_ARG_ARRAY_SIZE];
- char *options_str;
-
- options_str = ast_strdupa(__options_str);
-
- ast_app_parse_options(jack_exec_options, &options, option_args, options_str);
-
- if (ast_test_flag(&options, OPT_SERVER_NAME)) {
- if (!ast_strlen_zero(option_args[OPT_ARG_SERVER_NAME]))
- ast_string_field_set(jack_data, server_name, option_args[OPT_ARG_SERVER_NAME]);
- else {
- ast_log(LOG_ERROR, "A server name must be provided with the s() option\n");
- return -1;
- }
- }
-
- if (ast_test_flag(&options, OPT_INPUT_PORT)) {
- if (!ast_strlen_zero(option_args[OPT_ARG_INPUT_PORT]))
- ast_string_field_set(jack_data, connect_input_port, option_args[OPT_ARG_INPUT_PORT]);
- else {
- ast_log(LOG_ERROR, "A name must be provided with the i() option\n");
- return -1;
- }
- }
-
- if (ast_test_flag(&options, OPT_OUTPUT_PORT)) {
- if (!ast_strlen_zero(option_args[OPT_ARG_OUTPUT_PORT]))
- ast_string_field_set(jack_data, connect_output_port, option_args[OPT_ARG_OUTPUT_PORT]);
- else {
- ast_log(LOG_ERROR, "A name must be provided with the o() option\n");
- return -1;
- }
- }
-
- jack_data->no_start_server = ast_test_flag(&options, OPT_NOSTART_SERVER) ? 1 : 0;
-
- return 0;
-}
-
-static int jack_exec(struct ast_channel *chan, void *data)
-{
- struct jack_data *jack_data;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(options);
- );
-
- if (!(jack_data = jack_data_alloc()))
- return -1;
-
- args.options = data;
-
- if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options)) {
- destroy_jack_data(jack_data);
- return -1;
- }
-
- if (init_jack_data(chan, jack_data)) {
- destroy_jack_data(jack_data);
- return -1;
- }
-
- if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
- destroy_jack_data(jack_data);
- return -1;
- }
-
- if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
- destroy_jack_data(jack_data);
- return -1;
- }
-
- while (!jack_data->stop) {
- struct ast_frame *f;
-
- ast_waitfor(chan, -1);
-
- f = ast_read(chan);
- if (!f) {
- jack_data->stop = 1;
- continue;
- }
-
- switch (f->frametype) {
- case AST_FRAME_CONTROL:
- if (f->subclass == AST_CONTROL_HANGUP)
- jack_data->stop = 1;
- break;
- case AST_FRAME_VOICE:
- queue_voice_frame(jack_data, f);
- default:
- break;
- }
-
- ast_frfree(f);
-
- handle_jack_audio(chan, jack_data, NULL);
- }
-
- jack_data = destroy_jack_data(jack_data);
-
- return 0;
-}
-
-static void jack_hook_ds_destroy(void *data)
-{
- struct jack_data *jack_data = data;
-
- destroy_jack_data(jack_data);
-}
-
-static const struct ast_datastore_info jack_hook_ds_info = {
- .type = "JACK_HOOK",
- .destroy = jack_hook_ds_destroy,
-};
-
-static int jack_hook_callback(struct ast_audiohook *audiohook, struct ast_channel *chan,
- struct ast_frame *frame, enum ast_audiohook_direction direction)
-{
- struct ast_datastore *datastore;
- struct jack_data *jack_data;
-
- if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
- return 0;
-
- if (direction != AST_AUDIOHOOK_DIRECTION_READ)
- return 0;
-
- if (frame->frametype != AST_FRAME_VOICE)
- return 0;
-
- if (frame->subclass != AST_FORMAT_SLINEAR) {
- ast_log(LOG_WARNING, "Expected frame in SLINEAR for the audiohook, but got format %d\n",
- frame->subclass);
- return 0;
- }
-
- ast_channel_lock(chan);
-
- if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
- ast_log(LOG_ERROR, "JACK_HOOK datastore not found for '%s'\n", chan->name);
- ast_channel_unlock(chan);
- return -1;
- }
-
- jack_data = datastore->data;
-
- queue_voice_frame(jack_data, frame);
-
- handle_jack_audio(chan, jack_data, frame);
-
- ast_channel_unlock(chan);
-
- return 0;
-}
-
-static int enable_jack_hook(struct ast_channel *chan, char *data)
-{
- struct ast_datastore *datastore;
- struct jack_data *jack_data = NULL;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(mode);
- AST_APP_ARG(options);
- );
-
- AST_STANDARD_APP_ARGS(args, data);
-
- ast_channel_lock(chan);
-
- if ((datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
- ast_log(LOG_ERROR, "JACK_HOOK already enabled for '%s'\n", chan->name);
- goto return_error;
- }
-
- if (ast_strlen_zero(args.mode) || strcasecmp(args.mode, "manipulate")) {
- ast_log(LOG_ERROR, "'%s' is not a supported mode. Only manipulate is supported.\n",
- S_OR(args.mode, "<none>"));
- goto return_error;
- }
-
- if (!(jack_data = jack_data_alloc()))
- goto return_error;
-
- if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options))
- goto return_error;
-
- if (init_jack_data(chan, jack_data))
- goto return_error;
-
- if (!(datastore = ast_channel_datastore_alloc(&jack_hook_ds_info, NULL)))
- goto return_error;
-
- jack_data->has_audiohook = 1;
- ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK");
- jack_data->audiohook.manipulate_callback = jack_hook_callback;
-
- datastore->data = jack_data;
-
- if (ast_audiohook_attach(chan, &jack_data->audiohook))
- goto return_error;
-
- if (ast_channel_datastore_add(chan, datastore))
- goto return_error;
-
- ast_channel_unlock(chan);
-
- return 0;
-
-return_error:
- ast_channel_unlock(chan);
-
- if (jack_data)
- destroy_jack_data(jack_data);
-
- return -1;
-}
-
-static int disable_jack_hook(struct ast_channel *chan)
-{
- struct ast_datastore *datastore;
- struct jack_data *jack_data;
-
- ast_channel_lock(chan);
-
- if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
- ast_channel_unlock(chan);
- ast_log(LOG_WARNING, "No JACK_HOOK found to disable\n");
- return -1;
- }
-
- ast_channel_datastore_remove(chan, datastore);
-
- jack_data = datastore->data;
- ast_audiohook_detach(&jack_data->audiohook);
-
- /* Keep the channel locked while we destroy the datastore, so that we can
- * ensure that all of the jack stuff is stopped just in case another frame
- * tries to come through the audiohook callback. */
- ast_channel_datastore_free(datastore);
-
- ast_channel_unlock(chan);
-
- return 0;
-}
-
-static int jack_hook_write(struct ast_channel *chan, const char *cmd, char *data,
- const char *value)
-{
- int res;
-
- if (!strcasecmp(value, "on"))
- res = enable_jack_hook(chan, data);
- else if (!strcasecmp(value, "off"))
- res = disable_jack_hook(chan);
- else {
- ast_log(LOG_ERROR, "'%s' is not a valid value for JACK_HOOK()\n", value);
- res = -1;
- }
-
- return res;
-}
-
-static struct ast_custom_function jack_hook_function = {
- .name = "JACK_HOOK",
- .synopsis = "Enable a jack hook on a channel",
- .syntax = "JACK_HOOK(<mode>,[options])",
- .desc =
- " The JACK_HOOK allows turning on or off jack connectivity to this channel.\n"
- "When the JACK_HOOK is turned on, jack ports will get created that allow\n"
- "access to the audio stream for this channel. The mode specifies which mode\n"
- "this hook should run in. A mode must be specified when turning the JACK_HOOK.\n"
- "on. However, all arguments are optional when turning it off.\n"
- "\n"
- " Valid modes are:\n"
-#if 0
- /* XXX TODO */
- " spy - Create a read-only audio hook. Only an output jack port will\n"
- " get created.\n"
- " whisper - Create a write-only audio hook. Only an input jack port will\n"
- " get created.\n"
-#endif
- " manipulate - Create a read/write audio hook. Both an input and an output\n"
- " jack port will get created. Audio from the channel will be\n"
- " sent out the output port and will be replaced by the audio\n"
- " coming in on the input port as it gets passed on.\n"
- "\n"
- " Valid options are:\n"
- COMMON_OPTIONS
- "\n"
- " Examples:\n"
- " To turn on the JACK_HOOK,\n"
- " Set(JACK_HOOK(manipulate,i(pure_data_0:input0)o(pure_data_0:output0))=on)\n"
- " To turn off the JACK_HOOK,\n"
- " Set(JACK_HOOK()=off)\n"
- "",
- .write = jack_hook_write,
-};
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(jack_app);
- res |= ast_custom_function_unregister(&jack_hook_function);
-
- return res;
-}
-
-static int load_module(void)
-{
- if (ast_register_application(jack_app, jack_exec, jack_synopsis, jack_desc))
- return AST_MODULE_LOAD_DECLINE;
-
- if (ast_custom_function_register(&jack_hook_function)) {
- ast_unregister_application(jack_app);
- return AST_MODULE_LOAD_DECLINE;
- }
-
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "JACK Interface");
diff --git a/trunk/apps/app_macro.c b/trunk/apps/app_macro.c
deleted file mode 100644
index b087ad36b..000000000
--- a/trunk/apps/app_macro.c
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Dial plan macro Implementation
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/config.h"
-#include "asterisk/utils.h"
-#include "asterisk/lock.h"
-
-#define MAX_ARGS 80
-
-/* special result value used to force macro exit */
-#define MACRO_EXIT_RESULT 1024
-
-static char *descrip =
-" Macro(macroname,arg1,arg2...): Executes a macro using the context\n"
-"'macro-<macroname>', jumping to the 's' extension of that context and\n"
-"executing each step, then returning when the steps end. \n"
-"The calling extension, context, and priority are stored in ${MACRO_EXTEN}, \n"
-"${MACRO_CONTEXT} and ${MACRO_PRIORITY} respectively. Arguments become\n"
-"${ARG1}, ${ARG2}, etc in the macro context.\n"
-"If you Goto out of the Macro context, the Macro will terminate and control\n"
-"will be returned at the location of the Goto.\n"
-"If ${MACRO_OFFSET} is set at termination, Macro will attempt to continue\n"
-"at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n"
-"Extensions: While a macro is being executed, it becomes the current context.\n"
-" This means that if a hangup occurs, for instance, that the macro\n"
-" will be searched for an 'h' extension, NOT the context from which\n"
-" the macro was called. So, make sure to define all appropriate\n"
-" extensions in your macro! (Note: AEL does not use macros)\n"
-"WARNING: Because of the way Macro is implemented (it executes the priorities\n"
-" contained within it via sub-engine), and a fixed per-thread\n"
-" memory stack allowance, macros are limited to 7 levels\n"
-" of nesting (macro calling macro calling macro, etc.); It\n"
-" may be possible that stack-intensive applications in deeply nested macros\n"
-" could cause asterisk to crash earlier than this limit. It is advised that\n"
-" if you need to deeply nest macro calls, that you use the Gosub application\n"
-" (now allows arguments like a Macro) with explict Return() calls instead.\n";
-
-static char *if_descrip =
-" MacroIf(<expr>?macroname_a[,arg1][:macroname_b[,arg1]])\n"
-"Executes macro defined in <macroname_a> if <expr> is true\n"
-"(otherwise <macroname_b> if provided)\n"
-"Arguments and return values as in application Macro()\n";
-
-static char *exclusive_descrip =
-" MacroExclusive(macroname,arg1,arg2...):\n"
-"Executes macro defined in the context 'macro-macroname'\n"
-"Only one call at a time may run the macro.\n"
-"(we'll wait if another call is busy executing in the Macro)\n"
-"Arguments and return values as in application Macro()\n";
-
-static char *exit_descrip =
-" MacroExit():\n"
-"Causes the currently running macro to exit as if it had\n"
-"ended normally by running out of priorities to execute.\n"
-"If used outside a macro, will likely cause unexpected\n"
-"behavior.\n";
-
-static char *app = "Macro";
-static char *if_app = "MacroIf";
-static char *exclusive_app = "MacroExclusive";
-static char *exit_app = "MacroExit";
-
-static char *synopsis = "Macro Implementation";
-static char *if_synopsis = "Conditional Macro Implementation";
-static char *exclusive_synopsis = "Exclusive Macro Implementation";
-static char *exit_synopsis = "Exit From Macro";
-
-
-static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
-{
- struct ast_exten *e;
- struct ast_include *i;
- struct ast_context *c2;
-
- for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
- if (ast_extension_match(ast_get_extension_name(e), exten)) {
- int needmatch = ast_get_extension_matchcid(e);
- if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
- (!needmatch)) {
- /* This is the matching extension we want */
- struct ast_exten *p;
- for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
- if (priority != ast_get_extension_priority(p))
- continue;
- return p;
- }
- }
- }
- }
-
- /* No match; run through includes */
- for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
- for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
- if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
- e = find_matching_priority(c2, exten, priority, callerid);
- if (e)
- return e;
- }
- }
- }
- return NULL;
-}
-
-static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
-{
- const char *s;
- char *tmp;
- char *cur, *rest;
- char *macro;
- char fullmacro[80];
- char varname[80];
- char runningapp[80], runningdata[1024];
- char *oldargs[MAX_ARGS + 1] = { NULL, };
- int argc, x;
- int res=0;
- char oldexten[256]="";
- int oldpriority, gosub_level = 0;
- char pc[80], depthc[12];
- char oldcontext[AST_MAX_CONTEXT] = "";
- const char *inhangupc;
- int offset, depth = 0, maxdepth = 7;
- int setmacrocontext=0;
- int autoloopflag, dead = 0, inhangup = 0;
-
- char *save_macro_exten;
- char *save_macro_context;
- char *save_macro_priority;
- char *save_macro_offset;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
- return -1;
- }
-
- /* does the user want a deeper rabbit hole? */
- s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
- if (s)
- sscanf(s, "%d", &maxdepth);
-
- /* Count how many levels deep the rabbit hole goes */
- s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
- if (s)
- sscanf(s, "%d", &depth);
- /* Used for detecting whether to return when a Macro is called from another Macro after hangup */
- if (strcmp(chan->exten, "h") == 0)
- pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
- inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP");
- if (!ast_strlen_zero(inhangupc))
- sscanf(inhangupc, "%d", &inhangup);
-
- if (depth >= maxdepth) {
- ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
- return 0;
- }
- snprintf(depthc, sizeof(depthc), "%d", depth + 1);
- pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
-
- tmp = ast_strdupa(data);
- rest = tmp;
- macro = strsep(&rest, ",");
- if (ast_strlen_zero(macro)) {
- ast_log(LOG_WARNING, "Invalid macro name specified\n");
- return 0;
- }
-
- snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
- if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
- if (!ast_context_find(fullmacro))
- ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
- else
- ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
- return 0;
- }
-
- /* If we are to run the macro exclusively, take the mutex */
- if (exclusive) {
- ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
- ast_autoservice_start(chan);
- if (ast_context_lockmacro(fullmacro)) {
- ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
- ast_autoservice_stop(chan);
- return 0;
- }
- ast_autoservice_stop(chan);
- }
-
- /* Save old info */
- oldpriority = chan->priority;
- ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
- ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
- if (ast_strlen_zero(chan->macrocontext)) {
- ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
- ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
- chan->macropriority = chan->priority;
- setmacrocontext=1;
- }
- argc = 1;
- /* Save old macro variables */
- save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
- pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
-
- save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
- pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
-
- save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
- snprintf(pc, sizeof(pc), "%d", oldpriority);
- pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
-
- save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
- pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
-
- /* Setup environment for new run */
- chan->exten[0] = 's';
- chan->exten[1] = '\0';
- ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
- chan->priority = 1;
-
- while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
- const char *s;
- /* Save copy of old arguments if we're overwriting some, otherwise
- let them pass through to the other macro */
- snprintf(varname, sizeof(varname), "ARG%d", argc);
- s = pbx_builtin_getvar_helper(chan, varname);
- if (s)
- oldargs[argc] = ast_strdup(s);
- pbx_builtin_setvar_helper(chan, varname, cur);
- argc++;
- }
- autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
- ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
- while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
- struct ast_context *c;
- struct ast_exten *e;
- int foundx;
- runningapp[0] = '\0';
- runningdata[0] = '\0';
-
- /* What application will execute? */
- if (ast_rdlock_contexts()) {
- ast_log(LOG_WARNING, "Failed to lock contexts list\n");
- } else {
- for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
- if (!strcmp(ast_get_context_name(c), chan->context)) {
- if (ast_rdlock_context(c)) {
- ast_log(LOG_WARNING, "Unable to lock context?\n");
- } else {
- e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
- if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
- ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
- ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
- }
- ast_unlock_context(c);
- }
- break;
- }
- }
- }
- ast_unlock_contexts();
-
- /* Reset the macro depth, if it was changed in the last iteration */
- pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
-
- if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num, &foundx,1))) {
- /* Something bad happened, or a hangup has been requested. */
- if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
- (res == '*') || (res == '#')) {
- /* Just return result as to the previous application as if it had been dialed */
- ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
- break;
- }
- switch(res) {
- case MACRO_EXIT_RESULT:
- res = 0;
- goto out;
- case AST_PBX_KEEPALIVE:
- ast_debug(2, "Spawn extension (%s,%s,%d) exited KEEPALIVE in macro %s on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
- ast_verb(2, "Spawn extension (%s, %s, %d) exited KEEPALIVE in macro '%s' on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
- goto out;
- break;
- default:
- ast_debug(2, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
- ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
- dead = 1;
- goto out;
- }
- }
-
- ast_debug(1, "Executed application: %s\n", runningapp);
-
- if (!strcasecmp(runningapp, "GOSUB")) {
- gosub_level++;
- ast_debug(1, "Incrementing gosub_level\n");
- } else if (!strcasecmp(runningapp, "GOSUBIF")) {
- char tmp2[1024], *cond, *app, *app2 = tmp2;
- pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
- cond = strsep(&app2, "?");
- app = strsep(&app2, ":");
- if (pbx_checkcondition(cond)) {
- if (!ast_strlen_zero(app)) {
- gosub_level++;
- ast_debug(1, "Incrementing gosub_level\n");
- }
- } else {
- if (!ast_strlen_zero(app2)) {
- gosub_level++;
- ast_debug(1, "Incrementing gosub_level\n");
- }
- }
- } else if (!strcasecmp(runningapp, "RETURN")) {
- gosub_level--;
- ast_debug(1, "Decrementing gosub_level\n");
- } else if (!strcasecmp(runningapp, "STACKPOP")) {
- gosub_level--;
- ast_debug(1, "Decrementing gosub_level\n");
- } else if (!strncasecmp(runningapp, "EXEC", 4)) {
- /* Must evaluate args to find actual app */
- char tmp2[1024], *tmp3 = NULL;
- pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
- if (!strcasecmp(runningapp, "EXECIF")) {
- tmp3 = strchr(tmp2, '|');
- if (tmp3)
- *tmp3++ = '\0';
- if (!pbx_checkcondition(tmp2))
- tmp3 = NULL;
- } else
- tmp3 = tmp2;
-
- if (tmp3)
- ast_debug(1, "Last app: %s\n", tmp3);
-
- if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
- gosub_level++;
- ast_debug(1, "Incrementing gosub_level\n");
- } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
- gosub_level--;
- ast_debug(1, "Decrementing gosub_level\n");
- } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
- gosub_level--;
- ast_debug(1, "Decrementing gosub_level\n");
- }
- }
-
- if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
- ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
- break;
- }
-
- /* don't stop executing extensions when we're in "h" */
- if (ast_check_hangup(chan) && !inhangup) {
- ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
- goto out;
- }
- chan->priority++;
- }
- out:
- /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
- snprintf(depthc, sizeof(depthc), "%d", depth);
- if (!dead) {
- pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
- ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
- }
-
- for (x = 1; x < argc; x++) {
- /* Restore old arguments and delete ours */
- snprintf(varname, sizeof(varname), "ARG%d", x);
- if (oldargs[x]) {
- if (!dead)
- pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
- ast_free(oldargs[x]);
- } else if (!dead) {
- pbx_builtin_setvar_helper(chan, varname, NULL);
- }
- }
-
- /* Restore macro variables */
- if (!dead) {
- pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
- pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
- pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
- }
- if (save_macro_exten)
- ast_free(save_macro_exten);
- if (save_macro_context)
- ast_free(save_macro_context);
- if (save_macro_priority)
- ast_free(save_macro_priority);
-
- if (!dead && setmacrocontext) {
- chan->macrocontext[0] = '\0';
- chan->macroexten[0] = '\0';
- chan->macropriority = 0;
- }
-
- if (!dead && !strcasecmp(chan->context, fullmacro)) {
- /* If we're leaving the macro normally, restore original information */
- chan->priority = oldpriority;
- ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
- if (!(ast_check_hangup(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
- /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
- const char *offsets;
- ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
- if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
- /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
- normally if there is any problem */
- if (sscanf(offsets, "%d", &offset) == 1) {
- if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
- chan->priority += offset;
- }
- }
- }
- }
- }
-
- if (!dead)
- pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
- if (save_macro_offset)
- ast_free(save_macro_offset);
-
- /* Unlock the macro */
- if (exclusive) {
- ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
- if (ast_context_unlockmacro(fullmacro)) {
- ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
- res = 0;
- }
- }
-
- return res;
-}
-
-static int macro_exec(struct ast_channel *chan, void *data)
-{
- return _macro_exec(chan, data, 0);
-}
-
-static int macroexclusive_exec(struct ast_channel *chan, void *data)
-{
- return _macro_exec(chan, data, 1);
-}
-
-static int macroif_exec(struct ast_channel *chan, void *data)
-{
- char *expr = NULL, *label_a = NULL, *label_b = NULL;
- int res = 0;
-
- if (!(expr = ast_strdupa(data)))
- return -1;
-
- if ((label_a = strchr(expr, '?'))) {
- *label_a = '\0';
- label_a++;
- if ((label_b = strchr(label_a, ':'))) {
- *label_b = '\0';
- label_b++;
- }
- if (pbx_checkcondition(expr))
- res = macro_exec(chan, label_a);
- else if (label_b)
- res = macro_exec(chan, label_b);
- } else
- ast_log(LOG_WARNING, "Invalid Syntax.\n");
-
- return res;
-}
-
-static int macro_exit_exec(struct ast_channel *chan, void *data)
-{
- return MACRO_EXIT_RESULT;
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(if_app);
- res |= ast_unregister_application(exit_app);
- res |= ast_unregister_application(app);
- res |= ast_unregister_application(exclusive_app);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
-
- res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
- res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
- res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
- res |= ast_register_application(app, macro_exec, synopsis, descrip);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");
diff --git a/trunk/apps/app_meetme.c b/trunk/apps/app_meetme.c
deleted file mode 100644
index 3cc5b551e..000000000
--- a/trunk/apps/app_meetme.c
+++ /dev/null
@@ -1,5592 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2007, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * SLA Implementation by:
- * Russell Bryant <russell@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Meet me conference bridge and Shared Line Appearances
- *
- * \author Mark Spencer <markster@digium.com>
- * \author (SLA) Russell Bryant <russell@digium.com>
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>zaptel</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/zapata.h"
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/config.h"
-#include "asterisk/app.h"
-#include "asterisk/dsp.h"
-#include "asterisk/musiconhold.h"
-#include "asterisk/manager.h"
-#include "asterisk/cli.h"
-#include "asterisk/say.h"
-#include "asterisk/utils.h"
-#include "asterisk/translate.h"
-#include "asterisk/ulaw.h"
-#include "asterisk/astobj.h"
-#include "asterisk/devicestate.h"
-#include "asterisk/dial.h"
-#include "asterisk/causes.h"
-#include "asterisk/paths.h"
-
-#include "enter.h"
-#include "leave.h"
-
-#define CONFIG_FILE_NAME "meetme.conf"
-#define SLA_CONFIG_FILE "sla.conf"
-
-/*! each buffer is 20ms, so this is 640ms total */
-#define DEFAULT_AUDIO_BUFFERS 32
-
-/*! String format for scheduled conferences */
-#define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
-
-enum {
- ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
- ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
- ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
- /*! User has requested to speak */
- ADMINFLAG_T_REQUEST = (1 << 4),
-};
-
-#define MEETME_DELAYDETECTTALK 300
-#define MEETME_DELAYDETECTENDTALK 1000
-
-#define AST_FRAME_BITS 32
-
-enum volume_action {
- VOL_UP,
- VOL_DOWN
-};
-
-enum entrance_sound {
- ENTER,
- LEAVE
-};
-
-enum recording_state {
- MEETME_RECORD_OFF,
- MEETME_RECORD_STARTED,
- MEETME_RECORD_ACTIVE,
- MEETME_RECORD_TERMINATE
-};
-
-#define CONF_SIZE 320
-
-enum {
- /*! user has admin access on the conference */
- CONFFLAG_ADMIN = (1 << 0),
- /*! If set the user can only receive audio from the conference */
- CONFFLAG_MONITOR = (1 << 1),
- /*! If set asterisk will exit conference when key defined in p() option is pressed */
- CONFFLAG_KEYEXIT = (1 << 2),
- /*! If set asterisk will provide a menu to the user when '*' is pressed */
- CONFFLAG_STARMENU = (1 << 3),
- /*! If set the use can only send audio to the conference */
- CONFFLAG_TALKER = (1 << 4),
- /*! If set there will be no enter or leave sounds */
- CONFFLAG_QUIET = (1 << 5),
- /*! If set, when user joins the conference, they will be told the number
- * of users that are already in */
- CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
- /*! Set to run AGI Script in Background */
- CONFFLAG_AGI = (1 << 7),
- /*! Set to have music on hold when user is alone in conference */
- CONFFLAG_MOH = (1 << 8),
- /*! If set the MeetMe will return if all marked with this flag left */
- CONFFLAG_MARKEDEXIT = (1 << 9),
- /*! If set, the MeetMe will wait until a marked user enters */
- CONFFLAG_WAITMARKED = (1 << 10),
- /*! If set, the MeetMe will exit to the specified context */
- CONFFLAG_EXIT_CONTEXT = (1 << 11),
- /*! If set, the user will be marked */
- CONFFLAG_MARKEDUSER = (1 << 12),
- /*! If set, user will be ask record name on entry of conference */
- CONFFLAG_INTROUSER = (1 << 13),
- /*! If set, the MeetMe will be recorded */
- CONFFLAG_RECORDCONF = (1<< 14),
- /*! If set, the user will be monitored if the user is talking or not */
- CONFFLAG_MONITORTALKER = (1 << 15),
- CONFFLAG_DYNAMIC = (1 << 16),
- CONFFLAG_DYNAMICPIN = (1 << 17),
- CONFFLAG_EMPTY = (1 << 18),
- CONFFLAG_EMPTYNOPIN = (1 << 19),
- CONFFLAG_ALWAYSPROMPT = (1 << 20),
- /*! If set, won't speak the extra prompt when the first person
- * enters the conference */
- CONFFLAG_NOONLYPERSON = (1 << 22),
- /*! If set, user will be asked to record name on entry of conference
- * without review */
- CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
- /*! If set, the user will be initially self-muted */
- CONFFLAG_STARTMUTED = (1 << 24),
- /*! Pass DTMF through the conference */
- CONFFLAG_PASS_DTMF = (1 << 25),
- CONFFLAG_SLA_STATION = (1 << 26),
- CONFFLAG_SLA_TRUNK = (1 << 27),
- /*! If set, the user should continue in the dialplan if kicked out */
- CONFFLAG_KICK_CONTINUE = (1 << 28),
- CONFFLAG_DURATION_STOP = (1 << 29),
- CONFFLAG_DURATION_LIMIT = (1 << 30),
-};
-
-enum {
- OPT_ARG_WAITMARKED = 0,
- OPT_ARG_EXITKEYS = 1,
- OPT_ARG_DURATION_STOP = 2,
- OPT_ARG_DURATION_LIMIT = 3,
- OPT_ARG_MOH_CLASS = 4,
- OPT_ARG_ARRAY_SIZE = 5,
-};
-
-AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
- AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
- AST_APP_OPTION('a', CONFFLAG_ADMIN ),
- AST_APP_OPTION('b', CONFFLAG_AGI ),
- AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
- AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
- AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
- AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
- AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
- AST_APP_OPTION('e', CONFFLAG_EMPTY ),
- AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
- AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
- AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
- AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
- AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
- AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
- AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
- AST_APP_OPTION('q', CONFFLAG_QUIET ),
- AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
- AST_APP_OPTION('s', CONFFLAG_STARMENU ),
- AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
- AST_APP_OPTION('l', CONFFLAG_MONITOR ),
- AST_APP_OPTION('t', CONFFLAG_TALKER ),
- AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
- AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
- AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
- AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
- AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
- AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
-END_OPTIONS );
-
-static const char *app = "MeetMe";
-static const char *app2 = "MeetMeCount";
-static const char *app3 = "MeetMeAdmin";
-static const char *app4 = "MeetMeChannelAdmin";
-static const char *slastation_app = "SLAStation";
-static const char *slatrunk_app = "SLATrunk";
-
-static const char *synopsis = "MeetMe conference bridge";
-static const char *synopsis2 = "MeetMe participant count";
-static const char *synopsis3 = "MeetMe conference Administration";
-static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
-static const char *slastation_synopsis = "Shared Line Appearance Station";
-static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
-
-/* Lookup RealTime conferences based on confno and current time */
-static int rt_schedule;
-static int fuzzystart;
-static int earlyalert;
-static int endalert;
-
-/* Log participant count to the RealTime backend */
-static int rt_log_members;
-
-static const char *descrip =
-" MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
-"conference. If the conference number is omitted, the user will be prompted\n"
-"to enter one. User can exit the conference by hangup, or if the 'p' option\n"
-"is specified, by pressing '#'.\n"
-"Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
-" must be present for conferencing to operate properly. In addition, the chan_zap\n"
-" channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
-"The option string may contain zero or more of the following characters:\n"
-" 'a' -- set admin mode\n"
-" 'A' -- set marked mode\n"
-" 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
-" Default: conf-background.agi (Note: This does not work with\n"
-" non-Zap channels in the same conference)\n"
-" 'c' -- announce user(s) count on joining a conference\n"
-" 'C' -- continue in dialplan when kicked out of conference\n"
-" 'd' -- dynamically add conference\n"
-" 'D' -- dynamically add conference, prompting for a PIN\n"
-" 'e' -- select an empty conference\n"
-" 'E' -- select an empty pinless conference\n"
-" 'F' -- Pass DTMF through the conference.\n"
-" 'i' -- announce user join/leave with review\n"
-" 'I' -- announce user join/leave without review\n"
-" 'l' -- set listen only mode (Listen only, no talking)\n"
-" 'm' -- set initially muted\n"
-" 'M[(<class>)]'\n"
-" -- enable music on hold when the conference has a single caller.\n"
-" Optionally, specify a musiconhold class to use. If one is not\n"
-" provided, it will use the channel's currently set music class,\n"
-" or \"default\".\n"
-" 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
-" being muted, meaning (a) No encode is done on transmission and\n"
-" (b) Received audio that is not registered as talking is omitted\n"
-" causing no buildup in background noise\n"
-" 'p[(<keys>)]'\n"
-" -- allow user to exit the conference by pressing '#' (default)\n"
-" or any of the defined keys. If keys contain '*' this will override\n"
-" option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
-" 'P' -- always prompt for the pin even if it is specified\n"
-" 'q' -- quiet mode (don't play enter/leave sounds)\n"
-" 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
-" using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
-" meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
-" wav.\n"
-" 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
-" 't' -- set talk only mode. (Talk only, no listening)\n"
-" 'T' -- set talker detection (sent to manager interface and meetme list)\n"
-" 'w[(<secs>)]'\n"
-" -- wait until the marked user enters the conference\n"
-" 'x' -- close the conference when last marked user exits\n"
-" 'X' -- allow user to exit the conference by entering a valid single\n"
-" digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
-" if that variable is not defined.\n"
-" '1' -- do not play message when first person enters\n"
-" 'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
-" 'L(x[:y][:z])' - Limit the conference to 'x' ms. Play a warning when 'y' ms are\n"
-" left. Repeat the warning every 'z' ms. The following special\n"
-" variables can be used with this option:\n"
-" * CONF_LIMIT_TIMEOUT_FILE File to play when time is up.\n"
-" * CONF_LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
-" The default is to say the time remaining.\n"
-"";
-
-static const char *descrip2 =
-" MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
-"MeetMe conference. If var is specified, playback will be skipped and the value\n"
-"will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
-"the channel, unless priority n+1 exists, in which case priority progress will\n"
-"continue.\n"
-"A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
-
-static const char *descrip3 =
-" MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
-" 'e' -- Eject last user that joined\n"
-" 'k' -- Kick one user out of conference\n"
-" 'K' -- Kick all users out of conference\n"
-" 'l' -- Unlock conference\n"
-" 'L' -- Lock conference\n"
-" 'm' -- Unmute one user\n"
-" 'M' -- Mute one user\n"
-" 'n' -- Unmute all users in the conference\n"
-" 'N' -- Mute all non-admin users in the conference\n"
-" 'r' -- Reset one user's volume settings\n"
-" 'R' -- Reset all users volume settings\n"
-" 's' -- Lower entire conference speaking volume\n"
-" 'S' -- Raise entire conference speaking volume\n"
-" 't' -- Lower one user's talk volume\n"
-" 'T' -- Raise one user's talk volume\n"
-" 'u' -- Lower one user's listen volume\n"
-" 'U' -- Raise one user's listen volume\n"
-" 'v' -- Lower entire conference listening volume\n"
-" 'V' -- Raise entire conference listening volume\n"
-"";
-
-static const char *descrip4 =
-" MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
-"channel in any coference.\n"
-" 'k' -- Kick the specified user out of the conference he is in\n"
-" 'm' -- Unmute the specified user\n"
-" 'M' -- Mute the specified user\n"
-"";
-
-static const char *slastation_desc =
-" SLAStation(<station name>):\n"
-"This application should be executed by an SLA station. The argument depends\n"
-"on how the call was initiated. If the phone was just taken off hook, then\n"
-"the argument \"station\" should be just the station name. If the call was\n"
-"initiated by pressing a line key, then the station name should be preceded\n"
-"by an underscore and the trunk name associated with that line button.\n"
-"For example: \"station1_line1\"."
-" On exit, this application will set the variable SLASTATION_STATUS to\n"
-"one of the following values:\n"
-" FAILURE | CONGESTION | SUCCESS\n"
-"";
-
-static const char *slatrunk_desc =
-" SLATrunk(<trunk name>[,options]):\n"
-"This application should be executed by an SLA trunk on an inbound call.\n"
-"The channel calling this application should correspond to the SLA trunk\n"
-"with the name \"trunk\" that is being passed as an argument.\n"
-" On exit, this application will set the variable SLATRUNK_STATUS to\n"
-"one of the following values:\n"
-" FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
-" The available options are:\n"
-" M[(<class>)] - Play back the specified MOH class instead of ringing\n"
-"";
-
-#define MAX_CONFNUM 80
-#define MAX_PIN 80
-
-/*! \brief The MeetMe Conference object */
-struct ast_conference {
- ast_mutex_t playlock; /*!< Conference specific lock (players) */
- ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
- char confno[MAX_CONFNUM]; /*!< Conference */
- struct ast_channel *chan; /*!< Announcements channel */
- struct ast_channel *lchan; /*!< Listen/Record channel */
- int fd; /*!< Announcements fd */
- int zapconf; /*!< Zaptel Conf # */
- int users; /*!< Number of active users */
- int markedusers; /*!< Number of marked users */
- int maxusers; /*!< Participant limit if scheduled */
- int endalert; /*!< When to play conf ending message */
- time_t start; /*!< Start time (s) */
- int refcount; /*!< reference count of usage */
- enum recording_state recording:2; /*!< recording status */
- unsigned int isdynamic:1; /*!< Created on the fly? */
- unsigned int locked:1; /*!< Is the conference locked? */
- pthread_t recordthread; /*!< thread for recording */
- ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
- pthread_attr_t attr; /*!< thread attribute */
- const char *recordingfilename; /*!< Filename to record the Conference into */
- const char *recordingformat; /*!< Format to record the Conference in */
- char pin[MAX_PIN]; /*!< If protected by a PIN */
- char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
- char uniqueid[32];
- long endtime; /*!< When to end the conf if scheduled */
- struct ast_frame *transframe[32];
- struct ast_frame *origframe;
- struct ast_trans_pvt *transpath[32];
- AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
- AST_LIST_ENTRY(ast_conference) list;
-};
-
-static AST_LIST_HEAD_STATIC(confs, ast_conference);
-
-static unsigned int conf_map[1024] = {0, };
-
-struct volume {
- int desired; /*!< Desired volume adjustment */
- int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
-};
-
-/*! \brief The MeetMe User object */
-struct ast_conf_user {
- int user_no; /*!< User Number */
- int userflags; /*!< Flags as set in the conference */
- int adminflags; /*!< Flags set by the Admin */
- struct ast_channel *chan; /*!< Connected channel */
- int talking; /*!< Is user talking */
- int zapchannel; /*!< Is a Zaptel channel */
- char usrvalue[50]; /*!< Custom User Value */
- char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
- time_t jointime; /*!< Time the user joined the conference */
- time_t kicktime; /*!< Time the user will be kicked from the conference */
- struct timeval start_time; /*!< Time the user entered into the conference */
- long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
- long play_warning; /*!< Play a warning when 'y' ms are left */
- long warning_freq; /*!< Repeat the warning every 'z' ms */
- const char *warning_sound; /*!< File to play as warning if 'y' is defined */
- const char *end_sound; /*!< File to play when time is up. */
- struct volume talk;
- struct volume listen;
- AST_LIST_ENTRY(ast_conf_user) list;
-};
-
-enum sla_which_trunk_refs {
- ALL_TRUNK_REFS,
- INACTIVE_TRUNK_REFS,
-};
-
-enum sla_trunk_state {
- SLA_TRUNK_STATE_IDLE,
- SLA_TRUNK_STATE_RINGING,
- SLA_TRUNK_STATE_UP,
- SLA_TRUNK_STATE_ONHOLD,
- SLA_TRUNK_STATE_ONHOLD_BYME,
-};
-
-enum sla_hold_access {
- /*! This means that any station can put it on hold, and any station
- * can retrieve the call from hold. */
- SLA_HOLD_OPEN,
- /*! This means that only the station that put the call on hold may
- * retrieve it from hold. */
- SLA_HOLD_PRIVATE,
-};
-
-struct sla_trunk_ref;
-
-struct sla_station {
- AST_RWLIST_ENTRY(sla_station) entry;
- AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(name);
- AST_STRING_FIELD(device);
- AST_STRING_FIELD(autocontext);
- );
- AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
- struct ast_dial *dial;
- /*! Ring timeout for this station, for any trunk. If a ring timeout
- * is set for a specific trunk on this station, that will take
- * priority over this value. */
- unsigned int ring_timeout;
- /*! Ring delay for this station, for any trunk. If a ring delay
- * is set for a specific trunk on this station, that will take
- * priority over this value. */
- unsigned int ring_delay;
- /*! This option uses the values in the sla_hold_access enum and sets the
- * access control type for hold on this station. */
- unsigned int hold_access:1;
- /*! Use count for inside sla_station_exec */
- unsigned int ref_count;
-};
-
-struct sla_station_ref {
- AST_LIST_ENTRY(sla_station_ref) entry;
- struct sla_station *station;
-};
-
-struct sla_trunk {
- AST_RWLIST_ENTRY(sla_trunk) entry;
- AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(name);
- AST_STRING_FIELD(device);
- AST_STRING_FIELD(autocontext);
- );
- AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
- /*! Number of stations that use this trunk */
- unsigned int num_stations;
- /*! Number of stations currently on a call with this trunk */
- unsigned int active_stations;
- /*! Number of stations that have this trunk on hold. */
- unsigned int hold_stations;
- struct ast_channel *chan;
- unsigned int ring_timeout;
- /*! If set to 1, no station will be able to join an active call with
- * this trunk. */
- unsigned int barge_disabled:1;
- /*! This option uses the values in the sla_hold_access enum and sets the
- * access control type for hold on this trunk. */
- unsigned int hold_access:1;
- /*! Whether this trunk is currently on hold, meaning that once a station
- * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
- unsigned int on_hold:1;
- /*! Use count for inside sla_trunk_exec */
- unsigned int ref_count;
-};
-
-struct sla_trunk_ref {
- AST_LIST_ENTRY(sla_trunk_ref) entry;
- struct sla_trunk *trunk;
- enum sla_trunk_state state;
- struct ast_channel *chan;
- /*! Ring timeout to use when this trunk is ringing on this specific
- * station. This takes higher priority than a ring timeout set at
- * the station level. */
- unsigned int ring_timeout;
- /*! Ring delay to use when this trunk is ringing on this specific
- * station. This takes higher priority than a ring delay set at
- * the station level. */
- unsigned int ring_delay;
-};
-
-static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
-static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
-
-static const char sla_registrar[] = "SLA";
-
-/*! \brief Event types that can be queued up for the SLA thread */
-enum sla_event_type {
- /*! A station has put the call on hold */
- SLA_EVENT_HOLD,
- /*! The state of a dial has changed */
- SLA_EVENT_DIAL_STATE,
- /*! The state of a ringing trunk has changed */
- SLA_EVENT_RINGING_TRUNK,
- /*! A reload of configuration has been requested */
- SLA_EVENT_RELOAD,
- /*! Poke the SLA thread so it can check if it can perform a reload */
- SLA_EVENT_CHECK_RELOAD,
-};
-
-struct sla_event {
- enum sla_event_type type;
- struct sla_station *station;
- struct sla_trunk_ref *trunk_ref;
- AST_LIST_ENTRY(sla_event) entry;
-};
-
-/*! \brief A station that failed to be dialed
- * \note Only used by the SLA thread. */
-struct sla_failed_station {
- struct sla_station *station;
- struct timeval last_try;
- AST_LIST_ENTRY(sla_failed_station) entry;
-};
-
-/*! \brief A trunk that is ringing */
-struct sla_ringing_trunk {
- struct sla_trunk *trunk;
- /*! The time that this trunk started ringing */
- struct timeval ring_begin;
- AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
- AST_LIST_ENTRY(sla_ringing_trunk) entry;
-};
-
-enum sla_station_hangup {
- SLA_STATION_HANGUP_NORMAL,
- SLA_STATION_HANGUP_TIMEOUT,
-};
-
-/*! \brief A station that is ringing */
-struct sla_ringing_station {
- struct sla_station *station;
- /*! The time that this station started ringing */
- struct timeval ring_begin;
- AST_LIST_ENTRY(sla_ringing_station) entry;
-};
-
-/*!
- * \brief A structure for data used by the sla thread
- */
-static struct {
- /*! The SLA thread ID */
- pthread_t thread;
- ast_cond_t cond;
- ast_mutex_t lock;
- AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
- AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
- AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
- AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
- unsigned int stop:1;
- /*! Attempt to handle CallerID, even though it is known not to work
- * properly in some situations. */
- unsigned int attempt_callerid:1;
- /*! A reload has been requested */
- unsigned int reload:1;
-} sla = {
- .thread = AST_PTHREADT_NULL,
-};
-
-/*! The number of audio buffers to be allocated on pseudo channels
- * when in a conference */
-static int audio_buffers;
-
-/*! Map 'volume' levels from -5 through +5 into
- * decibel (dB) settings for channel drivers
- * Note: these are not a straight linear-to-dB
- * conversion... the numbers have been modified
- * to give the user a better level of adjustability
- */
-static char const gain_map[] = {
- -15,
- -13,
- -10,
- -6,
- 0,
- 0,
- 0,
- 6,
- 10,
- 13,
- 15,
-};
-
-
-static int admin_exec(struct ast_channel *chan, void *data);
-static void *recordthread(void *args);
-
-static char *istalking(int x)
-{
- if (x > 0)
- return "(talking)";
- else if (x < 0)
- return "(unmonitored)";
- else
- return "(not talking)";
-}
-
-static int careful_write(int fd, unsigned char *data, int len, int block)
-{
- int res;
- int x;
-
- while (len) {
- if (block) {
- x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
- res = ioctl(fd, ZT_IOMUX, &x);
- } else
- res = 0;
- if (res >= 0)
- 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 set_talk_volume(struct ast_conf_user *user, int volume)
-{
- char gain_adjust;
-
- /* attempt to make the adjustment in the channel driver;
- if successful, don't adjust in the frame reading routine
- */
- gain_adjust = gain_map[volume + 5];
-
- return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
-}
-
-static int set_listen_volume(struct ast_conf_user *user, int volume)
-{
- char gain_adjust;
-
- /* attempt to make the adjustment in the channel driver;
- if successful, don't adjust in the frame reading routine
- */
- gain_adjust = gain_map[volume + 5];
-
- return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
-}
-
-static void tweak_volume(struct volume *vol, enum volume_action action)
-{
- switch (action) {
- case VOL_UP:
- switch (vol->desired) {
- case 5:
- break;
- case 0:
- vol->desired = 2;
- break;
- case -2:
- vol->desired = 0;
- break;
- default:
- vol->desired++;
- break;
- }
- break;
- case VOL_DOWN:
- switch (vol->desired) {
- case -5:
- break;
- case 2:
- vol->desired = 0;
- break;
- case 0:
- vol->desired = -2;
- break;
- default:
- vol->desired--;
- break;
- }
- }
-}
-
-static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
-{
- tweak_volume(&user->talk, action);
- /* attempt to make the adjustment in the channel driver;
- if successful, don't adjust in the frame reading routine
- */
- if (!set_talk_volume(user, user->talk.desired))
- user->talk.actual = 0;
- else
- user->talk.actual = user->talk.desired;
-}
-
-static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
-{
- tweak_volume(&user->listen, action);
- /* attempt to make the adjustment in the channel driver;
- if successful, don't adjust in the frame reading routine
- */
- if (!set_listen_volume(user, user->listen.desired))
- user->listen.actual = 0;
- else
- user->listen.actual = user->listen.desired;
-}
-
-static void reset_volumes(struct ast_conf_user *user)
-{
- signed char zero_volume = 0;
-
- ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
- ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
-}
-
-static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
-{
- unsigned char *data;
- int len;
- int res = -1;
-
- if (!ast_check_hangup(chan))
- res = ast_autoservice_start(chan);
-
- AST_LIST_LOCK(&confs);
-
- switch(sound) {
- case ENTER:
- data = enter;
- len = sizeof(enter);
- break;
- case LEAVE:
- data = leave;
- len = sizeof(leave);
- break;
- default:
- data = NULL;
- len = 0;
- }
- if (data) {
- careful_write(conf->fd, data, len, 1);
- }
-
- AST_LIST_UNLOCK(&confs);
-
- if (!res)
- ast_autoservice_stop(chan);
-}
-
-/*!
- * \brief Find or create a conference
- *
- * \param confno The conference name/number
- * \param pin The regular user pin
- * \param pinadmin The admin pin
- * \param make Make the conf if it doesn't exist
- * \param dynamic Mark the newly created conference as dynamic
- * \param refcount How many references to mark on the conference
- * \param chan The asterisk channel
- *
- * \return A pointer to the conference struct, or NULL if it wasn't found and
- * make or dynamic were not set.
- */
-static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
-{
- struct ast_conference *cnf;
- struct zt_confinfo ztc = { 0, };
- int confno_int = 0;
-
- AST_LIST_LOCK(&confs);
-
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- if (!strcmp(confno, cnf->confno))
- break;
- }
-
- if (cnf || (!make && !dynamic))
- goto cnfout;
-
- /* Make a new one */
- if (!(cnf = ast_calloc(1, sizeof(*cnf))))
- goto cnfout;
-
- ast_mutex_init(&cnf->playlock);
- ast_mutex_init(&cnf->listenlock);
- cnf->recordthread = AST_PTHREADT_NULL;
- ast_mutex_init(&cnf->recordthreadlock);
- ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
- ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
- ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
- ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
-
- /* Setup a new zap conference */
- ztc.confno = -1;
- ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
- cnf->fd = open("/dev/zap/pseudo", O_RDWR);
- if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
- ast_log(LOG_WARNING, "Unable to open pseudo device\n");
- if (cnf->fd >= 0)
- close(cnf->fd);
- ast_free(cnf);
- cnf = NULL;
- goto cnfout;
- }
-
- cnf->zapconf = ztc.confno;
-
- /* Setup a new channel for playback of audio files */
- cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
- if (cnf->chan) {
- ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
- ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
- ztc.chan = 0;
- ztc.confno = cnf->zapconf;
- ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
- if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
- ast_log(LOG_WARNING, "Error setting conference\n");
- if (cnf->chan)
- ast_hangup(cnf->chan);
- else
- close(cnf->fd);
-
- ast_free(cnf);
- cnf = NULL;
- goto cnfout;
- }
- }
-
- /* Fill the conference struct */
- cnf->start = time(NULL);
- cnf->maxusers = 0x7fffffff;
- cnf->isdynamic = dynamic ? 1 : 0;
- ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
- AST_LIST_INSERT_HEAD(&confs, cnf, list);
-
- /* Reserve conference number in map */
- if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
- conf_map[confno_int] = 1;
-
-cnfout:
- if (cnf)
- ast_atomic_fetchadd_int(&cnf->refcount, refcount);
-
- AST_LIST_UNLOCK(&confs);
-
- return cnf;
-}
-
-
-static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
-{
- static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
-
- int len = strlen(word);
- int which = 0;
- struct ast_conference *cnf = NULL;
- struct ast_conf_user *usr = NULL;
- char *confno = NULL;
- char usrno[50] = "";
- char *myline, *ret = NULL;
-
- if (pos == 1) { /* Command */
- return ast_cli_complete(word, cmds, state);
- } else if (pos == 2) { /* Conference Number */
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
- ret = cnf->confno;
- break;
- }
- }
- ret = ast_strdup(ret); /* dup before releasing the lock */
- AST_LIST_UNLOCK(&confs);
- return ret;
- } else if (pos == 3) {
- /* User Number || Conf Command option*/
- if (strstr(line, "mute") || strstr(line, "kick")) {
- if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
- return ast_strdup("all");
- which++;
- AST_LIST_LOCK(&confs);
-
- /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
- myline = ast_strdupa(line);
- if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
- while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
- ;
- }
-
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- if (!strcmp(confno, cnf->confno))
- break;
- }
-
- if (cnf) {
- /* Search for the user */
- AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
- snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
- if (!strncasecmp(word, usrno, len) && ++which > state)
- break;
- }
- }
- AST_LIST_UNLOCK(&confs);
- return usr ? ast_strdup(usrno) : NULL;
- } else if (strstr(line, "list") && (state == 0))
- return ast_strdup("concise");
- }
-
- return NULL;
-}
-
-static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- /* Process the command */
- struct ast_conference *cnf;
- struct ast_conf_user *user;
- int hr, min, sec;
- int i = 0, total = 0;
- time_t now;
- char *header_format = "%-14s %-14s %-10s %-8s %-8s %-6s\n";
- char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n";
- char cmdline[1024] = "";
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "meetme";
- e->usage =
- "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
- " Executes a command for the conference or on a conferee\n";
- return NULL;
- case CLI_GENERATE:
- return complete_meetmecmd(a->line, a->word, a->pos, a->n);
- }
-
- if (a->argc > 8)
- ast_cli(a->fd, "Invalid Arguments.\n");
- /* Check for length so no buffer will overflow... */
- for (i = 0; i < a->argc; i++) {
- if (strlen(a->argv[i]) > 100)
- ast_cli(a->fd, "Invalid Arguments.\n");
- }
- if (a->argc == 1 || (a->argc == 2 && !strcasecmp(a->argv[1], "concise"))) {
- /* 'MeetMe': List all the conferences */
- int concise = (a->argc == 2 && !strcasecmp(a->argv[1], "concise"));
- now = time(NULL);
- AST_LIST_LOCK(&confs);
- if (AST_LIST_EMPTY(&confs)) {
- if (!concise)
- ast_cli(a->fd, "No active MeetMe conferences.\n");
- AST_LIST_UNLOCK(&confs);
- return CLI_SUCCESS;
- }
- if (!concise)
- ast_cli(a->fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- if (cnf->markedusers == 0)
- strcpy(cmdline, "N/A ");
- else
- snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
- hr = (now - cnf->start) / 3600;
- min = ((now - cnf->start) % 3600) / 60;
- sec = (now - cnf->start) % 60;
- if (!concise)
- ast_cli(a->fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
- else {
- ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
- cnf->confno,
- cnf->users,
- cnf->markedusers,
- hr, min, sec,
- cnf->isdynamic,
- cnf->locked);
- }
-
- total += cnf->users;
- }
- AST_LIST_UNLOCK(&confs);
- if (!concise)
- ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
- return CLI_SUCCESS;
- }
- if (a->argc < 3)
- return CLI_SHOWUSAGE;
- ast_copy_string(cmdline, a->argv[2], sizeof(cmdline)); /* Argv 2: conference number */
- if (strstr(a->argv[1], "lock")) {
- if (strcmp(a->argv[1], "lock") == 0) {
- /* Lock */
- strncat(cmdline, ",L", sizeof(cmdline) - strlen(cmdline) - 1);
- } else {
- /* Unlock */
- strncat(cmdline, ",l", sizeof(cmdline) - strlen(cmdline) - 1);
- }
- } else if (strstr(a->argv[1], "mute")) {
- if (a->argc < 4)
- return CLI_SHOWUSAGE;
- if (strcmp(a->argv[1], "mute") == 0) {
- /* Mute */
- if (strcmp(a->argv[3], "all") == 0) {
- strncat(cmdline, ",N", sizeof(cmdline) - strlen(cmdline) - 1);
- } else {
- strncat(cmdline, ",M,", sizeof(cmdline) - strlen(cmdline) - 1);
- strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
- }
- } else {
- /* Unmute */
- if (strcmp(a->argv[3], "all") == 0) {
- strncat(cmdline, ",n", sizeof(cmdline) - strlen(cmdline) - 1);
- } else {
- strncat(cmdline, ",m,", sizeof(cmdline) - strlen(cmdline) - 1);
- strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
- }
- }
- } else if (strcmp(a->argv[1], "kick") == 0) {
- if (a->argc < 4)
- return CLI_SHOWUSAGE;
- if (strcmp(a->argv[3], "all") == 0) {
- /* Kick all */
- strncat(cmdline, ",K", sizeof(cmdline) - strlen(cmdline) - 1);
- } else {
- /* Kick a single user */
- strncat(cmdline, ",k,", sizeof(cmdline) - strlen(cmdline) - 1);
- strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
- }
- } else if (strcmp(a->argv[1], "list") == 0) {
- int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
- /* List all the users in a conference */
- if (AST_LIST_EMPTY(&confs)) {
- if (!concise)
- ast_cli(a->fd, "No active conferences.\n");
- return CLI_SUCCESS;
- }
- /* Find the right conference */
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- if (strcmp(cnf->confno, a->argv[2]) == 0)
- break;
- }
- if (!cnf) {
- if (!concise)
- ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
- AST_LIST_UNLOCK(&confs);
- return CLI_SUCCESS;
- }
- /* Show all the users */
- time(&now);
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
- hr = (now - user->jointime) / 3600;
- min = ((now - user->jointime) % 3600) / 60;
- sec = (now - user->jointime) % 60;
- if (!concise)
- ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
- user->user_no,
- S_OR(user->chan->cid.cid_num, "<unknown>"),
- S_OR(user->chan->cid.cid_name, "<no name>"),
- user->chan->name,
- user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
- user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
- user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
- user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
- istalking(user->talking), hr, min, sec);
- else
- ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
- user->user_no,
- S_OR(user->chan->cid.cid_num, ""),
- S_OR(user->chan->cid.cid_name, ""),
- user->chan->name,
- user->userflags & CONFFLAG_ADMIN ? "1" : "",
- user->userflags & CONFFLAG_MONITOR ? "1" : "",
- user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
- user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
- user->talking, hr, min, sec);
-
- }
- if (!concise)
- ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
- AST_LIST_UNLOCK(&confs);
- return CLI_SUCCESS;
- } else
- return CLI_SHOWUSAGE;
-
- ast_debug(1, "Cmdline: %s\n", cmdline);
-
- admin_exec(NULL, cmdline);
-
- return CLI_SUCCESS;
-}
-
-static const char *sla_hold_str(unsigned int hold_access)
-{
- const char *hold = "Unknown";
-
- switch (hold_access) {
- case SLA_HOLD_OPEN:
- hold = "Open";
- break;
- case SLA_HOLD_PRIVATE:
- hold = "Private";
- default:
- break;
- }
-
- return hold;
-}
-
-static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- const struct sla_trunk *trunk;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "sla show trunks";
- e->usage =
- "Usage: sla show trunks\n"
- " This will list all trunks defined in sla.conf\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- ast_cli(a->fd, "\n"
- "=============================================================\n"
- "=== Configured SLA Trunks ===================================\n"
- "=============================================================\n"
- "===\n");
- AST_RWLIST_RDLOCK(&sla_trunks);
- AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
- struct sla_station_ref *station_ref;
- char ring_timeout[16] = "(none)";
- if (trunk->ring_timeout)
- snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
- ast_cli(a->fd, "=== ---------------------------------------------------------\n"
- "=== Trunk Name: %s\n"
- "=== ==> Device: %s\n"
- "=== ==> AutoContext: %s\n"
- "=== ==> RingTimeout: %s\n"
- "=== ==> BargeAllowed: %s\n"
- "=== ==> HoldAccess: %s\n"
- "=== ==> Stations ...\n",
- trunk->name, trunk->device,
- S_OR(trunk->autocontext, "(none)"),
- ring_timeout,
- trunk->barge_disabled ? "No" : "Yes",
- sla_hold_str(trunk->hold_access));
- AST_RWLIST_RDLOCK(&sla_stations);
- AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
- ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
- AST_RWLIST_UNLOCK(&sla_stations);
- ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
- }
- AST_RWLIST_UNLOCK(&sla_trunks);
- ast_cli(a->fd, "=============================================================\n\n");
-
- return CLI_SUCCESS;
-}
-
-static const char *trunkstate2str(enum sla_trunk_state state)
-{
-#define S(e) case e: return # e;
- switch (state) {
- S(SLA_TRUNK_STATE_IDLE)
- S(SLA_TRUNK_STATE_RINGING)
- S(SLA_TRUNK_STATE_UP)
- S(SLA_TRUNK_STATE_ONHOLD)
- S(SLA_TRUNK_STATE_ONHOLD_BYME)
- }
- return "Uknown State";
-#undef S
-}
-
-static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- const struct sla_station *station;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "sla show stations";
- e->usage =
- "Usage: sla show stations\n"
- " This will list all stations defined in sla.conf\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- ast_cli(a->fd, "\n"
- "=============================================================\n"
- "=== Configured SLA Stations =================================\n"
- "=============================================================\n"
- "===\n");
- AST_RWLIST_RDLOCK(&sla_stations);
- AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
- struct sla_trunk_ref *trunk_ref;
- char ring_timeout[16] = "(none)";
- char ring_delay[16] = "(none)";
- if (station->ring_timeout) {
- snprintf(ring_timeout, sizeof(ring_timeout),
- "%u", station->ring_timeout);
- }
- if (station->ring_delay) {
- snprintf(ring_delay, sizeof(ring_delay),
- "%u", station->ring_delay);
- }
- ast_cli(a->fd, "=== ---------------------------------------------------------\n"
- "=== Station Name: %s\n"
- "=== ==> Device: %s\n"
- "=== ==> AutoContext: %s\n"
- "=== ==> RingTimeout: %s\n"
- "=== ==> RingDelay: %s\n"
- "=== ==> HoldAccess: %s\n"
- "=== ==> Trunks ...\n",
- station->name, station->device,
- S_OR(station->autocontext, "(none)"),
- ring_timeout, ring_delay,
- sla_hold_str(station->hold_access));
- AST_RWLIST_RDLOCK(&sla_trunks);
- AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (trunk_ref->ring_timeout) {
- snprintf(ring_timeout, sizeof(ring_timeout),
- "%u", trunk_ref->ring_timeout);
- } else
- strcpy(ring_timeout, "(none)");
- if (trunk_ref->ring_delay) {
- snprintf(ring_delay, sizeof(ring_delay),
- "%u", trunk_ref->ring_delay);
- } else
- strcpy(ring_delay, "(none)");
- ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
- "=== ==> State: %s\n"
- "=== ==> RingTimeout: %s\n"
- "=== ==> RingDelay: %s\n",
- trunk_ref->trunk->name,
- trunkstate2str(trunk_ref->state),
- ring_timeout, ring_delay);
- }
- AST_RWLIST_UNLOCK(&sla_trunks);
- ast_cli(a->fd, "=== ---------------------------------------------------------\n"
- "===\n");
- }
- AST_RWLIST_UNLOCK(&sla_stations);
- ast_cli(a->fd, "============================================================\n"
- "\n");
-
- return CLI_SUCCESS;
-}
-
-static struct ast_cli_entry cli_meetme[] = {
- AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
- AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
- AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
-};
-
-static void conf_flush(int fd, struct ast_channel *chan)
-{
- int x;
-
- /* read any frames that may be waiting on the channel
- and throw them away
- */
- if (chan) {
- struct ast_frame *f;
-
- /* when no frames are available, this will wait
- for 1 millisecond maximum
- */
- while (ast_waitfor(chan, 1)) {
- f = ast_read(chan);
- if (f)
- ast_frfree(f);
- else /* channel was hung up or something else happened */
- break;
- }
- }
-
- /* flush any data sitting in the pseudo channel */
- x = ZT_FLUSH_ALL;
- if (ioctl(fd, ZT_FLUSH, &x))
- ast_log(LOG_WARNING, "Error flushing channel\n");
-
-}
-
-/* Remove the conference from the list and free it.
- We assume that this was called while holding conflock. */
-static int conf_free(struct ast_conference *conf)
-{
- int x;
-
- AST_LIST_REMOVE(&confs, conf, list);
- manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
-
- if (conf->recording == MEETME_RECORD_ACTIVE) {
- conf->recording = MEETME_RECORD_TERMINATE;
- AST_LIST_UNLOCK(&confs);
- while (1) {
- usleep(1);
- AST_LIST_LOCK(&confs);
- if (conf->recording == MEETME_RECORD_OFF)
- break;
- AST_LIST_UNLOCK(&confs);
- }
- }
-
- for (x = 0; x < AST_FRAME_BITS; x++) {
- if (conf->transframe[x])
- ast_frfree(conf->transframe[x]);
- if (conf->transpath[x])
- ast_translator_free_path(conf->transpath[x]);
- }
- if (conf->origframe)
- ast_frfree(conf->origframe);
- if (conf->lchan)
- ast_hangup(conf->lchan);
- if (conf->chan)
- ast_hangup(conf->chan);
- if (conf->fd >= 0)
- close(conf->fd);
-
- ast_mutex_destroy(&conf->playlock);
- ast_mutex_destroy(&conf->listenlock);
- ast_mutex_destroy(&conf->recordthreadlock);
- ast_free(conf);
-
- return 0;
-}
-
-static void conf_queue_dtmf(const struct ast_conference *conf,
- const struct ast_conf_user *sender, struct ast_frame *f)
-{
- struct ast_conf_user *user;
-
- AST_LIST_TRAVERSE(&conf->userlist, user, list) {
- if (user == sender)
- continue;
- if (ast_write(user->chan, f) < 0)
- ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
- }
-}
-
-static void sla_queue_event_full(enum sla_event_type type,
- struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
-{
- struct sla_event *event;
-
- if (!(event = ast_calloc(1, sizeof(*event))))
- return;
-
- event->type = type;
- event->trunk_ref = trunk_ref;
- event->station = station;
-
- if (!lock) {
- AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
- return;
- }
-
- ast_mutex_lock(&sla.lock);
- AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
- ast_cond_signal(&sla.cond);
- ast_mutex_unlock(&sla.lock);
-}
-
-static void sla_queue_event_nolock(enum sla_event_type type)
-{
- sla_queue_event_full(type, NULL, NULL, 0);
-}
-
-static void sla_queue_event(enum sla_event_type type)
-{
- sla_queue_event_full(type, NULL, NULL, 1);
-}
-
-/*! \brief Queue a SLA event from the conference */
-static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
- struct ast_conference *conf)
-{
- struct sla_station *station;
- struct sla_trunk_ref *trunk_ref = NULL;
- char *trunk_name;
-
- trunk_name = ast_strdupa(conf->confno);
- strsep(&trunk_name, "_");
- if (ast_strlen_zero(trunk_name)) {
- ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
- return;
- }
-
- AST_RWLIST_RDLOCK(&sla_stations);
- AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
- AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
- break;
- }
- if (trunk_ref)
- break;
- }
- AST_RWLIST_UNLOCK(&sla_stations);
-
- if (!trunk_ref) {
- ast_debug(1, "Trunk not found for event!\n");
- return;
- }
-
- sla_queue_event_full(type, trunk_ref, station, 1);
-}
-
-/* Decrement reference counts, as incremented by find_conf() */
-static int dispose_conf(struct ast_conference *conf)
-{
- int res = 0;
- int confno_int = 0;
-
- AST_LIST_LOCK(&confs);
- if (ast_atomic_dec_and_test(&conf->refcount)) {
- /* Take the conference room number out of an inuse state */
- if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
- conf_map[confno_int] = 0;
- conf_free(conf);
- res = 1;
- }
- AST_LIST_UNLOCK(&confs);
-
- return res;
-}
-
-static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
-{
- char *original_moh;
-
- ast_channel_lock(chan);
- original_moh = ast_strdupa(chan->musicclass);
- ast_string_field_set(chan, musicclass, musicclass);
- ast_channel_unlock(chan);
-
- ast_moh_start(chan, original_moh, NULL);
-
- ast_channel_lock(chan);
- ast_string_field_set(chan, musicclass, original_moh);
- ast_channel_unlock(chan);
-}
-
-static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
-{
- struct ast_conf_user *user = NULL;
- struct ast_conf_user *usr = NULL;
- int fd;
- struct zt_confinfo ztc, ztc_empty;
- 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 musiconhold = 0;
- int firstpass = 0;
- int lastmarked = 0;
- int currentmarked = 0;
- int ret = -1;
- int x;
- int menu_active = 0;
- int talkreq_manager = 0;
- int using_pseudo = 0;
- int duration = 20;
- int hr, min, sec;
- int sent_event = 0;
- int checked = 0;
- int announcement_played = 0;
- struct timeval now;
- struct ast_dsp *dsp = NULL;
- struct ast_app *app;
- const char *agifile;
- const char *agifiledefault = "conf-background.agi";
- char meetmesecs[30] = "";
- char exitcontext[AST_MAX_CONTEXT] = "";
- char recordingtmp[AST_MAX_EXTENSION] = "";
- char members[10] = "";
- int dtmf, opt_waitmarked_timeout = 0;
- time_t timeout = 0;
- ZT_BUFFERINFO bi;
- char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
- char *buf = __buf + AST_FRIENDLY_OFFSET;
- char *exitkeys = NULL;
- unsigned int calldurationlimit = 0;
- long timelimit = 0;
- long play_warning = 0;
- long warning_freq = 0;
- const char *warning_sound = NULL;
- const char *end_sound = NULL;
- char *parse;
- long time_left_ms = 0;
- struct timeval nexteventts = { 0, };
- int to;
- int setusercount = 0;
-
- if (!(user = ast_calloc(1, sizeof(*user))))
- return ret;
-
- /* Possible timeout waiting for marked user */
- if ((confflags & CONFFLAG_WAITMARKED) &&
- !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
- (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
- (opt_waitmarked_timeout > 0)) {
- timeout = time(NULL) + opt_waitmarked_timeout;
- }
-
- if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
- calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
- ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
- }
-
- if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
- char *limit_str, *warning_str, *warnfreq_str;
- const char *var;
-
- parse = optargs[OPT_ARG_DURATION_LIMIT];
- limit_str = strsep(&parse, ":");
- warning_str = strsep(&parse, ":");
- warnfreq_str = parse;
-
- timelimit = atol(limit_str);
- if (warning_str)
- play_warning = atol(warning_str);
- if (warnfreq_str)
- warning_freq = atol(warnfreq_str);
-
- if (!timelimit) {
- timelimit = play_warning = warning_freq = 0;
- warning_sound = NULL;
- } else if (play_warning > timelimit) {
- if (!warning_freq) {
- play_warning = 0;
- } else {
- while (play_warning > timelimit)
- play_warning -= warning_freq;
- if (play_warning < 1)
- play_warning = warning_freq = 0;
- }
- }
-
- var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE");
- warning_sound = var ? var : "timeleft";
-
- var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE");
- end_sound = var ? var : NULL;
-
- /* undo effect of S(x) in case they are both used */
- calldurationlimit = 0;
- /* more efficient do it like S(x) does since no advanced opts */
- if (!play_warning && !end_sound && timelimit) {
- calldurationlimit = timelimit / 1000;
- timelimit = play_warning = warning_freq = 0;
- } else {
- ast_debug(2, "Limit Data for this call:\n");
- ast_debug(2, "- timelimit = %ld\n", timelimit);
- ast_debug(2, "- play_warning = %ld\n", play_warning);
- ast_debug(2, "- warning_freq = %ld\n", warning_freq);
- ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
- ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
- }
- }
-
- /* Get exit keys */
- if ((confflags & CONFFLAG_KEYEXIT)) {
- if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
- exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
- else
- exitkeys = ast_strdupa("#"); /* Default */
- }
-
- if (confflags & CONFFLAG_RECORDCONF) {
- if (!conf->recordingfilename) {
- conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
- if (!conf->recordingfilename) {
- snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
- conf->recordingfilename = ast_strdupa(recordingtmp);
- }
- conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
- if (!conf->recordingformat) {
- ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
- conf->recordingformat = ast_strdupa(recordingtmp);
- }
- ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
- conf->confno, conf->recordingfilename, conf->recordingformat);
- }
- }
-
- ast_mutex_lock(&conf->recordthreadlock);
- if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
- ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
- ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
- ztc.chan = 0;
- ztc.confno = conf->zapconf;
- ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
- if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
- ast_log(LOG_WARNING, "Error starting listen channel\n");
- ast_hangup(conf->lchan);
- conf->lchan = NULL;
- } else {
- ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
- }
- }
- ast_mutex_unlock(&conf->recordthreadlock);
-
- time(&user->jointime);
-
- user->timelimit = timelimit;
- user->play_warning = play_warning;
- user->warning_freq = warning_freq;
- user->warning_sound = warning_sound;
- user->end_sound = end_sound;
-
- if (calldurationlimit > 0) {
- time(&user->kicktime);
- user->kicktime = user->kicktime + calldurationlimit;
- }
-
- if (ast_tvzero(user->start_time))
- user->start_time = ast_tvnow();
- time_left_ms = user->timelimit;
-
- if (user->timelimit) {
- nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
- nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
- }
-
- if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
- /* Sorry, but this conference is locked! */
- if (!ast_streamfile(chan, "conf-locked", chan->language))
- ast_waitstream(chan, "");
- goto outrun;
- }
-
- ast_mutex_lock(&conf->playlock);
-
- if (AST_LIST_EMPTY(&conf->userlist))
- user->user_no = 1;
- else
- user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
-
- if (rt_schedule && conf->maxusers)
- if (user->user_no > conf->maxusers) {
- /* Sorry, but this confernce has reached the participant limit! */
- if (!ast_streamfile(chan, "conf-full", chan->language))
- ast_waitstream(chan, "");
- goto outrun;
- }
-
- AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
-
- user->chan = chan;
- user->userflags = confflags;
- user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
- user->talking = -1;
-
- ast_mutex_unlock(&conf->playlock);
-
- if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
- snprintf(user->namerecloc, sizeof(user->namerecloc),
- "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
- conf->confno, user->user_no);
- if (confflags & CONFFLAG_INTROUSERNOREVIEW)
- res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
- else
- res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
- if (res == -1)
- goto outrun;
- }
-
- ast_mutex_lock(&conf->playlock);
-
- if (confflags & CONFFLAG_MARKEDUSER)
- conf->markedusers++;
- conf->users++;
- if (rt_log_members) {
- /* Update table */
- snprintf(members, sizeof(members), "%d", conf->users);
- ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
- }
- setusercount = 1;
-
- /* This device changed state now - if this is the first user */
- if (conf->users == 1)
- ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
-
- ast_mutex_unlock(&conf->playlock);
-
- /* return the unique ID of the conference */
- pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
-
- if (confflags & CONFFLAG_EXIT_CONTEXT) {
- if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
- ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
- else if (!ast_strlen_zero(chan->macrocontext))
- ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
- else
- ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
- }
-
- if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
- if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
- if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
- ast_waitstream(chan, "");
- if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
- if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
- ast_waitstream(chan, "");
- }
-
- if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
- int keepplaying = 1;
-
- if (conf->users == 2) {
- if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
- res = ast_waitstream(chan, AST_DIGIT_ANY);
- ast_stopstream(chan);
- if (res > 0)
- keepplaying = 0;
- else if (res == -1)
- goto outrun;
- }
- } else {
- if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
- res = ast_waitstream(chan, AST_DIGIT_ANY);
- ast_stopstream(chan);
- if (res > 0)
- keepplaying = 0;
- else if (res == -1)
- goto outrun;
- }
- if (keepplaying) {
- res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
- if (res > 0)
- keepplaying = 0;
- else if (res == -1)
- goto outrun;
- }
- if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
- res = ast_waitstream(chan, AST_DIGIT_ANY);
- ast_stopstream(chan);
- if (res > 0)
- keepplaying = 0;
- else if (res == -1)
- goto outrun;
- }
- }
- }
-
- ast_indicate(chan, -1);
-
- if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
- ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
- goto outrun;
- }
-
- if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
- ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
- goto outrun;
- }
-
- retryzap = (strcasecmp(chan->tech->type, "Zap") || (chan->audiohooks || chan->monitor) ? 1 : 0);
- user->zapchannel = !retryzap;
-
- 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;
- }
- using_pseudo = 1;
- /* 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 / 2;
- bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
- bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
- bi.numbufs = audio_buffers;
- if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
- ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
- close(fd);
- goto outrun;
- }
- x = 1;
- if (ioctl(fd, ZT_SETLINEAR, &x)) {
- ast_log(LOG_WARNING, "Unable to set linear mode: %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));
- memset(&ztc_empty, 0, sizeof(ztc_empty));
- /* 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_debug(1, "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 = conf->zapconf;
-
- ast_mutex_lock(&conf->playlock);
-
- if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
- if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
- if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
- ast_waitstream(conf->chan, "");
- if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
- ast_waitstream(conf->chan, "");
- }
- }
-
- if (confflags & CONFFLAG_MONITOR)
- ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
- else if (confflags & CONFFLAG_TALKER)
- ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
- else
- ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
-
- if (ioctl(fd, ZT_SETCONF, &ztc)) {
- ast_log(LOG_WARNING, "Error setting conference\n");
- close(fd);
- ast_mutex_unlock(&conf->playlock);
- goto outrun;
- }
- ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
-
- if (!sent_event) {
- manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Meetme: %s\r\n"
- "Usernum: %d\r\n"
- "CallerIDnum: %s\r\n"
- "CallerIDname: %s\r\n",
- chan->name, chan->uniqueid, conf->confno,
- user->user_no,
- S_OR(user->chan->cid.cid_num, "<unknown>"),
- S_OR(user->chan->cid.cid_name, "<unknown>")
- );
- sent_event = 1;
- }
-
- if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
- firstpass = 1;
- if (!(confflags & CONFFLAG_QUIET))
- if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
- conf_play(chan, conf, ENTER);
- }
-
- ast_mutex_unlock(&conf->playlock);
-
- conf_flush(fd, chan);
-
- if (confflags & CONFFLAG_AGI) {
- /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
- or use default filename of conf-background.agi */
-
- agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
- if (!agifile)
- agifile = agifiledefault;
-
- if (user->zapchannel) {
- /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
- x = 1;
- ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
- }
- /* Find a pointer to the agi app and execute the script */
- app = pbx_findapp("agi");
- if (app) {
- char *s = ast_strdupa(agifile);
- ret = pbx_exec(chan, app, s);
- } else {
- ast_log(LOG_WARNING, "Could not find application (agi)\n");
- ret = -2;
- }
- if (user->zapchannel) {
- /* Remove CONFMUTE mode on Zap channel */
- x = 0;
- ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
- }
- } else {
- if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
- /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
- x = 1;
- ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
- }
- if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
- ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
- res = -1;
- }
- for (;;) {
- int menu_was_active = 0;
-
- outfd = -1;
- ms = -1;
- now = ast_tvnow();
-
- if (rt_schedule) {
- if (now.tv_sec % 60 == 0) {
- if (!checked) {
- if (now.tv_sec > conf->endtime) {
- ast_verbose("Quitting time...\n");
- goto outrun;
- }
-
- if (!announcement_played && conf->endalert) {
- if (now.tv_sec + conf->endalert > conf->endtime) {
- if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
- ast_waitstream(chan, "");
- ast_say_digits(chan, (now.tv_sec + conf->endalert - conf->endtime) / 60, "", chan->language);
- if (!ast_streamfile(chan, "minutes", chan->language))
- ast_waitstream(chan, "");
- announcement_played = 1;
- }
- }
- checked = 1;
-
- }
- } else {
- checked = 0;
- }
- }
-
- if (user->kicktime && (user->kicktime <= now.tv_sec))
- break;
-
- to = -1;
- if (user->timelimit) {
- int minutes = 0, seconds = 0, remain = 0;
-
- to = ast_tvdiff_ms(nexteventts, now);
- if (to < 0)
- to = 0;
- time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
- if (time_left_ms < to)
- to = time_left_ms;
-
- if (time_left_ms <= 0) {
- if (user->end_sound) {
- res = ast_streamfile(chan, user->end_sound, chan->language);
- res = ast_waitstream(chan, "");
- }
- break;
- }
-
- if (!to) {
- if (time_left_ms >= 5000) {
-
- remain = (time_left_ms + 500) / 1000;
- if (remain / 60 >= 1) {
- minutes = remain / 60;
- seconds = remain % 60;
- } else {
- seconds = remain;
- }
-
- /* force the time left to round up if appropriate */
- if (user->warning_sound && user->play_warning) {
- if (!strcmp(user->warning_sound, "timeleft")) {
-
- res = ast_streamfile(chan, "vm-youhave", chan->language);
- res = ast_waitstream(chan, "");
- if (minutes) {
- res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
- res = ast_streamfile(chan, "queue-minutes", chan->language);
- res = ast_waitstream(chan, "");
- }
- if (seconds) {
- res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
- res = ast_streamfile(chan, "queue-seconds", chan->language);
- res = ast_waitstream(chan, "");
- }
- } else {
- res = ast_streamfile(chan, user->warning_sound, chan->language);
- res = ast_waitstream(chan, "");
- }
- }
- }
- if (user->warning_freq)
- nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
- else
- nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
- }
- }
-
- now = ast_tvnow();
- if (timeout && now.tv_sec >= timeout)
- break;
-
- /* if we have just exited from the menu, and the user had a channel-driver
- volume adjustment, restore it
- */
- if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
- set_talk_volume(user, user->listen.desired);
-
- menu_was_active = menu_active;
-
- currentmarked = conf->markedusers;
- if (!(confflags & CONFFLAG_QUIET) &&
- (confflags & CONFFLAG_MARKEDUSER) &&
- (confflags & CONFFLAG_WAITMARKED) &&
- lastmarked == 0) {
- if (currentmarked == 1 && conf->users > 1) {
- ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
- if (conf->users - 1 == 1) {
- if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
- ast_waitstream(chan, "");
- } else {
- if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
- ast_waitstream(chan, "");
- }
- }
- if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
- if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
- ast_waitstream(chan, "");
- }
-
- c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
-
- /* Update the struct with the actual confflags */
- user->userflags = confflags;
-
- if (confflags & CONFFLAG_WAITMARKED) {
- if (currentmarked == 0) {
- if (lastmarked != 0) {
- if (!(confflags & CONFFLAG_QUIET))
- if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
- ast_waitstream(chan, "");
- if (confflags & CONFFLAG_MARKEDEXIT) {
- if (confflags & CONFFLAG_KICK_CONTINUE)
- ret = 0;
- break;
- } else {
- ztc.confmode = ZT_CONF_CONF;
- if (ioctl(fd, ZT_SETCONF, &ztc)) {
- ast_log(LOG_WARNING, "Error setting conference\n");
- close(fd);
- goto outrun;
- }
- }
- }
- if (!musiconhold && (confflags & CONFFLAG_MOH)) {
- conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
- musiconhold = 1;
- }
- } else if (currentmarked >= 1 && lastmarked == 0) {
- /* Marked user entered, so cancel timeout */
- timeout = 0;
- if (confflags & CONFFLAG_MONITOR)
- ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
- else if (confflags & CONFFLAG_TALKER)
- ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
- else
- ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
- if (ioctl(fd, ZT_SETCONF, &ztc)) {
- ast_log(LOG_WARNING, "Error setting conference\n");
- close(fd);
- goto outrun;
- }
- if (musiconhold && (confflags & CONFFLAG_MOH)) {
- ast_moh_stop(chan);
- musiconhold = 0;
- }
- if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
- if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
- ast_waitstream(chan, "");
- conf_play(chan, conf, ENTER);
- }
- }
- }
-
- /* trying to add moh for single person conf */
- if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
- if (conf->users == 1) {
- if (!musiconhold) {
- conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
- musiconhold = 1;
- }
- } else {
- if (musiconhold) {
- ast_moh_stop(chan);
- musiconhold = 0;
- }
- }
- }
-
- /* Leave if the last marked user left */
- if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
- if (confflags & CONFFLAG_KICK_CONTINUE)
- ret = 0;
- else
- ret = -1;
- break;
- }
-
- /* Check if my modes have changed */
-
- /* If I should be muted but am still talker, mute me */
- if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
- ztc.confmode ^= ZT_CONF_TALKER;
- if (ioctl(fd, ZT_SETCONF, &ztc)) {
- ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
- ret = -1;
- break;
- }
-
- manager_event(EVENT_FLAG_CALL, "MeetmeMute",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Meetme: %s\r\n"
- "Usernum: %i\r\n"
- "Status: on\r\n",
- chan->name, chan->uniqueid, conf->confno, user->user_no);
- }
-
- /* If I should be un-muted but am not talker, un-mute me */
- if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
- ztc.confmode |= ZT_CONF_TALKER;
- if (ioctl(fd, ZT_SETCONF, &ztc)) {
- ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
- ret = -1;
- break;
- }
-
- manager_event(EVENT_FLAG_CALL, "MeetmeMute",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Meetme: %s\r\n"
- "Usernum: %i\r\n"
- "Status: off\r\n",
- chan->name, chan->uniqueid, conf->confno, user->user_no);
- }
-
- if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
- (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
- talkreq_manager = 1;
-
- manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Meetme: %s\r\n"
- "Usernum: %i\r\n"
- "Status: on\r\n",
- chan->name, chan->uniqueid, conf->confno, user->user_no);
- }
-
-
- if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
- !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
- talkreq_manager = 0;
- manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Meetme: %s\r\n"
- "Usernum: %i\r\n"
- "Status: off\r\n",
- chan->name, chan->uniqueid, conf->confno, user->user_no);
- }
-
- /* If I have been kicked, exit the conference */
- if (user->adminflags & ADMINFLAG_KICKME) {
- /* You have been kicked. */
- if (!(confflags & CONFFLAG_QUIET) &&
- !ast_streamfile(chan, "conf-kicked", chan->language)) {
- ast_waitstream(chan, "");
- }
- ret = 0;
- break;
- }
-
- /* Perform an extra hangup check just in case */
- if (ast_check_hangup(chan))
- break;
-
- if (c) {
- if (c->fds[0] != origfd || (user->zapchannel && (c->audiohooks || c->monitor))) {
- if (using_pseudo) {
- /* Kill old pseudo */
- close(fd);
- using_pseudo = 0;
- }
- ast_debug(1, "Ooh, something swapped out under us, starting over\n");
- retryzap = (strcasecmp(c->tech->type, "Zap") || (c->audiohooks || c->monitor) ? 1 : 0);
- user->zapchannel = !retryzap;
- goto zapretry;
- }
- if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
- f = ast_read_noaudio(c);
- else
- f = ast_read(c);
- if (!f)
- break;
- if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
- if (user->talk.actual)
- ast_frame_adjust_volume(f, user->talk.actual);
-
- if (!(confflags & CONFFLAG_MONITOR)) {
- int totalsilence;
-
- if (user->talking == -1)
- user->talking = 0;
-
- res = ast_dsp_silence(dsp, f, &totalsilence);
- if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
- user->talking = 1;
- if (confflags & CONFFLAG_MONITORTALKER)
- manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Meetme: %s\r\n"
- "Usernum: %d\r\n"
- "Status: on\r\n",
- chan->name, chan->uniqueid, conf->confno, user->user_no);
- }
- if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
- user->talking = 0;
- if (confflags & CONFFLAG_MONITORTALKER)
- manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Meetme: %s\r\n"
- "Usernum: %d\r\n"
- "Status: off\r\n",
- chan->name, chan->uniqueid, conf->confno, user->user_no);
- }
- }
- if (using_pseudo) {
- /* Absolutely do _not_ use careful_write here...
- it is important that we read data from the channel
- as fast as it arrives, and feed it into the conference.
- The buffering in the pseudo channel will take care of any
- timing differences, unless they are so drastic as to lose
- audio frames (in which case carefully writing would only
- have delayed the audio even further).
- */
- /* As it turns out, we do want to use careful write. We just
- don't want to block, but we do want to at least *try*
- to write out all the samples.
- */
- if (user->talking)
- careful_write(fd, f->data, f->datalen, 0);
- }
- } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
- char tmp[2];
-
- if (confflags & CONFFLAG_PASS_DTMF)
- conf_queue_dtmf(conf, user, f);
-
- tmp[0] = f->subclass;
- tmp[1] = '\0';
- if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
- ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
- ret = 0;
- ast_frfree(f);
- break;
- } else {
- ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
- }
- } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
- char exitkey[2];
-
- exitkey[0] = f->subclass;
- exitkey[1] = '\0';
-
- pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
-
- if (confflags & CONFFLAG_PASS_DTMF)
- conf_queue_dtmf(conf, user, f);
- ret = 0;
- ast_frfree(f);
- break;
- } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
- if (confflags & CONFFLAG_PASS_DTMF)
- conf_queue_dtmf(conf, user, f);
- if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
- ast_log(LOG_WARNING, "Error setting conference\n");
- close(fd);
- ast_frfree(f);
- goto outrun;
- }
-
- /* if we are entering the menu, and the user has a channel-driver
- volume adjustment, clear it
- */
- if (!menu_active && user->talk.desired && !user->talk.actual)
- set_talk_volume(user, 0);
-
- if (musiconhold) {
- ast_moh_stop(chan);
- }
- if ((confflags & CONFFLAG_ADMIN)) {
- /* Admin menu */
- if (!menu_active) {
- menu_active = 1;
- /* Record this sound! */
- if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
- dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
- ast_stopstream(chan);
- } else
- dtmf = 0;
- } else
- dtmf = f->subclass;
- if (dtmf) {
- switch(dtmf) {
- case '1': /* Un/Mute */
- menu_active = 0;
-
- /* for admin, change both admin and use flags */
- if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
- user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
- else
- user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
-
- if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
- if (!ast_streamfile(chan, "conf-muted", chan->language))
- ast_waitstream(chan, "");
- } else {
- if (!ast_streamfile(chan, "conf-unmuted", chan->language))
- ast_waitstream(chan, "");
- }
- break;
- case '2': /* Un/Lock the Conference */
- menu_active = 0;
- if (conf->locked) {
- conf->locked = 0;
- if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
- ast_waitstream(chan, "");
- } else {
- conf->locked = 1;
- if (!ast_streamfile(chan, "conf-lockednow", chan->language))
- ast_waitstream(chan, "");
- }
- break;
- case '3': /* Eject last user */
- menu_active = 0;
- usr = AST_LIST_LAST(&conf->userlist);
- if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
- if (!ast_streamfile(chan, "conf-errormenu", chan->language))
- ast_waitstream(chan, "");
- } else
- usr->adminflags |= ADMINFLAG_KICKME;
- ast_stopstream(chan);
- break;
- case '4':
- tweak_listen_volume(user, VOL_DOWN);
- break;
- case '6':
- tweak_listen_volume(user, VOL_UP);
- break;
- case '7':
- tweak_talk_volume(user, VOL_DOWN);
- break;
- case '8':
- menu_active = 0;
- break;
- case '9':
- tweak_talk_volume(user, VOL_UP);
- break;
- default:
- menu_active = 0;
- /* Play an error message! */
- if (!ast_streamfile(chan, "conf-errormenu", chan->language))
- ast_waitstream(chan, "");
- break;
- }
- }
- } else {
- /* User menu */
- if (!menu_active) {
- menu_active = 1;
- if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
- dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
- ast_stopstream(chan);
- } else
- dtmf = 0;
- } else
- dtmf = f->subclass;
- if (dtmf) {
- switch(dtmf) {
- case '1': /* Un/Mute */
- menu_active = 0;
-
- /* user can only toggle the self-muted state */
- user->adminflags ^= ADMINFLAG_SELFMUTED;
-
- /* they can't override the admin mute state */
- if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
- if (!ast_streamfile(chan, "conf-muted", chan->language))
- ast_waitstream(chan, "");
- } else {
- if (!ast_streamfile(chan, "conf-unmuted", chan->language))
- ast_waitstream(chan, "");
- }
- break;
- case '2':
- menu_active = 0;
- if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
- user->adminflags |= ADMINFLAG_T_REQUEST;
-
- if (user->adminflags & ADMINFLAG_T_REQUEST)
- if (!ast_streamfile(chan, "beep", chan->language))
- ast_waitstream(chan, "");
- break;
- case '4':
- tweak_listen_volume(user, VOL_DOWN);
- break;
- case '6':
- tweak_listen_volume(user, VOL_UP);
- break;
- case '7':
- tweak_talk_volume(user, VOL_DOWN);
- break;
- case '8':
- menu_active = 0;
- break;
- case '9':
- tweak_talk_volume(user, VOL_UP);
- break;
- default:
- menu_active = 0;
- if (!ast_streamfile(chan, "conf-errormenu", chan->language))
- ast_waitstream(chan, "");
- break;
- }
- }
- }
- if (musiconhold)
- conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
-
- if (ioctl(fd, ZT_SETCONF, &ztc)) {
- ast_log(LOG_WARNING, "Error setting conference\n");
- close(fd);
- ast_frfree(f);
- goto outrun;
- }
-
- conf_flush(fd, chan);
- } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
- && confflags & CONFFLAG_PASS_DTMF) {
- conf_queue_dtmf(conf, user, f);
- } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
- switch (f->subclass) {
- case AST_CONTROL_HOLD:
- sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
- break;
- default:
- break;
- }
- } else if (f->frametype == AST_FRAME_NULL) {
- /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
- } else {
- ast_debug(1,
- "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
- chan->name, f->frametype, 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_SLINEAR;
- fr.datalen = res;
- fr.samples = res / 2;
- fr.data = buf;
- fr.offset = AST_FRIENDLY_OFFSET;
- if (!user->listen.actual &&
- ((confflags & CONFFLAG_MONITOR) ||
- (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
- (!user->talking)) ) {
- int index;
- for (index = 0; index < AST_FRAME_BITS; index++)
- if (chan->rawwriteformat & (1 << index))
- break;
- if (index >= AST_FRAME_BITS)
- goto bailoutandtrynormal;
- ast_mutex_lock(&conf->listenlock);
- if (!conf->transframe[index]) {
- if (conf->origframe) {
- if (!conf->transpath[index])
- conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
- if (conf->transpath[index]) {
- conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
- if (!conf->transframe[index])
- conf->transframe[index] = &ast_null_frame;
- }
- }
- }
- if (conf->transframe[index]) {
- if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
- if (ast_write(chan, conf->transframe[index]))
- ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
- }
- } else {
- ast_mutex_unlock(&conf->listenlock);
- goto bailoutandtrynormal;
- }
- ast_mutex_unlock(&conf->listenlock);
- } else {
-bailoutandtrynormal:
- if (user->listen.actual)
- ast_frame_adjust_volume(&fr, user->listen.actual);
- if (ast_write(chan, &fr) < 0) {
- ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
- }
- }
- } else
- ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
- }
- lastmarked = currentmarked;
- }
- }
-
- if (musiconhold)
- ast_moh_stop(chan);
-
- if (using_pseudo)
- close(fd);
- else {
- /* Take out of conference */
- ztc.chan = 0;
- ztc.confno = 0;
- ztc.confmode = 0;
- if (ioctl(fd, ZT_SETCONF, &ztc)) {
- ast_log(LOG_WARNING, "Error setting conference\n");
- }
- }
-
- reset_volumes(user);
-
- AST_LIST_LOCK(&confs);
- if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
- conf_play(chan, conf, LEAVE);
-
- if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
- if (ast_fileexists(user->namerecloc, NULL, NULL)) {
- if ((conf->chan) && (conf->users > 1)) {
- if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
- ast_waitstream(conf->chan, "");
- if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
- ast_waitstream(conf->chan, "");
- }
- ast_filedelete(user->namerecloc, NULL);
- }
- }
- AST_LIST_UNLOCK(&confs);
-
- outrun:
- AST_LIST_LOCK(&confs);
-
- if (dsp)
- ast_dsp_free(dsp);
-
- if (user->user_no) { /* Only cleanup users who really joined! */
- now = ast_tvnow();
- hr = (now.tv_sec - user->jointime) / 3600;
- min = ((now.tv_sec - user->jointime) % 3600) / 60;
- sec = (now.tv_sec - user->jointime) % 60;
-
- if (sent_event) {
- manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Meetme: %s\r\n"
- "Usernum: %d\r\n"
- "CallerIDNum: %s\r\n"
- "CallerIDName: %s\r\n"
- "Duration: %ld\r\n",
- chan->name, chan->uniqueid, conf->confno,
- user->user_no,
- S_OR(user->chan->cid.cid_num, "<unknown>"),
- S_OR(user->chan->cid.cid_name, "<unknown>"),
- (long)(now.tv_sec - user->jointime));
- }
-
- if (setusercount) {
- conf->users--;
- if (rt_log_members) {
- /* Update table */
- snprintf(members, sizeof(members), "%d", conf->users);
- ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
- }
- if (confflags & CONFFLAG_MARKEDUSER)
- conf->markedusers--;
- }
- /* Remove ourselves from the list */
- AST_LIST_REMOVE(&conf->userlist, user, list);
-
- /* Change any states */
- if (!conf->users)
- ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
-
- /* Return the number of seconds the user was in the conf */
- snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
- pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
- }
- ast_free(user);
- AST_LIST_UNLOCK(&confs);
-
- return ret;
-}
-
-static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
- char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
- char *optargs[], int *too_early)
-{
- struct ast_variable *var;
- struct ast_conference *cnf;
-
- *too_early = 0;
-
- /* Check first in the conference list */
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- if (!strcmp(confno, cnf->confno))
- break;
- }
- if (cnf) {
- cnf->refcount += refcount;
- }
- AST_LIST_UNLOCK(&confs);
-
- if (!cnf) {
- char *pin = NULL, *pinadmin = NULL; /* For temp use */
- int maxusers = 0;
- struct timeval now;
- char currenttime[19] = "";
- char eatime[19] = "";
- char useropts[32] = "";
- char adminopts[32] = "";
- struct ast_tm tm, etm;
- struct timeval starttime = { .tv_sec = 0 }, endtime = { .tv_sec = 0 };
-
- if (rt_schedule) {
- now = ast_tvnow();
-
- if (fuzzystart)
- now.tv_sec += fuzzystart;
-
- ast_localtime(&now, &tm, NULL);
- ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
-
- if (earlyalert) {
- now.tv_sec += earlyalert;
- ast_localtime(&now, &etm, NULL);
- ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
- } else {
- ast_copy_string(eatime, currenttime, sizeof(eatime));
- }
-
- ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
-
- var = ast_load_realtime("meetme", "confno",
- confno, "starttime <= ", eatime, "endtime >= ",
- currenttime, NULL);
- } else
- var = ast_load_realtime("meetme", "confno", confno, NULL);
-
- if (!var)
- return NULL;
-
- while (var) {
- if (!strcasecmp(var->name, "pin")) {
- pin = ast_strdupa(var->value);
- } else if (!strcasecmp(var->name, "adminpin")) {
- pinadmin = ast_strdupa(var->value);
- } else if (!strcasecmp(var->name, "opts")) {
- ast_copy_string(useropts, var->value, sizeof(useropts));
- } else if (!strcasecmp(var->name, "maxusers")) {
- maxusers = atoi(var->value);
- } else if (!strcasecmp(var->name, "adminopts")) {
- ast_copy_string(adminopts, var->value, sizeof(adminopts));
- } else if (!strcasecmp(var->name, "endtime")) {
- union {
- struct ast_tm atm;
- struct tm tm;
- } t = { { 0, }, };
- strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
- endtime = ast_mktime(&t.atm, NULL);
- } else if (!strcasecmp(var->name, "starttime")) {
- union {
- struct ast_tm atm;
- struct tm tm;
- } t = { { 0, }, };
- strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
- starttime = ast_mktime(&t.atm, NULL);
- }
-
- var = var->next;
- }
- ast_variables_destroy(var);
-
- if (earlyalert) {
- now = ast_tvnow();
-
- if (now.tv_sec + fuzzystart < starttime.tv_sec) {
- /* Announce that the caller is early and exit */
- if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
- ast_waitstream(chan, "");
- *too_early = 1;
- return NULL;
- }
- }
-
- cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
-
- if (cnf) {
- cnf->maxusers = maxusers;
- cnf->endalert = endalert;
- cnf->endtime = endtime.tv_sec;
- }
- }
-
- if (cnf) {
- if (confflags && !cnf->chan &&
- !ast_test_flag(confflags, CONFFLAG_QUIET) &&
- ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
- ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
- ast_clear_flag(confflags, CONFFLAG_INTROUSER);
- }
-
- if (confflags && !cnf->chan &&
- ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
- ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
- ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
- }
- }
-
- return cnf;
-}
-
-
-static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
- char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
-{
- struct ast_config *cfg;
- struct ast_variable *var;
- struct ast_flags config_flags = { 0 };
- struct ast_conference *cnf;
- char *parse;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(confno);
- AST_APP_ARG(pin);
- AST_APP_ARG(pinadmin);
- );
-
- /* Check first in the conference list */
- ast_debug(1, "The requested confno is '%s'?\n", confno);
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
- if (!strcmp(confno, cnf->confno))
- break;
- }
- if (cnf) {
- cnf->refcount += refcount;
- }
- AST_LIST_UNLOCK(&confs);
-
- if (!cnf) {
- if (dynamic) {
- /* No need to parse meetme.conf */
- ast_debug(1, "Building dynamic conference '%s'\n", confno);
- if (dynamic_pin) {
- if (dynamic_pin[0] == 'q') {
- /* Query the user to enter a PIN */
- if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
- return NULL;
- }
- cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
- } else {
- cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
- }
- } else {
- /* Check the config */
- cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
- if (!cfg) {
- ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
- return NULL;
- }
- for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
- if (strcasecmp(var->name, "conf"))
- continue;
-
- if (!(parse = ast_strdupa(var->value)))
- return NULL;
-
- AST_STANDARD_APP_ARGS(args, parse);
- ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
- if (!strcasecmp(args.confno, confno)) {
- /* Bingo it's a valid conference */
- cnf = build_conf(args.confno,
- S_OR(args.pin, ""),
- S_OR(args.pinadmin, ""),
- make, dynamic, refcount, chan);
- break;
- }
- }
- if (!var) {
- ast_debug(1, "%s isn't a valid conference\n", confno);
- }
- ast_config_destroy(cfg);
- }
- } else if (dynamic_pin) {
- /* Correct for the user selecting 'D' instead of 'd' to have
- someone join into a conference that has already been created
- with a pin. */
- if (dynamic_pin[0] == 'q')
- dynamic_pin[0] = '\0';
- }
-
- if (cnf) {
- if (confflags && !cnf->chan &&
- !ast_test_flag(confflags, CONFFLAG_QUIET) &&
- ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
- ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
- ast_clear_flag(confflags, CONFFLAG_INTROUSER);
- }
-
- if (confflags && !cnf->chan &&
- ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
- ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
- ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
- }
- }
-
- return cnf;
-}
-
-/*! \brief The MeetmeCount application */
-static int count_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_conference *conf;
- int count;
- char *localdata;
- char val[80] = "0";
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(confno);
- AST_APP_ARG(varname);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
- return -1;
- }
-
- if (!(localdata = ast_strdupa(data)))
- return -1;
-
- AST_STANDARD_APP_ARGS(args, localdata);
-
- conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
-
- if (conf) {
- count = conf->users;
- dispose_conf(conf);
- conf = NULL;
- } else
- count = 0;
-
- if (!ast_strlen_zero(args.varname)) {
- /* have var so load it and exit */
- snprintf(val, sizeof(val), "%d", count);
- pbx_builtin_setvar_helper(chan, args.varname, val);
- } else {
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
- res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
- }
-
- return res;
-}
-
-/*! \brief The meetme() application */
-static int conf_exec(struct ast_channel *chan, void *data)
-{
- int res = -1;
- char confno[MAX_CONFNUM] = "";
- int allowretry = 0;
- int retrycnt = 0;
- struct ast_conference *cnf = NULL;
- struct ast_flags confflags = {0}, config_flags = { 0 };
- int dynamic = 0;
- int empty = 0, empty_no_pin = 0;
- int always_prompt = 0;
- char *notdata, *info, the_pin[MAX_PIN] = "";
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(confno);
- AST_APP_ARG(options);
- AST_APP_ARG(pin);
- );
- char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
-
- if (ast_strlen_zero(data)) {
- allowretry = 1;
- notdata = "";
- } else {
- notdata = data;
- }
-
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- info = ast_strdupa(notdata);
-
- AST_STANDARD_APP_ARGS(args, info);
-
- if (args.confno) {
- ast_copy_string(confno, args.confno, sizeof(confno));
- if (ast_strlen_zero(confno)) {
- allowretry = 1;
- }
- }
-
- if (args.pin)
- ast_copy_string(the_pin, args.pin, sizeof(the_pin));
-
- if (args.options) {
- ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
- dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
- if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
- strcpy(the_pin, "q");
-
- empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
- empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
- always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
- }
-
- do {
- if (retrycnt > 3)
- allowretry = 0;
- if (empty) {
- int i;
- struct ast_config *cfg;
- struct ast_variable *var;
- int confno_int;
-
- /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
- if ((empty_no_pin) || (!dynamic)) {
- cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
- if (cfg) {
- var = ast_variable_browse(cfg, "rooms");
- while (var) {
- if (!strcasecmp(var->name, "conf")) {
- char *stringp = ast_strdupa(var->value);
- if (stringp) {
- char *confno_tmp = strsep(&stringp, "|,");
- int found = 0;
- if (!dynamic) {
- /* For static: run through the list and see if this conference is empty */
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- if (!strcmp(confno_tmp, cnf->confno)) {
- /* The conference exists, therefore it's not empty */
- found = 1;
- break;
- }
- }
- AST_LIST_UNLOCK(&confs);
- if (!found) {
- /* At this point, we have a confno_tmp (static conference) that is empty */
- if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
- /* Case 1: empty_no_pin and pin is nonexistent (NULL)
- * Case 2: empty_no_pin and pin is blank (but not NULL)
- * Case 3: not empty_no_pin
- */
- ast_copy_string(confno, confno_tmp, sizeof(confno));
- break;
- /* XXX the map is not complete (but we do have a confno) */
- }
- }
- }
- }
- }
- var = var->next;
- }
- ast_config_destroy(cfg);
- }
- }
-
- /* Select first conference number not in use */
- if (ast_strlen_zero(confno) && dynamic) {
- AST_LIST_LOCK(&confs);
- for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
- if (!conf_map[i]) {
- snprintf(confno, sizeof(confno), "%d", i);
- conf_map[i] = 1;
- break;
- }
- }
- AST_LIST_UNLOCK(&confs);
- }
-
- /* Not found? */
- if (ast_strlen_zero(confno)) {
- res = ast_streamfile(chan, "conf-noempty", chan->language);
- if (!res)
- ast_waitstream(chan, "");
- } else {
- if (sscanf(confno, "%d", &confno_int) == 1) {
- if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
- res = ast_streamfile(chan, "conf-enteringno", chan->language);
- if (!res) {
- ast_waitstream(chan, "");
- res = ast_say_digits(chan, confno_int, "", chan->language);
- }
- }
- } else {
- ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
- }
- }
- }
-
- while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
- /* Prompt user for conference number */
- res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
- if (res < 0) {
- /* Don't try to validate when we catch an error */
- confno[0] = '\0';
- allowretry = 0;
- break;
- }
- }
- if (!ast_strlen_zero(confno)) {
- /* Check the validity of the conference */
- cnf = find_conf(chan, confno, 1, dynamic, the_pin,
- sizeof(the_pin), 1, &confflags);
- if (!cnf) {
- int too_early = 0;
- cnf = find_conf_realtime(chan, confno, 1, dynamic,
- the_pin, sizeof(the_pin), 1, &confflags, optargs, &too_early);
- if (rt_schedule && too_early)
- allowretry = 0;
- }
-
- if (!cnf) {
- if (allowretry) {
- confno[0] = '\0';
- res = ast_streamfile(chan, "conf-invalid", chan->language);
- if (!res)
- ast_waitstream(chan, "");
- res = -1;
- }
- } else {
- if ((!ast_strlen_zero(cnf->pin) &&
- !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
- (!ast_strlen_zero(cnf->pinadmin) &&
- ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
- char pin[MAX_PIN] = "";
- int j;
-
- /* Allow the pin to be retried up to 3 times */
- for (j = 0; j < 3; j++) {
- if (*the_pin && (always_prompt == 0)) {
- ast_copy_string(pin, the_pin, sizeof(pin));
- res = 0;
- } else {
- /* Prompt user for pin if pin is required */
- res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
- }
- if (res >= 0) {
- if (!strcasecmp(pin, cnf->pin) ||
- (!ast_strlen_zero(cnf->pinadmin) &&
- !strcasecmp(pin, cnf->pinadmin))) {
- /* Pin correct */
- allowretry = 0;
- if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
- ast_set_flag(&confflags, CONFFLAG_ADMIN);
- /* Run the conference */
- res = conf_run(chan, cnf, confflags.flags, optargs);
- break;
- } else {
- /* Pin invalid */
- if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
- res = ast_waitstream(chan, AST_DIGIT_ANY);
- ast_stopstream(chan);
- }
- else {
- ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
- break;
- }
- if (res < 0)
- break;
- pin[0] = res;
- pin[1] = '\0';
- res = -1;
- if (allowretry)
- confno[0] = '\0';
- }
- } else {
- /* failed when getting the pin */
- res = -1;
- allowretry = 0;
- /* see if we need to get rid of the conference */
- break;
- }
-
- /* Don't retry pin with a static pin */
- if (*the_pin && (always_prompt == 0)) {
- break;
- }
- }
- } else {
- /* No pin required */
- allowretry = 0;
-
- /* Run the conference */
- res = conf_run(chan, cnf, confflags.flags, optargs);
- }
- dispose_conf(cnf);
- cnf = NULL;
- }
- }
- } while (allowretry);
-
- if (cnf)
- dispose_conf(cnf);
-
- return res;
-}
-
-static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
-{
- struct ast_conf_user *user = NULL;
- int cid;
-
- sscanf(callerident, "%i", &cid);
- if (conf && callerident) {
- AST_LIST_TRAVERSE(&conf->userlist, user, list) {
- if (cid == user->user_no)
- return user;
- }
- }
- return NULL;
-}
-
-/*! \brief The MeetMeadmin application */
-/* MeetMeAdmin(confno, command, caller) */
-static int admin_exec(struct ast_channel *chan, void *data) {
- char *params;
- struct ast_conference *cnf;
- struct ast_conf_user *user = NULL;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(confno);
- AST_APP_ARG(command);
- AST_APP_ARG(user);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
- return -1;
- }
-
- params = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, params);
-
- if (!args.command) {
- ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
- return -1;
- }
-
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- if (!strcmp(cnf->confno, args.confno))
- break;
- }
-
- if (!cnf) {
- ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
- AST_LIST_UNLOCK(&confs);
- return 0;
- }
-
- ast_atomic_fetchadd_int(&cnf->refcount, 1);
-
- if (args.user)
- user = find_user(cnf, args.user);
-
- switch (*args.command) {
- case 76: /* L: Lock */
- cnf->locked = 1;
- break;
- case 108: /* l: Unlock */
- cnf->locked = 0;
- break;
- case 75: /* K: kick all users */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list)
- user->adminflags |= ADMINFLAG_KICKME;
- break;
- case 101: /* e: Eject last user*/
- user = AST_LIST_LAST(&cnf->userlist);
- if (!(user->userflags & CONFFLAG_ADMIN))
- user->adminflags |= ADMINFLAG_KICKME;
- else
- ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
- break;
- case 77: /* M: Mute */
- if (user) {
- user->adminflags |= ADMINFLAG_MUTED;
- } else
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- break;
- case 78: /* N: Mute all (non-admin) users */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
- if (!(user->userflags & CONFFLAG_ADMIN))
- user->adminflags |= ADMINFLAG_MUTED;
- }
- break;
- case 109: /* m: Unmute */
- if (user) {
- user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
- } else
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- break;
- case 110: /* n: Unmute all users */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list)
- user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
- break;
- case 107: /* k: Kick user */
- if (user)
- user->adminflags |= ADMINFLAG_KICKME;
- else
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- break;
- case 118: /* v: Lower all users listen volume */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list)
- tweak_listen_volume(user, VOL_DOWN);
- break;
- case 86: /* V: Raise all users listen volume */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list)
- tweak_listen_volume(user, VOL_UP);
- break;
- case 115: /* s: Lower all users speaking volume */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list)
- tweak_talk_volume(user, VOL_DOWN);
- break;
- case 83: /* S: Raise all users speaking volume */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list)
- tweak_talk_volume(user, VOL_UP);
- break;
- case 82: /* R: Reset all volume levels */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list)
- reset_volumes(user);
- break;
- case 114: /* r: Reset user's volume level */
- if (user)
- reset_volumes(user);
- else
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- break;
- case 85: /* U: Raise user's listen volume */
- if (user)
- tweak_listen_volume(user, VOL_UP);
- else
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- break;
- case 117: /* u: Lower user's listen volume */
- if (user)
- tweak_listen_volume(user, VOL_DOWN);
- else
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- break;
- case 84: /* T: Raise user's talk volume */
- if (user)
- tweak_talk_volume(user, VOL_UP);
- else
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- break;
- case 116: /* t: Lower user's talk volume */
- if (user)
- tweak_talk_volume(user, VOL_DOWN);
- else
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- break;
- }
-
- AST_LIST_UNLOCK(&confs);
-
- dispose_conf(cnf);
-
- return 0;
-}
-
-/*--- channel_admin_exec: The MeetMeChannelAdmin application */
-/* MeetMeChannelAdmin(channel, command) */
-static int channel_admin_exec(struct ast_channel *chan, void *data) {
- char *params;
- struct ast_conference *conf = NULL;
- struct ast_conf_user *user = NULL;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(channel);
- AST_APP_ARG(command);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
- return -1;
- }
-
- params = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, params);
-
- if (!args.channel) {
- ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
- return -1;
- }
-
- if (!args.command) {
- ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
- return -1;
- }
-
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, conf, list) {
- AST_LIST_TRAVERSE(&conf->userlist, user, list) {
- if (!strcmp(user->chan->name, args.channel))
- break;
- }
- }
-
- if (!user) {
- ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
- AST_LIST_UNLOCK(&confs);
- return 0;
- }
-
- /* perform the specified action */
- switch (*args.command) {
- case 77: /* M: Mute */
- user->adminflags |= ADMINFLAG_MUTED;
- break;
- case 109: /* m: Unmute */
- user->adminflags &= ~ADMINFLAG_MUTED;
- break;
- case 107: /* k: Kick user */
- user->adminflags |= ADMINFLAG_KICKME;
- break;
- default: /* unknown command */
- ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
- break;
- }
-
- AST_LIST_UNLOCK(&confs);
-
- return 0;
-}
-
-static int meetmemute(struct mansession *s, const struct message *m, int mute)
-{
- struct ast_conference *conf;
- struct ast_conf_user *user;
- const char *confid = astman_get_header(m, "Meetme");
- char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
- int userno;
-
- if (ast_strlen_zero(confid)) {
- astman_send_error(s, m, "Meetme conference not specified");
- return 0;
- }
-
- if (ast_strlen_zero(userid)) {
- astman_send_error(s, m, "Meetme user number not specified");
- return 0;
- }
-
- userno = strtoul(userid, &userid, 10);
-
- if (*userid) {
- astman_send_error(s, m, "Invalid user number");
- return 0;
- }
-
- /* Look in the conference list */
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, conf, list) {
- if (!strcmp(confid, conf->confno))
- break;
- }
-
- if (!conf) {
- AST_LIST_UNLOCK(&confs);
- astman_send_error(s, m, "Meetme conference does not exist");
- return 0;
- }
-
- AST_LIST_TRAVERSE(&conf->userlist, user, list)
- if (user->user_no == userno)
- break;
-
- if (!user) {
- AST_LIST_UNLOCK(&confs);
- astman_send_error(s, m, "User number not found");
- return 0;
- }
-
- if (mute)
- user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
- else
- user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST); /* request user unmuting */
-
- AST_LIST_UNLOCK(&confs);
-
- ast_log(LOG_NOTICE, "Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ? "" : "un", conf->confno, user->user_no, user->chan->name, user->chan->uniqueid);
-
- astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
- return 0;
-}
-
-static int action_meetmemute(struct mansession *s, const struct message *m)
-{
- return meetmemute(s, m, 1);
-}
-
-static int action_meetmeunmute(struct mansession *s, const struct message *m)
-{
- return meetmemute(s, m, 0);
-}
-
-static char mandescr_meetmelist[] =
-"Description: Lists all users in a particular MeetMe conference.\n"
-"MeetmeList will follow as separate events, followed by a final event called\n"
-"MeetmeListComplete.\n"
-"Variables:\n"
-" *ActionId: <id>\n"
-" *Conference: <confno>\n";
-
-static int action_meetmelist(struct mansession *s, const struct message *m)
-{
- const char *actionid = astman_get_header(m, "ActionID");
- const char *conference = astman_get_header(m, "Conference");
- char idText[80] = "";
- struct ast_conference *cnf;
- struct ast_conf_user *user;
- int total = 0;
-
- if (!ast_strlen_zero(actionid))
- snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
-
- if (AST_LIST_EMPTY(&confs)) {
- astman_send_error(s, m, "No active conferences.");
- return 0;
- }
-
- astman_send_listack(s, m, "Meetme user list will follow", "start");
-
- /* Find the right conference */
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- /* If we ask for one particular, and this isn't it, skip it */
- if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
- continue;
-
- /* Show all the users */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
- total++;
- astman_append(s,
- "Event: MeetmeList\r\n"
- "%s"
- "Conference: %s\r\n"
- "UserNumber: %d\r\n"
- "CallerIDNum: %s\r\n"
- "CallerIDName: %s\r\n"
- "Channel: %s\r\n"
- "Admin: %s\r\n"
- "Role: %s\r\n"
- "MarkedUser: %s\r\n"
- "Muted: %s\r\n"
- "Talking: %s\r\n"
- "\r\n",
- idText,
- cnf->confno,
- user->user_no,
- S_OR(user->chan->cid.cid_num, "<unknown>"),
- S_OR(user->chan->cid.cid_name, "<no name>"),
- user->chan->name,
- user->userflags & CONFFLAG_ADMIN ? "Yes" : "No",
- user->userflags & CONFFLAG_MONITOR ? "Listen only" : user->userflags & CONFFLAG_TALKER ? "Talk only" : "Talk and listen",
- user->userflags & CONFFLAG_MARKEDUSER ? "Yes" : "No",
- user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
- user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
- }
- }
- AST_LIST_UNLOCK(&confs);
- /* Send final confirmation */
- astman_append(s,
- "Event: MeetmeListComplete\r\n"
- "EventList: Complete\r\n"
- "ListItems: %d\r\n"
- "%s"
- "\r\n", total, idText);
- return 0;
-}
-
-static void *recordthread(void *args)
-{
- struct ast_conference *cnf = args;
- struct ast_frame *f = NULL;
- int flags;
- struct ast_filestream *s = NULL;
- int res = 0;
- int x;
- const char *oldrecordingfilename = NULL;
-
- if (!cnf || !cnf->lchan) {
- pthread_exit(0);
- }
-
- ast_stopstream(cnf->lchan);
- flags = O_CREAT | O_TRUNC | O_WRONLY;
-
-
- cnf->recording = MEETME_RECORD_ACTIVE;
- while (ast_waitfor(cnf->lchan, -1) > -1) {
- if (cnf->recording == MEETME_RECORD_TERMINATE) {
- AST_LIST_LOCK(&confs);
- AST_LIST_UNLOCK(&confs);
- break;
- }
- if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
- s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
- oldrecordingfilename = cnf->recordingfilename;
- }
-
- f = ast_read(cnf->lchan);
- if (!f) {
- res = -1;
- break;
- }
- if (f->frametype == AST_FRAME_VOICE) {
- ast_mutex_lock(&cnf->listenlock);
- for (x = 0; x < AST_FRAME_BITS; x++) {
- /* Free any translations that have occured */
- if (cnf->transframe[x]) {
- ast_frfree(cnf->transframe[x]);
- cnf->transframe[x] = NULL;
- }
- }
- if (cnf->origframe)
- ast_frfree(cnf->origframe);
- cnf->origframe = ast_frdup(f);
- ast_mutex_unlock(&cnf->listenlock);
- if (s)
- res = ast_writestream(s, f);
- if (res) {
- ast_frfree(f);
- break;
- }
- }
- ast_frfree(f);
- }
- cnf->recording = MEETME_RECORD_OFF;
- if (s)
- ast_closestream(s);
-
- pthread_exit(0);
-}
-
-/*! \brief Callback for devicestate providers */
-static enum ast_device_state meetmestate(const char *data)
-{
- struct ast_conference *conf;
-
- /* Find conference */
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, conf, list) {
- if (!strcmp(data, conf->confno))
- break;
- }
- AST_LIST_UNLOCK(&confs);
- if (!conf)
- return AST_DEVICE_INVALID;
-
-
- /* SKREP to fill */
- if (!conf->users)
- return AST_DEVICE_NOT_INUSE;
-
- return AST_DEVICE_INUSE;
-}
-
-static void load_config_meetme(void)
-{
- struct ast_config *cfg;
- struct ast_flags config_flags = { 0 };
- const char *val;
-
- if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags)))
- return;
-
- audio_buffers = DEFAULT_AUDIO_BUFFERS;
-
- /* Scheduling support is off by default */
- rt_schedule = 0;
- fuzzystart = 0;
- earlyalert = 0;
- endalert = 0;
-
- /* Logging of participants defaults to ON for compatibility reasons */
- rt_log_members = 1;
-
- if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
- if ((sscanf(val, "%d", &audio_buffers) != 1)) {
- ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
- audio_buffers = DEFAULT_AUDIO_BUFFERS;
- } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
- ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
- ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
- audio_buffers = DEFAULT_AUDIO_BUFFERS;
- }
- if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
- ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
- }
-
- if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
- rt_schedule = ast_true(val);
- if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
- rt_log_members = ast_true(val);
- if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
- if ((sscanf(val, "%d", &fuzzystart) != 1)) {
- ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
- fuzzystart = 0;
- }
- }
- if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
- if ((sscanf(val, "%d", &earlyalert) != 1)) {
- ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
- earlyalert = 0;
- }
- }
- if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
- if ((sscanf(val, "%d", &endalert) != 1)) {
- ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
- endalert = 0;
- }
- }
-
- ast_config_destroy(cfg);
-}
-
-/*! \brief Find an SLA trunk by name
- * \note This must be called with the sla_trunks container locked
- */
-static struct sla_trunk *sla_find_trunk(const char *name)
-{
- struct sla_trunk *trunk = NULL;
-
- AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
- if (!strcasecmp(trunk->name, name))
- break;
- }
-
- return trunk;
-}
-
-/*! \brief Find an SLA station by name
- * \note This must be called with the sla_stations container locked
- */
-static struct sla_station *sla_find_station(const char *name)
-{
- struct sla_station *station = NULL;
-
- AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
- if (!strcasecmp(station->name, name))
- break;
- }
-
- return station;
-}
-
-static int sla_check_station_hold_access(const struct sla_trunk *trunk,
- const struct sla_station *station)
-{
- struct sla_station_ref *station_ref;
- struct sla_trunk_ref *trunk_ref;
-
- /* For each station that has this call on hold, check for private hold. */
- AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
- AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
- if (trunk_ref->trunk != trunk || station_ref->station == station)
- continue;
- if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
- station_ref->station->hold_access == SLA_HOLD_PRIVATE)
- return 1;
- return 0;
- }
- }
-
- return 0;
-}
-
-/*! \brief Find a trunk reference on a station by name
- * \param station the station
- * \param name the trunk's name
- * \return a pointer to the station's trunk reference. If the trunk
- * is not found, it is not idle and barge is disabled, or if
- * it is on hold and private hold is set, then NULL will be returned.
- */
-static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
- const char *name)
-{
- struct sla_trunk_ref *trunk_ref = NULL;
-
- AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (strcasecmp(trunk_ref->trunk->name, name))
- continue;
-
- if ( (trunk_ref->trunk->barge_disabled
- && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
- (trunk_ref->trunk->hold_stations
- && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
- && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
- sla_check_station_hold_access(trunk_ref->trunk, station) )
- {
- trunk_ref = NULL;
- }
-
- break;
- }
-
- return trunk_ref;
-}
-
-static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
-{
- struct sla_station_ref *station_ref;
-
- if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
- return NULL;
-
- station_ref->station = station;
-
- return station_ref;
-}
-
-static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
-{
- struct sla_ringing_station *ringing_station;
-
- if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
- return NULL;
-
- ringing_station->station = station;
- ringing_station->ring_begin = ast_tvnow();
-
- return ringing_station;
-}
-
-static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
-{
- switch (state) {
- case SLA_TRUNK_STATE_IDLE:
- return AST_DEVICE_NOT_INUSE;
- case SLA_TRUNK_STATE_RINGING:
- return AST_DEVICE_RINGING;
- case SLA_TRUNK_STATE_UP:
- return AST_DEVICE_INUSE;
- case SLA_TRUNK_STATE_ONHOLD:
- case SLA_TRUNK_STATE_ONHOLD_BYME:
- return AST_DEVICE_ONHOLD;
- }
-
- return AST_DEVICE_UNKNOWN;
-}
-
-static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
- enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
-{
- struct sla_station *station;
- struct sla_trunk_ref *trunk_ref;
-
- AST_LIST_TRAVERSE(&sla_stations, station, entry) {
- AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
- || trunk_ref == exclude)
- continue;
- trunk_ref->state = state;
- ast_devstate_changed(sla_state_to_devstate(state),
- "SLA:%s_%s", station->name, trunk->name);
- break;
- }
- }
-}
-
-struct run_station_args {
- struct sla_station *station;
- struct sla_trunk_ref *trunk_ref;
- ast_mutex_t *cond_lock;
- ast_cond_t *cond;
-};
-
-static void *run_station(void *data)
-{
- struct sla_station *station;
- struct sla_trunk_ref *trunk_ref;
- char conf_name[MAX_CONFNUM];
- struct ast_flags conf_flags = { 0 };
- struct ast_conference *conf;
-
- {
- struct run_station_args *args = data;
- station = args->station;
- trunk_ref = args->trunk_ref;
- ast_mutex_lock(args->cond_lock);
- ast_cond_signal(args->cond);
- ast_mutex_unlock(args->cond_lock);
- /* args is no longer valid here. */
- }
-
- ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
- snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
- ast_set_flag(&conf_flags,
- CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
- ast_answer(trunk_ref->chan);
- conf = build_conf(conf_name, "", "", 0, 0, 1, trunk_ref->chan);
- if (conf) {
- conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
- dispose_conf(conf);
- conf = NULL;
- }
- trunk_ref->chan = NULL;
- if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
- trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
- strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
- admin_exec(NULL, conf_name);
- trunk_ref->trunk->hold_stations = 0;
- sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
- }
-
- ast_dial_join(station->dial);
- ast_dial_destroy(station->dial);
- station->dial = NULL;
-
- return NULL;
-}
-
-static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
-{
- char buf[80];
- struct sla_station_ref *station_ref;
-
- snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
- admin_exec(NULL, buf);
- sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
-
- while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
- ast_free(station_ref);
-
- ast_free(ringing_trunk);
-}
-
-static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
- enum sla_station_hangup hangup)
-{
- struct sla_ringing_trunk *ringing_trunk;
- struct sla_trunk_ref *trunk_ref;
- struct sla_station_ref *station_ref;
-
- ast_dial_join(ringing_station->station->dial);
- ast_dial_destroy(ringing_station->station->dial);
- ringing_station->station->dial = NULL;
-
- if (hangup == SLA_STATION_HANGUP_NORMAL)
- goto done;
-
- /* If the station is being hung up because of a timeout, then add it to the
- * list of timed out stations on each of the ringing trunks. This is so
- * that when doing further processing to figure out which stations should be
- * ringing, which trunk to answer, determining timeouts, etc., we know which
- * ringing trunks we should ignore. */
- AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
- AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
- if (ringing_trunk->trunk == trunk_ref->trunk)
- break;
- }
- if (!trunk_ref)
- continue;
- if (!(station_ref = sla_create_station_ref(ringing_station->station)))
- continue;
- AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
- }
-
-done:
- ast_free(ringing_station);
-}
-
-static void sla_dial_state_callback(struct ast_dial *dial)
-{
- sla_queue_event(SLA_EVENT_DIAL_STATE);
-}
-
-/*! \brief Check to see if dialing this station already timed out for this ringing trunk
- * \note Assumes sla.lock is locked
- */
-static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
- const struct sla_station *station)
-{
- struct sla_station_ref *timed_out_station;
-
- AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
- if (station == timed_out_station->station)
- return 1;
- }
-
- return 0;
-}
-
-/*! \brief Choose the highest priority ringing trunk for a station
- * \param station the station
- * \param remove remove the ringing trunk once selected
- * \param trunk_ref a place to store the pointer to this stations reference to
- * the selected trunk
- * \return a pointer to the selected ringing trunk, or NULL if none found
- * \note Assumes that sla.lock is locked
- */
-static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
- struct sla_trunk_ref **trunk_ref, int remove)
-{
- struct sla_trunk_ref *s_trunk_ref;
- struct sla_ringing_trunk *ringing_trunk = NULL;
-
- AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
- AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
- /* Make sure this is the trunk we're looking for */
- if (s_trunk_ref->trunk != ringing_trunk->trunk)
- continue;
-
- /* This trunk on the station is ringing. But, make sure this station
- * didn't already time out while this trunk was ringing. */
- if (sla_check_timed_out_station(ringing_trunk, station))
- continue;
-
- if (remove)
- AST_LIST_REMOVE_CURRENT(entry);
-
- if (trunk_ref)
- *trunk_ref = s_trunk_ref;
-
- break;
- }
- AST_LIST_TRAVERSE_SAFE_END;
-
- if (ringing_trunk)
- break;
- }
-
- return ringing_trunk;
-}
-
-static void sla_handle_dial_state_event(void)
-{
- struct sla_ringing_station *ringing_station;
-
- AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
- struct sla_trunk_ref *s_trunk_ref = NULL;
- struct sla_ringing_trunk *ringing_trunk = NULL;
- struct run_station_args args;
- enum ast_dial_result dial_res;
- pthread_t dont_care;
- ast_mutex_t cond_lock;
- ast_cond_t cond;
-
- switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
- case AST_DIAL_RESULT_HANGUP:
- case AST_DIAL_RESULT_INVALID:
- case AST_DIAL_RESULT_FAILED:
- case AST_DIAL_RESULT_TIMEOUT:
- case AST_DIAL_RESULT_UNANSWERED:
- AST_LIST_REMOVE_CURRENT(entry);
- sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
- break;
- case AST_DIAL_RESULT_ANSWERED:
- AST_LIST_REMOVE_CURRENT(entry);
- /* Find the appropriate trunk to answer. */
- ast_mutex_lock(&sla.lock);
- ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
- ast_mutex_unlock(&sla.lock);
- if (!ringing_trunk) {
- ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
- break;
- }
- /* Track the channel that answered this trunk */
- s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
- /* Actually answer the trunk */
- ast_answer(ringing_trunk->trunk->chan);
- sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
- /* Now, start a thread that will connect this station to the trunk. The rest of
- * the code here sets up the thread and ensures that it is able to save the arguments
- * before they are no longer valid since they are allocated on the stack. */
- args.trunk_ref = s_trunk_ref;
- args.station = ringing_station->station;
- args.cond = &cond;
- args.cond_lock = &cond_lock;
- ast_free(ringing_trunk);
- ast_free(ringing_station);
- ast_mutex_init(&cond_lock);
- ast_cond_init(&cond, NULL);
- ast_mutex_lock(&cond_lock);
- ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
- ast_cond_wait(&cond, &cond_lock);
- ast_mutex_unlock(&cond_lock);
- ast_mutex_destroy(&cond_lock);
- ast_cond_destroy(&cond);
- break;
- case AST_DIAL_RESULT_TRYING:
- case AST_DIAL_RESULT_RINGING:
- case AST_DIAL_RESULT_PROGRESS:
- case AST_DIAL_RESULT_PROCEEDING:
- break;
- }
- if (dial_res == AST_DIAL_RESULT_ANSWERED) {
- /* Queue up reprocessing ringing trunks, and then ringing stations again */
- sla_queue_event(SLA_EVENT_RINGING_TRUNK);
- sla_queue_event(SLA_EVENT_DIAL_STATE);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
-}
-
-/*! \brief Check to see if this station is already ringing
- * \note Assumes sla.lock is locked
- */
-static int sla_check_ringing_station(const struct sla_station *station)
-{
- struct sla_ringing_station *ringing_station;
-
- AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
- if (station == ringing_station->station)
- return 1;
- }
-
- return 0;
-}
-
-/*! \brief Check to see if this station has failed to be dialed in the past minute
- * \note assumes sla.lock is locked
- */
-static int sla_check_failed_station(const struct sla_station *station)
-{
- struct sla_failed_station *failed_station;
- int res = 0;
-
- AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
- if (station != failed_station->station)
- continue;
- if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
- AST_LIST_REMOVE_CURRENT(entry);
- ast_free(failed_station);
- break;
- }
- res = 1;
- }
- AST_LIST_TRAVERSE_SAFE_END
-
- return res;
-}
-
-/*! \brief Ring a station
- * \note Assumes sla.lock is locked
- */
-static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
-{
- char *tech, *tech_data;
- struct ast_dial *dial;
- struct sla_ringing_station *ringing_station;
- const char *cid_name = NULL, *cid_num = NULL;
- enum ast_dial_result res;
-
- if (!(dial = ast_dial_create()))
- return -1;
-
- ast_dial_set_state_callback(dial, sla_dial_state_callback);
- tech_data = ast_strdupa(station->device);
- tech = strsep(&tech_data, "/");
-
- if (ast_dial_append(dial, tech, tech_data) == -1) {
- ast_dial_destroy(dial);
- return -1;
- }
-
- if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_name)) {
- cid_name = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_name);
- ast_free(ringing_trunk->trunk->chan->cid.cid_name);
- ringing_trunk->trunk->chan->cid.cid_name = NULL;
- }
- if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_num)) {
- cid_num = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_num);
- ast_free(ringing_trunk->trunk->chan->cid.cid_num);
- ringing_trunk->trunk->chan->cid.cid_num = NULL;
- }
-
- res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
-
- if (cid_name)
- ringing_trunk->trunk->chan->cid.cid_name = ast_strdup(cid_name);
- if (cid_num)
- ringing_trunk->trunk->chan->cid.cid_num = ast_strdup(cid_num);
-
- if (res != AST_DIAL_RESULT_TRYING) {
- struct sla_failed_station *failed_station;
- ast_dial_destroy(dial);
- if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
- return -1;
- failed_station->station = station;
- failed_station->last_try = ast_tvnow();
- AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
- return -1;
- }
- if (!(ringing_station = sla_create_ringing_station(station))) {
- ast_dial_join(dial);
- ast_dial_destroy(dial);
- return -1;
- }
-
- station->dial = dial;
-
- AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
-
- return 0;
-}
-
-/*! \brief Check to see if a station is in use
- */
-static int sla_check_inuse_station(const struct sla_station *station)
-{
- struct sla_trunk_ref *trunk_ref;
-
- AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (trunk_ref->chan)
- return 1;
- }
-
- return 0;
-}
-
-static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
- const struct sla_trunk *trunk)
-{
- struct sla_trunk_ref *trunk_ref = NULL;
-
- AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (trunk_ref->trunk == trunk)
- break;
- }
-
- return trunk_ref;
-}
-
-/*! \brief Calculate the ring delay for a given ringing trunk on a station
- * \param station the station
- * \param ringing_trunk the trunk. If NULL, the highest priority ringing trunk will be used
- * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
- */
-static int sla_check_station_delay(struct sla_station *station,
- struct sla_ringing_trunk *ringing_trunk)
-{
- struct sla_trunk_ref *trunk_ref;
- unsigned int delay = UINT_MAX;
- int time_left, time_elapsed;
-
- if (!ringing_trunk)
- ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
- else
- trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
-
- if (!ringing_trunk || !trunk_ref)
- return delay;
-
- /* If this station has a ring delay specific to the highest priority
- * ringing trunk, use that. Otherwise, use the ring delay specified
- * globally for the station. */
- delay = trunk_ref->ring_delay;
- if (!delay)
- delay = station->ring_delay;
- if (!delay)
- return INT_MAX;
-
- time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
- time_left = (delay * 1000) - time_elapsed;
-
- return time_left;
-}
-
-/*! \brief Ring stations based on current set of ringing trunks
- * \note Assumes that sla.lock is locked
- */
-static void sla_ring_stations(void)
-{
- struct sla_station_ref *station_ref;
- struct sla_ringing_trunk *ringing_trunk;
-
- /* Make sure that every station that uses at least one of the ringing
- * trunks, is ringing. */
- AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
- AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
- int time_left;
-
- /* Is this station already ringing? */
- if (sla_check_ringing_station(station_ref->station))
- continue;
-
- /* Is this station already in a call? */
- if (sla_check_inuse_station(station_ref->station))
- continue;
-
- /* Did we fail to dial this station earlier? If so, has it been
- * a minute since we tried? */
- if (sla_check_failed_station(station_ref->station))
- continue;
-
- /* If this station already timed out while this trunk was ringing,
- * do not dial it again for this ringing trunk. */
- if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
- continue;
-
- /* Check for a ring delay in progress */
- time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
- if (time_left != INT_MAX && time_left > 0)
- continue;
-
- /* It is time to make this station begin to ring. Do it! */
- sla_ring_station(ringing_trunk, station_ref->station);
- }
- }
- /* Now, all of the stations that should be ringing, are ringing. */
-}
-
-static void sla_hangup_stations(void)
-{
- struct sla_trunk_ref *trunk_ref;
- struct sla_ringing_station *ringing_station;
-
- AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
- AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
- struct sla_ringing_trunk *ringing_trunk;
- ast_mutex_lock(&sla.lock);
- AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
- if (trunk_ref->trunk == ringing_trunk->trunk)
- break;
- }
- ast_mutex_unlock(&sla.lock);
- if (ringing_trunk)
- break;
- }
- if (!trunk_ref) {
- AST_LIST_REMOVE_CURRENT(entry);
- ast_dial_join(ringing_station->station->dial);
- ast_dial_destroy(ringing_station->station->dial);
- ringing_station->station->dial = NULL;
- ast_free(ringing_station);
- }
- }
- AST_LIST_TRAVERSE_SAFE_END
-}
-
-static void sla_handle_ringing_trunk_event(void)
-{
- ast_mutex_lock(&sla.lock);
- sla_ring_stations();
- ast_mutex_unlock(&sla.lock);
-
- /* Find stations that shouldn't be ringing anymore. */
- sla_hangup_stations();
-}
-
-static void sla_handle_hold_event(struct sla_event *event)
-{
- ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
- event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
- ast_devstate_changed(AST_DEVICE_ONHOLD, "SLA:%s_%s",
- event->station->name, event->trunk_ref->trunk->name);
- sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
- INACTIVE_TRUNK_REFS, event->trunk_ref);
-
- if (event->trunk_ref->trunk->active_stations == 1) {
- /* The station putting it on hold is the only one on the call, so start
- * Music on hold to the trunk. */
- event->trunk_ref->trunk->on_hold = 1;
- ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
- }
-
- ast_softhangup(event->trunk_ref->chan, AST_CAUSE_NORMAL);
- event->trunk_ref->chan = NULL;
-}
-
-/*! \brief Process trunk ring timeouts
- * \note Called with sla.lock locked
- * \return non-zero if a change to the ringing trunks was made
- */
-static int sla_calc_trunk_timeouts(unsigned int *timeout)
-{
- struct sla_ringing_trunk *ringing_trunk;
- int res = 0;
-
- AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
- int time_left, time_elapsed;
- if (!ringing_trunk->trunk->ring_timeout)
- continue;
- time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
- time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
- if (time_left <= 0) {
- pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
- AST_LIST_REMOVE_CURRENT(entry);
- sla_stop_ringing_trunk(ringing_trunk);
- res = 1;
- continue;
- }
- if (time_left < *timeout)
- *timeout = time_left;
- }
- AST_LIST_TRAVERSE_SAFE_END;
-
- return res;
-}
-
-/*! \brief Process station ring timeouts
- * \note Called with sla.lock locked
- * \return non-zero if a change to the ringing stations was made
- */
-static int sla_calc_station_timeouts(unsigned int *timeout)
-{
- struct sla_ringing_trunk *ringing_trunk;
- struct sla_ringing_station *ringing_station;
- int res = 0;
-
- AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
- unsigned int ring_timeout = 0;
- int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
- struct sla_trunk_ref *trunk_ref;
-
- /* If there are any ring timeouts specified for a specific trunk
- * on the station, then use the highest per-trunk ring timeout.
- * Otherwise, use the ring timeout set for the entire station. */
- AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
- struct sla_station_ref *station_ref;
- int trunk_time_elapsed, trunk_time_left;
-
- AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
- if (ringing_trunk->trunk == trunk_ref->trunk)
- break;
- }
- if (!ringing_trunk)
- continue;
-
- /* If there is a trunk that is ringing without a timeout, then the
- * only timeout that could matter is a global station ring timeout. */
- if (!trunk_ref->ring_timeout)
- break;
-
- /* This trunk on this station is ringing and has a timeout.
- * However, make sure this trunk isn't still ringing from a
- * previous timeout. If so, don't consider it. */
- AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
- if (station_ref->station == ringing_station->station)
- break;
- }
- if (station_ref)
- continue;
-
- trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
- trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
- if (trunk_time_left > final_trunk_time_left)
- final_trunk_time_left = trunk_time_left;
- }
-
- /* No timeout was found for ringing trunks, and no timeout for the entire station */
- if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
- continue;
-
- /* Compute how much time is left for a global station timeout */
- if (ringing_station->station->ring_timeout) {
- ring_timeout = ringing_station->station->ring_timeout;
- time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
- time_left = (ring_timeout * 1000) - time_elapsed;
- }
-
- /* If the time left based on the per-trunk timeouts is smaller than the
- * global station ring timeout, use that. */
- if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
- time_left = final_trunk_time_left;
-
- /* If there is no time left, the station needs to stop ringing */
- if (time_left <= 0) {
- AST_LIST_REMOVE_CURRENT(entry);
- sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
- res = 1;
- continue;
- }
-
- /* There is still some time left for this station to ring, so save that
- * timeout if it is the first event scheduled to occur */
- if (time_left < *timeout)
- *timeout = time_left;
- }
- AST_LIST_TRAVERSE_SAFE_END;
-
- return res;
-}
-
-/*! \brief Calculate the ring delay for a station
- * \note Assumes sla.lock is locked
- */
-static int sla_calc_station_delays(unsigned int *timeout)
-{
- struct sla_station *station;
- int res = 0;
-
- AST_LIST_TRAVERSE(&sla_stations, station, entry) {
- struct sla_ringing_trunk *ringing_trunk;
- int time_left;
-
- /* Ignore stations already ringing */
- if (sla_check_ringing_station(station))
- continue;
-
- /* Ignore stations already on a call */
- if (sla_check_inuse_station(station))
- continue;
-
- /* Ignore stations that don't have one of their trunks ringing */
- if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
- continue;
-
- if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
- continue;
-
- /* If there is no time left, then the station needs to start ringing.
- * Return non-zero so that an event will be queued up an event to
- * make that happen. */
- if (time_left <= 0) {
- res = 1;
- continue;
- }
-
- if (time_left < *timeout)
- *timeout = time_left;
- }
-
- return res;
-}
-
-/*! \brief Calculate the time until the next known event
- * \note Called with sla.lock locked */
-static int sla_process_timers(struct timespec *ts)
-{
- unsigned int timeout = UINT_MAX;
- struct timeval tv;
- unsigned int change_made = 0;
-
- /* Check for ring timeouts on ringing trunks */
- if (sla_calc_trunk_timeouts(&timeout))
- change_made = 1;
-
- /* Check for ring timeouts on ringing stations */
- if (sla_calc_station_timeouts(&timeout))
- change_made = 1;
-
- /* Check for station ring delays */
- if (sla_calc_station_delays(&timeout))
- change_made = 1;
-
- /* queue reprocessing of ringing trunks */
- if (change_made)
- sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
-
- /* No timeout */
- if (timeout == UINT_MAX)
- return 0;
-
- if (ts) {
- tv = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
- ts->tv_sec = tv.tv_sec;
- ts->tv_nsec = tv.tv_usec * 1000;
- }
-
- return 1;
-}
-
-static int sla_load_config(int reload);
-
-/*! \brief Check if we can do a reload of SLA, and do it if we can */
-static void sla_check_reload(void)
-{
- struct sla_station *station;
- struct sla_trunk *trunk;
-
- ast_mutex_lock(&sla.lock);
-
- if (!AST_LIST_EMPTY(&sla.event_q) || !AST_LIST_EMPTY(&sla.ringing_trunks)
- || !AST_LIST_EMPTY(&sla.ringing_stations)) {
- ast_mutex_unlock(&sla.lock);
- return;
- }
-
- AST_RWLIST_RDLOCK(&sla_stations);
- AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
- if (station->ref_count)
- break;
- }
- AST_RWLIST_UNLOCK(&sla_stations);
- if (station) {
- ast_mutex_unlock(&sla.lock);
- return;
- }
-
- AST_RWLIST_RDLOCK(&sla_trunks);
- AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
- if (trunk->ref_count)
- break;
- }
- AST_RWLIST_UNLOCK(&sla_trunks);
- if (trunk) {
- ast_mutex_unlock(&sla.lock);
- return;
- }
-
- /* yay */
- sla_load_config(1);
- sla.reload = 0;
-
- ast_mutex_unlock(&sla.lock);
-}
-
-static void *sla_thread(void *data)
-{
- struct sla_failed_station *failed_station;
- struct sla_ringing_station *ringing_station;
-
- ast_mutex_lock(&sla.lock);
-
- while (!sla.stop) {
- struct sla_event *event;
- struct timespec ts = { 0, };
- unsigned int have_timeout = 0;
-
- if (AST_LIST_EMPTY(&sla.event_q)) {
- if ((have_timeout = sla_process_timers(&ts)))
- ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
- else
- ast_cond_wait(&sla.cond, &sla.lock);
- if (sla.stop)
- break;
- }
-
- if (have_timeout)
- sla_process_timers(NULL);
-
- while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
- ast_mutex_unlock(&sla.lock);
- switch (event->type) {
- case SLA_EVENT_HOLD:
- sla_handle_hold_event(event);
- break;
- case SLA_EVENT_DIAL_STATE:
- sla_handle_dial_state_event();
- break;
- case SLA_EVENT_RINGING_TRUNK:
- sla_handle_ringing_trunk_event();
- break;
- case SLA_EVENT_RELOAD:
- sla.reload = 1;
- case SLA_EVENT_CHECK_RELOAD:
- break;
- }
- ast_free(event);
- ast_mutex_lock(&sla.lock);
- }
-
- if (sla.reload)
- sla_check_reload();
- }
-
- ast_mutex_unlock(&sla.lock);
-
- while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
- ast_free(ringing_station);
-
- while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
- ast_free(failed_station);
-
- return NULL;
-}
-
-struct dial_trunk_args {
- struct sla_trunk_ref *trunk_ref;
- struct sla_station *station;
- ast_mutex_t *cond_lock;
- ast_cond_t *cond;
-};
-
-static void *dial_trunk(void *data)
-{
- struct dial_trunk_args *args = data;
- struct ast_dial *dial;
- char *tech, *tech_data;
- enum ast_dial_result dial_res;
- char conf_name[MAX_CONFNUM];
- struct ast_conference *conf;
- struct ast_flags conf_flags = { 0 };
- struct sla_trunk_ref *trunk_ref = args->trunk_ref;
- const char *cid_name = NULL, *cid_num = NULL;
-
- if (!(dial = ast_dial_create())) {
- ast_mutex_lock(args->cond_lock);
- ast_cond_signal(args->cond);
- ast_mutex_unlock(args->cond_lock);
- return NULL;
- }
-
- tech_data = ast_strdupa(trunk_ref->trunk->device);
- tech = strsep(&tech_data, "/");
- if (ast_dial_append(dial, tech, tech_data) == -1) {
- ast_mutex_lock(args->cond_lock);
- ast_cond_signal(args->cond);
- ast_mutex_unlock(args->cond_lock);
- ast_dial_destroy(dial);
- return NULL;
- }
-
- if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_name)) {
- cid_name = ast_strdupa(trunk_ref->chan->cid.cid_name);
- ast_free(trunk_ref->chan->cid.cid_name);
- trunk_ref->chan->cid.cid_name = NULL;
- }
- if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_num)) {
- cid_num = ast_strdupa(trunk_ref->chan->cid.cid_num);
- ast_free(trunk_ref->chan->cid.cid_num);
- trunk_ref->chan->cid.cid_num = NULL;
- }
-
- dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
-
- if (cid_name)
- trunk_ref->chan->cid.cid_name = ast_strdup(cid_name);
- if (cid_num)
- trunk_ref->chan->cid.cid_num = ast_strdup(cid_num);
-
- if (dial_res != AST_DIAL_RESULT_TRYING) {
- ast_mutex_lock(args->cond_lock);
- ast_cond_signal(args->cond);
- ast_mutex_unlock(args->cond_lock);
- ast_dial_destroy(dial);
- return NULL;
- }
-
- for (;;) {
- unsigned int done = 0;
- switch ((dial_res = ast_dial_state(dial))) {
- case AST_DIAL_RESULT_ANSWERED:
- trunk_ref->trunk->chan = ast_dial_answered(dial);
- case AST_DIAL_RESULT_HANGUP:
- case AST_DIAL_RESULT_INVALID:
- case AST_DIAL_RESULT_FAILED:
- case AST_DIAL_RESULT_TIMEOUT:
- case AST_DIAL_RESULT_UNANSWERED:
- done = 1;
- case AST_DIAL_RESULT_TRYING:
- case AST_DIAL_RESULT_RINGING:
- case AST_DIAL_RESULT_PROGRESS:
- case AST_DIAL_RESULT_PROCEEDING:
- break;
- }
- if (done)
- break;
- }
-
- if (!trunk_ref->trunk->chan) {
- ast_mutex_lock(args->cond_lock);
- ast_cond_signal(args->cond);
- ast_mutex_unlock(args->cond_lock);
- ast_dial_join(dial);
- ast_dial_destroy(dial);
- return NULL;
- }
-
- snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
- ast_set_flag(&conf_flags,
- CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
- CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
- conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan);
-
- ast_mutex_lock(args->cond_lock);
- ast_cond_signal(args->cond);
- ast_mutex_unlock(args->cond_lock);
-
- if (conf) {
- conf_run(trunk_ref->trunk->chan, conf, conf_flags.flags, NULL);
- dispose_conf(conf);
- conf = NULL;
- }
-
- /* If the trunk is going away, it is definitely now IDLE. */
- sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
-
- trunk_ref->trunk->chan = NULL;
- trunk_ref->trunk->on_hold = 0;
-
- ast_dial_join(dial);
- ast_dial_destroy(dial);
-
- return NULL;
-}
-
-/*! \brief For a given station, choose the highest priority idle trunk
- */
-static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
-{
- struct sla_trunk_ref *trunk_ref = NULL;
-
- AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
- break;
- }
-
- return trunk_ref;
-}
-
-static int sla_station_exec(struct ast_channel *chan, void *data)
-{
- char *station_name, *trunk_name;
- struct sla_station *station;
- struct sla_trunk_ref *trunk_ref = NULL;
- char conf_name[MAX_CONFNUM];
- struct ast_flags conf_flags = { 0 };
- struct ast_conference *conf;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
- pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
- return 0;
- }
-
- trunk_name = ast_strdupa(data);
- station_name = strsep(&trunk_name, "_");
-
- if (ast_strlen_zero(station_name)) {
- ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
- pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
- return 0;
- }
-
- AST_RWLIST_RDLOCK(&sla_stations);
- station = sla_find_station(station_name);
- if (station)
- ast_atomic_fetchadd_int((int *) &station->ref_count, 1);
- AST_RWLIST_UNLOCK(&sla_stations);
-
- if (!station) {
- ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
- pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
- return 0;
- }
-
- AST_RWLIST_RDLOCK(&sla_trunks);
- if (!ast_strlen_zero(trunk_name)) {
- trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
- } else
- trunk_ref = sla_choose_idle_trunk(station);
- AST_RWLIST_UNLOCK(&sla_trunks);
-
- if (!trunk_ref) {
- if (ast_strlen_zero(trunk_name))
- ast_log(LOG_NOTICE, "No trunks available for call.\n");
- else {
- ast_log(LOG_NOTICE, "Can't join existing call on trunk "
- "'%s' due to access controls.\n", trunk_name);
- }
- pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
- ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
- return 0;
- }
-
- if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
- if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
- sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
- else {
- trunk_ref->state = SLA_TRUNK_STATE_UP;
- ast_devstate_changed(AST_DEVICE_INUSE,
- "SLA:%s_%s", station->name, trunk_ref->trunk->name);
- }
- } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
- struct sla_ringing_trunk *ringing_trunk;
-
- ast_mutex_lock(&sla.lock);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
- if (ringing_trunk->trunk == trunk_ref->trunk) {
- AST_LIST_REMOVE_CURRENT(entry);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END
- ast_mutex_unlock(&sla.lock);
-
- if (ringing_trunk) {
- ast_answer(ringing_trunk->trunk->chan);
- sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
-
- free(ringing_trunk);
-
- /* Queue up reprocessing ringing trunks, and then ringing stations again */
- sla_queue_event(SLA_EVENT_RINGING_TRUNK);
- sla_queue_event(SLA_EVENT_DIAL_STATE);
- }
- }
-
- trunk_ref->chan = chan;
-
- if (!trunk_ref->trunk->chan) {
- ast_mutex_t cond_lock;
- ast_cond_t cond;
- pthread_t dont_care;
- struct dial_trunk_args args = {
- .trunk_ref = trunk_ref,
- .station = station,
- .cond_lock = &cond_lock,
- .cond = &cond,
- };
- sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
- /* Create a thread to dial the trunk and dump it into the conference.
- * However, we want to wait until the trunk has been dialed and the
- * conference is created before continuing on here. */
- ast_autoservice_start(chan);
- ast_mutex_init(&cond_lock);
- ast_cond_init(&cond, NULL);
- ast_mutex_lock(&cond_lock);
- ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
- ast_cond_wait(&cond, &cond_lock);
- ast_mutex_unlock(&cond_lock);
- ast_mutex_destroy(&cond_lock);
- ast_cond_destroy(&cond);
- ast_autoservice_stop(chan);
- if (!trunk_ref->trunk->chan) {
- ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
- pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
- sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
- trunk_ref->chan = NULL;
- ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
- return 0;
- }
- }
-
- if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
- trunk_ref->trunk->on_hold) {
- trunk_ref->trunk->on_hold = 0;
- ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
- sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
- }
-
- snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
- ast_set_flag(&conf_flags,
- CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
- ast_answer(chan);
- conf = build_conf(conf_name, "", "", 0, 0, 1, chan);
- if (conf) {
- conf_run(chan, conf, conf_flags.flags, NULL);
- dispose_conf(conf);
- conf = NULL;
- }
- trunk_ref->chan = NULL;
- if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
- trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
- strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
- admin_exec(NULL, conf_name);
- trunk_ref->trunk->hold_stations = 0;
- sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
- }
-
- pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
-
- ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
-
- return 0;
-}
-
-static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
-{
- struct sla_trunk_ref *trunk_ref;
-
- if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
- return NULL;
-
- trunk_ref->trunk = trunk;
-
- return trunk_ref;
-}
-
-static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
-{
- struct sla_ringing_trunk *ringing_trunk;
-
- if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
- return NULL;
-
- ringing_trunk->trunk = trunk;
- ringing_trunk->ring_begin = ast_tvnow();
-
- sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
-
- ast_mutex_lock(&sla.lock);
- AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
- ast_mutex_unlock(&sla.lock);
-
- sla_queue_event(SLA_EVENT_RINGING_TRUNK);
-
- return ringing_trunk;
-}
-
-enum {
- SLA_TRUNK_OPT_MOH = (1 << 0),
-};
-
-enum {
- SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
- SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
-};
-
-AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
- AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
-END_OPTIONS );
-
-static int sla_trunk_exec(struct ast_channel *chan, void *data)
-{
- char conf_name[MAX_CONFNUM];
- struct ast_conference *conf;
- struct ast_flags conf_flags = { 0 };
- struct sla_trunk *trunk;
- struct sla_ringing_trunk *ringing_trunk;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(trunk_name);
- AST_APP_ARG(options);
- );
- char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
- char *conf_opt_args[OPT_ARG_ARRAY_SIZE] = { NULL, };
- struct ast_flags opt_flags = { 0 };
- char *parse;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
- if (args.argc == 2) {
- if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
- ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
- return -1;
- }
- }
-
- AST_RWLIST_RDLOCK(&sla_trunks);
- trunk = sla_find_trunk(args.trunk_name);
- if (trunk)
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, 1);
- AST_RWLIST_UNLOCK(&sla_trunks);
-
- if (!trunk) {
- ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
- pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
- return 0;
- }
-
- if (trunk->chan) {
- ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
- args.trunk_name);
- pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
- return 0;
- }
-
- trunk->chan = chan;
-
- if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
- pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
- return 0;
- }
-
- snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
- conf = build_conf(conf_name, "", "", 1, 1, 1, chan);
- if (!conf) {
- pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
- return 0;
- }
- ast_set_flag(&conf_flags,
- CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF);
-
- if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
- ast_indicate(chan, -1);
- ast_set_flag(&conf_flags, CONFFLAG_MOH);
- conf_opt_args[OPT_ARG_MOH_CLASS] = opts[SLA_TRUNK_OPT_ARG_MOH_CLASS];
- } else
- ast_indicate(chan, AST_CONTROL_RINGING);
-
- conf_run(chan, conf, conf_flags.flags, opts);
- dispose_conf(conf);
- conf = NULL;
- trunk->chan = NULL;
- trunk->on_hold = 0;
-
- if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
- pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
-
- /* Remove the entry from the list of ringing trunks if it is still there. */
- ast_mutex_lock(&sla.lock);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
- if (ringing_trunk->trunk == trunk) {
- AST_LIST_REMOVE_CURRENT(entry);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- ast_mutex_unlock(&sla.lock);
- if (ringing_trunk) {
- sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
- ast_free(ringing_trunk);
- pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
- /* Queue reprocessing of ringing trunks to make stations stop ringing
- * that shouldn't be ringing after this trunk stopped. */
- sla_queue_event(SLA_EVENT_RINGING_TRUNK);
- }
-
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
-
- return 0;
-}
-
-static enum ast_device_state sla_state(const char *data)
-{
- char *buf, *station_name, *trunk_name;
- struct sla_station *station;
- struct sla_trunk_ref *trunk_ref;
- enum ast_device_state res = AST_DEVICE_INVALID;
-
- trunk_name = buf = ast_strdupa(data);
- station_name = strsep(&trunk_name, "_");
-
- AST_RWLIST_RDLOCK(&sla_stations);
- AST_LIST_TRAVERSE(&sla_stations, station, entry) {
- if (strcasecmp(station_name, station->name))
- continue;
- AST_RWLIST_RDLOCK(&sla_trunks);
- AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
- break;
- }
- if (!trunk_ref) {
- AST_RWLIST_UNLOCK(&sla_trunks);
- break;
- }
- res = sla_state_to_devstate(trunk_ref->state);
- AST_RWLIST_UNLOCK(&sla_trunks);
- }
- AST_RWLIST_UNLOCK(&sla_stations);
-
- if (res == AST_DEVICE_INVALID) {
- ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
- trunk_name, station_name);
- }
-
- return res;
-}
-
-static void destroy_trunk(struct sla_trunk *trunk)
-{
- struct sla_station_ref *station_ref;
-
- if (!ast_strlen_zero(trunk->autocontext))
- ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
-
- while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
- ast_free(station_ref);
-
- ast_string_field_free_memory(trunk);
- ast_free(trunk);
-}
-
-static void destroy_station(struct sla_station *station)
-{
- struct sla_trunk_ref *trunk_ref;
-
- if (!ast_strlen_zero(station->autocontext)) {
- AST_RWLIST_RDLOCK(&sla_trunks);
- AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- char exten[AST_MAX_EXTENSION];
- char hint[AST_MAX_APP];
- snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
- snprintf(hint, sizeof(hint), "SLA:%s", exten);
- ast_context_remove_extension(station->autocontext, exten,
- 1, sla_registrar);
- ast_context_remove_extension(station->autocontext, hint,
- PRIORITY_HINT, sla_registrar);
- }
- AST_RWLIST_UNLOCK(&sla_trunks);
- }
-
- while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
- ast_free(trunk_ref);
-
- ast_string_field_free_memory(station);
- ast_free(station);
-}
-
-static void sla_destroy(void)
-{
- struct sla_trunk *trunk;
- struct sla_station *station;
-
- AST_RWLIST_WRLOCK(&sla_trunks);
- while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
- destroy_trunk(trunk);
- AST_RWLIST_UNLOCK(&sla_trunks);
-
- AST_RWLIST_WRLOCK(&sla_stations);
- while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
- destroy_station(station);
- AST_RWLIST_UNLOCK(&sla_stations);
-
- if (sla.thread != AST_PTHREADT_NULL) {
- ast_mutex_lock(&sla.lock);
- sla.stop = 1;
- ast_cond_signal(&sla.cond);
- ast_mutex_unlock(&sla.lock);
- pthread_join(sla.thread, NULL);
- }
-
- /* Drop any created contexts from the dialplan */
- ast_context_destroy(NULL, sla_registrar);
-
- ast_mutex_destroy(&sla.lock);
- ast_cond_destroy(&sla.cond);
-}
-
-static int sla_check_device(const char *device)
-{
- char *tech, *tech_data;
-
- tech_data = ast_strdupa(device);
- tech = strsep(&tech_data, "/");
-
- if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
- return -1;
-
- return 0;
-}
-
-static int sla_build_trunk(struct ast_config *cfg, const char *cat)
-{
- struct sla_trunk *trunk;
- struct ast_variable *var;
- const char *dev;
-
- if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
- ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
- return -1;
- }
-
- if (sla_check_device(dev)) {
- ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
- cat, dev);
- return -1;
- }
-
- if (!(trunk = ast_calloc(1, sizeof(*trunk))))
- return -1;
- if (ast_string_field_init(trunk, 32)) {
- ast_free(trunk);
- return -1;
- }
-
- ast_string_field_set(trunk, name, cat);
- ast_string_field_set(trunk, device, dev);
-
- for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
- if (!strcasecmp(var->name, "autocontext"))
- ast_string_field_set(trunk, autocontext, var->value);
- else if (!strcasecmp(var->name, "ringtimeout")) {
- if (sscanf(var->value, "%u", &trunk->ring_timeout) != 1) {
- ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
- var->value, trunk->name);
- trunk->ring_timeout = 0;
- }
- } else if (!strcasecmp(var->name, "barge"))
- trunk->barge_disabled = ast_false(var->value);
- else if (!strcasecmp(var->name, "hold")) {
- if (!strcasecmp(var->value, "private"))
- trunk->hold_access = SLA_HOLD_PRIVATE;
- else if (!strcasecmp(var->value, "open"))
- trunk->hold_access = SLA_HOLD_OPEN;
- else {
- ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
- var->value, trunk->name);
- }
- } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
- ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
- var->name, var->lineno, SLA_CONFIG_FILE);
- }
- }
-
- if (!ast_strlen_zero(trunk->autocontext)) {
- struct ast_context *context;
- context = ast_context_find_or_create(NULL, trunk->autocontext, sla_registrar);
- if (!context) {
- ast_log(LOG_ERROR, "Failed to automatically find or create "
- "context '%s' for SLA!\n", trunk->autocontext);
- destroy_trunk(trunk);
- return -1;
- }
- if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
- NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
- ast_log(LOG_ERROR, "Failed to automatically create extension "
- "for trunk '%s'!\n", trunk->name);
- destroy_trunk(trunk);
- return -1;
- }
- }
-
- AST_RWLIST_WRLOCK(&sla_trunks);
- AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
- AST_RWLIST_UNLOCK(&sla_trunks);
-
- return 0;
-}
-
-static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
-{
- struct sla_trunk *trunk;
- struct sla_trunk_ref *trunk_ref;
- struct sla_station_ref *station_ref;
- char *trunk_name, *options, *cur;
-
- options = ast_strdupa(var->value);
- trunk_name = strsep(&options, ",");
-
- AST_RWLIST_RDLOCK(&sla_trunks);
- AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
- if (!strcasecmp(trunk->name, trunk_name))
- break;
- }
-
- AST_RWLIST_UNLOCK(&sla_trunks);
- if (!trunk) {
- ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
- return;
- }
- if (!(trunk_ref = create_trunk_ref(trunk)))
- return;
- trunk_ref->state = SLA_TRUNK_STATE_IDLE;
-
- while ((cur = strsep(&options, ","))) {
- char *name, *value = cur;
- name = strsep(&value, "=");
- if (!strcasecmp(name, "ringtimeout")) {
- if (sscanf(value, "%u", &trunk_ref->ring_timeout) != 1) {
- ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
- "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
- trunk_ref->ring_timeout = 0;
- }
- } else if (!strcasecmp(name, "ringdelay")) {
- if (sscanf(value, "%u", &trunk_ref->ring_delay) != 1) {
- ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
- "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
- trunk_ref->ring_delay = 0;
- }
- } else {
- ast_log(LOG_WARNING, "Invalid option '%s' for "
- "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
- }
- }
-
- if (!(station_ref = sla_create_station_ref(station))) {
- ast_free(trunk_ref);
- return;
- }
- ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
- AST_RWLIST_WRLOCK(&sla_trunks);
- AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
- AST_RWLIST_UNLOCK(&sla_trunks);
- AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
-}
-
-static int sla_build_station(struct ast_config *cfg, const char *cat)
-{
- struct sla_station *station;
- struct ast_variable *var;
- const char *dev;
-
- if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
- ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
- return -1;
- }
-
- if (!(station = ast_calloc(1, sizeof(*station))))
- return -1;
- if (ast_string_field_init(station, 32)) {
- ast_free(station);
- return -1;
- }
-
- ast_string_field_set(station, name, cat);
- ast_string_field_set(station, device, dev);
-
- for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
- if (!strcasecmp(var->name, "trunk"))
- sla_add_trunk_to_station(station, var);
- else if (!strcasecmp(var->name, "autocontext"))
- ast_string_field_set(station, autocontext, var->value);
- else if (!strcasecmp(var->name, "ringtimeout")) {
- if (sscanf(var->value, "%u", &station->ring_timeout) != 1) {
- ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
- var->value, station->name);
- station->ring_timeout = 0;
- }
- } else if (!strcasecmp(var->name, "ringdelay")) {
- if (sscanf(var->value, "%u", &station->ring_delay) != 1) {
- ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
- var->value, station->name);
- station->ring_delay = 0;
- }
- } else if (!strcasecmp(var->name, "hold")) {
- if (!strcasecmp(var->value, "private"))
- station->hold_access = SLA_HOLD_PRIVATE;
- else if (!strcasecmp(var->value, "open"))
- station->hold_access = SLA_HOLD_OPEN;
- else {
- ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
- var->value, station->name);
- }
-
- } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
- ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
- var->name, var->lineno, SLA_CONFIG_FILE);
- }
- }
-
- if (!ast_strlen_zero(station->autocontext)) {
- struct ast_context *context;
- struct sla_trunk_ref *trunk_ref;
- context = ast_context_find_or_create(NULL, station->autocontext, sla_registrar);
- if (!context) {
- ast_log(LOG_ERROR, "Failed to automatically find or create "
- "context '%s' for SLA!\n", station->autocontext);
- destroy_station(station);
- return -1;
- }
- /* The extension for when the handset goes off-hook.
- * exten => station1,1,SLAStation(station1) */
- if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
- NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
- ast_log(LOG_ERROR, "Failed to automatically create extension "
- "for trunk '%s'!\n", station->name);
- destroy_station(station);
- return -1;
- }
- AST_RWLIST_RDLOCK(&sla_trunks);
- AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- char exten[AST_MAX_EXTENSION];
- char hint[AST_MAX_APP];
- snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
- snprintf(hint, sizeof(hint), "SLA:%s", exten);
- /* Extension for this line button
- * exten => station1_line1,1,SLAStation(station1_line1) */
- if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
- NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
- ast_log(LOG_ERROR, "Failed to automatically create extension "
- "for trunk '%s'!\n", station->name);
- destroy_station(station);
- return -1;
- }
- /* Hint for this line button
- * exten => station1_line1,hint,SLA:station1_line1 */
- if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
- NULL, NULL, hint, NULL, NULL, sla_registrar)) {
- ast_log(LOG_ERROR, "Failed to automatically create hint "
- "for trunk '%s'!\n", station->name);
- destroy_station(station);
- return -1;
- }
- }
- AST_RWLIST_UNLOCK(&sla_trunks);
- }
-
- AST_RWLIST_WRLOCK(&sla_stations);
- AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
- AST_RWLIST_UNLOCK(&sla_stations);
-
- return 0;
-}
-
-static int sla_load_config(int reload)
-{
- struct ast_config *cfg;
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
- const char *cat = NULL;
- int res = 0;
- const char *val;
-
- if (!reload) {
- ast_mutex_init(&sla.lock);
- ast_cond_init(&sla.cond, NULL);
- }
-
- if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags)))
- return 0; /* Treat no config as normal */
- else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
-
- if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
- sla.attempt_callerid = ast_true(val);
-
- while ((cat = ast_category_browse(cfg, cat)) && !res) {
- const char *type;
- if (!strcasecmp(cat, "general"))
- continue;
- if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
- ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
- SLA_CONFIG_FILE);
- continue;
- }
- if (!strcasecmp(type, "trunk"))
- res = sla_build_trunk(cfg, cat);
- else if (!strcasecmp(type, "station"))
- res = sla_build_station(cfg, cat);
- else {
- ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
- SLA_CONFIG_FILE, type);
- }
- }
-
- ast_config_destroy(cfg);
-
- if (!reload)
- ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
-
- return res;
-}
-
-static int load_config(int reload)
-{
- load_config_meetme();
-
- if (reload) {
- sla_queue_event(SLA_EVENT_RELOAD);
- ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
- "and will be completed when the system is idle.\n");
- return 0;
- }
-
- return sla_load_config(0);
-}
-
-static int unload_module(void)
-{
- int res = 0;
-
- ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
- res = ast_manager_unregister("MeetmeMute");
- res |= ast_manager_unregister("MeetmeUnmute");
- res |= ast_manager_unregister("MeetmeList");
- res |= ast_unregister_application(app4);
- res |= ast_unregister_application(app3);
- res |= ast_unregister_application(app2);
- res |= ast_unregister_application(app);
- res |= ast_unregister_application(slastation_app);
- res |= ast_unregister_application(slatrunk_app);
-
- ast_devstate_prov_del("Meetme");
- ast_devstate_prov_del("SLA");
-
- sla_destroy();
-
- return res;
-}
-
-static int load_module(void)
-{
- int res = 0;
-
- res |= load_config(0);
-
- ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
- res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL,
- action_meetmemute, "Mute a Meetme user");
- res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL,
- action_meetmeunmute, "Unmute a Meetme user");
- res |= ast_manager_register2("MeetmeList", EVENT_FLAG_REPORTING,
- action_meetmelist, "List participants in a conference", mandescr_meetmelist);
- res |= ast_register_application(app4, channel_admin_exec, synopsis4, descrip4);
- res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
- res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
- res |= ast_register_application(app, conf_exec, synopsis, descrip);
- res |= ast_register_application(slastation_app, sla_station_exec,
- slastation_synopsis, slastation_desc);
- res |= ast_register_application(slatrunk_app, sla_trunk_exec,
- slatrunk_synopsis, slatrunk_desc);
-
- res |= ast_devstate_prov_add("Meetme", meetmestate);
- res |= ast_devstate_prov_add("SLA", sla_state);
-
- return res;
-}
-
-static int reload(void)
-{
- return load_config(1);
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MeetMe conference bridge",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
-
diff --git a/trunk/apps/app_milliwatt.c b/trunk/apps/app_milliwatt.c
deleted file mode 100644
index 754faa555..000000000
--- a/trunk/apps/app_milliwatt.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Digital Milliwatt Test
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/channel.h"
-
-static char *app = "Milliwatt";
-
-static char *synopsis = "Generate a Constant 1000Hz tone at 0dbm (mu-law)";
-
-static char *descrip =
-"Milliwatt(): Generate a Constant 1000Hz tone at 0dbm (mu-law)\n";
-
-static char digital_milliwatt[] = {0x1e,0x0b,0x0b,0x1e,0x9e,0x8b,0x8b,0x9e} ;
-
-static void *milliwatt_alloc(struct ast_channel *chan, void *params)
-{
- return ast_calloc(1, sizeof(int));
-}
-
-static void milliwatt_release(struct ast_channel *chan, void *data)
-{
- ast_free(data);
- return;
-}
-
-static int milliwatt_generate(struct ast_channel *chan, void *data, int len, int samples)
-{
- unsigned char buf[AST_FRIENDLY_OFFSET + 640];
- const int maxsamples = sizeof (buf) / sizeof (buf[0]);
- int i, *indexp = (int *) data;
- struct ast_frame wf = {
- .frametype = AST_FRAME_VOICE,
- .subclass = AST_FORMAT_ULAW,
- .offset = AST_FRIENDLY_OFFSET,
- .data = buf + AST_FRIENDLY_OFFSET,
- .src = __FUNCTION__,
- };
-
- /* Instead of len, use samples, because channel.c generator_force
- * generate(chan, tmp, 0, 160) ignores len. In any case, len is
- * a multiple of samples, given by number of samples times bytes per
- * sample. In the case of ulaw, len = samples. for signed linear
- * len = 2 * samples */
- if (samples > maxsamples) {
- ast_log(LOG_WARNING, "Only doing %d samples (%d requested)\n", maxsamples, samples);
- samples = maxsamples;
- }
- len = samples * sizeof (buf[0]);
- wf.datalen = len;
- wf.samples = samples;
-
- /* create a buffer containing the digital milliwatt pattern */
- for (i = 0; i < len; i++) {
- buf[AST_FRIENDLY_OFFSET + i] = digital_milliwatt[(*indexp)++];
- *indexp &= 7;
- }
-
- if (ast_write(chan,&wf) < 0) {
- ast_log(LOG_WARNING,"Failed to write frame to '%s': %s\n",chan->name,strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-static struct ast_generator milliwattgen =
-{
- alloc: milliwatt_alloc,
- release: milliwatt_release,
- generate: milliwatt_generate,
-};
-
-static int milliwatt_exec(struct ast_channel *chan, void *data)
-{
-
- ast_set_write_format(chan, AST_FORMAT_ULAW);
- ast_set_read_format(chan, AST_FORMAT_ULAW);
-
-
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- if (ast_activate_generator(chan,&milliwattgen,"milliwatt") < 0) {
- ast_log(LOG_WARNING,"Failed to activate generator on '%s'\n",chan->name);
- return -1;
- }
-
- while(!ast_safe_sleep(chan, 10000));
-
- ast_deactivate_generator(chan);
-
- return -1;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, milliwatt_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Digital Milliwatt (mu-law) Test Application");
diff --git a/trunk/apps/app_minivm.c b/trunk/apps/app_minivm.c
deleted file mode 100644
index 77497fb19..000000000
--- a/trunk/apps/app_minivm.c
+++ /dev/null
@@ -1,3109 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- * and Edvina AB, Sollentuna, Sweden
- *
- * Mark Spencer <markster@digium.com> (Comedian Mail)
- * and Olle E. Johansson, Edvina.net <oej@edvina.net> (Mini-Voicemail changes)
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief MiniVoiceMail - A Minimal Voicemail System for Asterisk
- *
- * A voicemail system in small building blocks, working together
- * based on the Comedian Mail voicemail system (app_voicemail.c).
- *
- * \par See also
- * \arg \ref Config_minivm
- * \arg \ref Config_minivm_examples
- * \arg \ref App_minivm
- *
- * \ingroup applications
- *
- * \page App_minivm Asterisk Mini-voicemail - A minimal voicemail system
- *
- * This is a minimal voicemail system, building blocks for something
- * else. It is built for multi-language systems.
- * The current version is focused on accounts where voicemail is
- * forwarded to users in e-mail. It's work in progress, with loosed ends hanging
- * around from the old voicemail system and it's configuration.
- *
- * Hopefully, we can expand this to be a full replacement of voicemail() and voicemailmain()
- * in the future.
- *
- * Dialplan applications
- * - minivmRecord - record voicemail and send as e-mail ( \ref minivm_record_exec() )
- * - minivmGreet - Play user's greeting or default greeting ( \ref minivm_greet_exec() )
- * - minivmNotify - Notify user of message ( \ref minivm_notify_exec() )
- * - minivmDelete - Delete voicemail message ( \ref minivm_delete_exec() )
- * - minivmAccMess - Record personal messages (busy | unavailable | temporary)
- *
- * Dialplan functions
- * - MINIVMACCOUNT() - A dialplan function
- * - MINIVMCOUNTER() - Manage voicemail-related counters for accounts or domains
- *
- * CLI Commands
- * - minivm list accounts
- * - minivm list zones
- * - minivm list templates
- * - minivm show stats
- * - minivm show settings
- *
- * Some notes
- * - General configuration in minivm.conf
- * - Users in realtime or configuration file
- * - Or configured on the command line with just the e-mail address
- *
- * Voicemail accounts are identified by userid and domain
- *
- * Language codes are like setlocale - langcode_countrycode
- * \note Don't use language codes like the rest of Asterisk, two letter countrycode. Use
- * language_country like setlocale().
- *
- * Examples:
- * - Swedish, Sweden sv_se
- * - Swedish, Finland sv_fi
- * - English, USA en_us
- * - English, GB en_gb
- *
- * \par See also
- * \arg \ref Config_minivm
- * \arg \ref Config_minivm_examples
- * \arg \ref Minivm_directories
- * \arg \ref app_minivm.c
- * \arg Comedian mail: app_voicemail.c
- * \arg \ref descrip_minivm_accmess
- * \arg \ref descrip_minivm_greet
- * \arg \ref descrip_minivm_record
- * \arg \ref descrip_minivm_delete
- * \arg \ref descrip_minivm_notify
- *
- * \arg \ref App_minivm_todo
- */
-/*! \page Minivm_directories Asterisk Mini-Voicemail Directory structure
- *
- * The directory structure for storing voicemail
- * - AST_SPOOL_DIR - usually /var/spool/asterisk (configurable in asterisk.conf)
- * - MVM_SPOOL_DIR - should be configurable, usually AST_SPOOL_DIR/voicemail
- * - Domain MVM_SPOOL_DIR/domain
- * - Username MVM_SPOOL_DIR/domain/username
- * - /greet : Recording of account owner's name
- * - /busy : Busy message
- * - /unavailable : Unavailable message
- * - /temp : Temporary message
- *
- * For account anita@localdomain.xx the account directory would as a default be
- * \b /var/spool/asterisk/voicemail/localdomain.xx/anita
- *
- * To avoid transcoding, these sound files should be converted into several formats
- * They are recorded in the format closest to the incoming streams
- *
- *
- * Back: \ref App_minivm
- */
-
-/*! \page Config_minivm_examples Example dialplan for Mini-Voicemail
- * \section Example dialplan scripts for Mini-Voicemail
- * \verbinclude extensions_minivm.conf.sample
- *
- * Back: \ref App_minivm
- */
-
-/*! \page App_minivm_todo Asterisk Mini-Voicemail - todo
- * - configure accounts from AMI?
- * - test, test, test, test
- * - fix "vm-theextensionis.gsm" voiceprompt from Allison in various formats
- * "The extension you are calling"
- * - For trunk, consider using channel storage for information passing between small applications
- * - Set default directory for voicemail
- * - New app for creating directory for account if it does not exist
- * - Re-insert code for IMAP storage at some point
- * - Jabber integration for notifications
- * - Figure out how to handle video in voicemail
- * - Integration with the HTTP server
- * - New app for moving messages between mailboxes, and optionally mark it as "new"
- *
- * For Asterisk 1.4/trunk
- * - Use string fields for minivm_account
- *
- * Back: \ref App_minivm
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <ctype.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <time.h>
-#include <dirent.h>
-#include <locale.h>
-
-
-#include "asterisk/paths.h" /* use various paths */
-#include "asterisk/astobj.h"
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/config.h"
-#include "asterisk/say.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-#include "asterisk/manager.h"
-#include "asterisk/dsp.h"
-#include "asterisk/localtime.h"
-#include "asterisk/cli.h"
-#include "asterisk/utils.h"
-#include "asterisk/linkedlists.h"
-#include "asterisk/callerid.h"
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-
-#define MVM_REVIEW (1 << 0) /*!< Review message */
-#define MVM_OPERATOR (1 << 1) /*!< Operator exit during voicemail recording */
-#define MVM_REALTIME (1 << 2) /*!< This user is a realtime account */
-#define MVM_SVMAIL (1 << 3)
-#define MVM_ENVELOPE (1 << 4)
-#define MVM_PBXSKIP (1 << 9)
-#define MVM_ALLOCED (1 << 13)
-
-/*! \brief Default mail command to mail voicemail. Change it with the
- mailcmd= command in voicemail.conf */
-#define SENDMAIL "/usr/sbin/sendmail -t"
-
-#define SOUND_INTRO "vm-intro"
-#define B64_BASEMAXINLINE 256 /*!< Buffer size for Base 64 attachment encoding */
-#define B64_BASELINELEN 72 /*!< Line length for Base 64 endoded messages */
-#define EOL "\r\n"
-
-#define MAX_DATETIME_FORMAT 512
-#define MAX_NUM_CID_CONTEXTS 10
-
-#define ERROR_LOCK_PATH -100
-#define VOICEMAIL_DIR_MODE 0700
-
-#define VOICEMAIL_CONFIG "minivm.conf"
-#define ASTERISK_USERNAME "asterisk" /*!< Default username for sending mail is asterisk\@localhost */
-
-/*! \brief Message types for notification */
-enum mvm_messagetype {
- MVM_MESSAGE_EMAIL,
- MVM_MESSAGE_PAGE
- /* For trunk: MVM_MESSAGE_JABBER, */
-};
-
-static char MVM_SPOOL_DIR[PATH_MAX];
-
-/* Module declarations */
-static char *app_minivm_record = "MinivmRecord"; /* Leave a message */
-static char *app_minivm_greet = "MinivmGreet"; /* Play voicemail prompts */
-static char *app_minivm_notify = "MinivmNotify"; /* Notify about voicemail by using one of several methods */
-static char *app_minivm_delete = "MinivmDelete"; /* Notify about voicemail by using one of several methods */
-static char *app_minivm_accmess = "MinivmAccMess"; /* Record personal voicemail messages */
-
-static char *synopsis_minivm_record = "Receive Mini-Voicemail and forward via e-mail";
-static char *descrip_minivm_record =
- " MinivmRecord(username@domain[,options]):\n"
- "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
- "MiniVM records audio file in configured format and forwards message to e-mail and pager.\n"
- "If there's no user account for that address, a temporary account will\n"
- "be used with default options.\n"
- "The recorded file name and path will be stored in MINIVM_FILENAME and the \n"
- "duration of the message will be stored in MINIVM_DURATION\n"
- "\nNote: If the caller hangs up after the recording, the only way to send\n"
- "the message and clean up is to execute in the \"h\" extension.\n"
- "\nThe application will exit if any of the following DTMF digits are \n"
- "received and the requested extension exist in the current context.\n"
- " 0 - Jump to the 'o' extension in the current dialplan context.\n"
- " * - Jump to the 'a' extension in the current dialplan context.\n"
- "\n"
- "Result is given in channel variable MINIVM_RECORD_STATUS\n"
- " The possible values are: SUCCESS | USEREXIT | FAILED\n\n"
- " Options:\n"
- " g(#) - Use the specified amount of gain when recording the voicemail\n"
- " message. The units are whole-number decibels (dB).\n"
- "\n";
-
-static char *synopsis_minivm_greet = "Play Mini-Voicemail prompts";
-static char *descrip_minivm_greet =
- " MinivmGreet(username@domain[,options]):\n"
- "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
- "MinivmGreet() plays default prompts or user specific prompts for an account.\n"
- "Busy and unavailable messages can be choosen, but will be overridden if a temporary\n"
- "message exists for the account.\n"
- "\n"
- "Result is given in channel variable MINIVM_GREET_STATUS\n"
- " The possible values are: SUCCESS | USEREXIT | FAILED\n\n"
- " Options:\n"
- " b - Play the 'busy' greeting to the calling party.\n"
- " s - Skip the playback of instructions for leaving a message to the\n"
- " calling party.\n"
- " u - Play the 'unavailable greeting.\n"
- "\n";
-
-static char *synopsis_minivm_notify = "Notify voicemail owner about new messages.";
-static char *descrip_minivm_notify =
- " MinivmNotify(username@domain[,template]):\n"
- "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
- "MiniVMnotify forwards messages about new voicemail to e-mail and pager.\n"
- "If there's no user account for that address, a temporary account will\n"
- "be used with default options (set in minivm.conf).\n"
- "The recorded file name and path will be read from MVM_FILENAME and the \n"
- "duration of the message will be accessed from MVM_DURATION (set by MinivmRecord() )\n"
- "If the channel variable MVM_COUNTER is set, this will be used in the\n"
- "message file name and available in the template for the message.\n"
- "If not template is given, the default email template will be used to send email and\n"
- "default pager template to send paging message (if the user account is configured with\n"
- "a paging address.\n"
- "\n"
- "Result is given in channel variable MINIVM_NOTIFY_STATUS\n"
- " The possible values are: SUCCESS | FAILED\n"
- "\n";
-
-static char *synopsis_minivm_delete = "Delete Mini-Voicemail voicemail messages";
-static char *descrip_minivm_delete =
- " MinivmDelete(filename):\n"
- "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
- "It deletes voicemail file set in MVM_FILENAME or given filename.\n"
- "\n"
- "Result is given in channel variable MINIVM_DELETE_STATUS\n"
- " The possible values are: SUCCESS | FAILED\n"
- " FAILED is set if the file does not exist or can't be deleted.\n"
- "\n";
-
-static char *synopsis_minivm_accmess = "Record account specific messages";
-static char *descrip_minivm_accmess =
- " MinivmAccmess(username@domain,option):\n"
- "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
- "Use this application to record account specific audio/video messages for\n"
- "busy, unavailable and temporary messages.\n"
- "Account specific directories will be created if they do not exist.\n"
- "\nThe option selects message to be recorded:\n"
- " u Unavailable\n"
- " b Busy\n"
- " t Temporary (overrides busy and unavailable)\n"
- " n Account name\n"
- "\n"
- "Result is given in channel variable MINIVM_ACCMESS_STATUS\n"
- " The possible values are: SUCCESS | FAILED\n"
- " FAILED is set if the file can't be created.\n"
- "\n";
-
-enum {
- OPT_SILENT = (1 << 0),
- OPT_BUSY_GREETING = (1 << 1),
- OPT_UNAVAIL_GREETING = (1 << 2),
- OPT_TEMP_GREETING = (1 << 3),
- OPT_NAME_GREETING = (1 << 4),
- OPT_RECORDGAIN = (1 << 5),
-} minivm_option_flags;
-
-enum {
- OPT_ARG_RECORDGAIN = 0,
- OPT_ARG_ARRAY_SIZE = 1,
-} minivm_option_args;
-
-AST_APP_OPTIONS(minivm_app_options, {
- AST_APP_OPTION('s', OPT_SILENT),
- AST_APP_OPTION('b', OPT_BUSY_GREETING),
- AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
- AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
-});
-
-AST_APP_OPTIONS(minivm_accmess_options, {
- AST_APP_OPTION('b', OPT_BUSY_GREETING),
- AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
- AST_APP_OPTION('t', OPT_TEMP_GREETING),
- AST_APP_OPTION('n', OPT_NAME_GREETING),
-});
-
-/*! \brief Structure for linked list of Mini-Voicemail users: \ref minivm_accounts */
-struct minivm_account {
- char username[AST_MAX_CONTEXT]; /*!< Mailbox username */
- char domain[AST_MAX_CONTEXT]; /*!< Voicemail domain */
-
- char pincode[10]; /*!< Secret pin code, numbers only */
- char fullname[120]; /*!< Full name, for directory app */
- char email[80]; /*!< E-mail address - override */
- char pager[80]; /*!< E-mail address to pager (no attachment) */
- char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Voicemail account account code */
- char serveremail[80]; /*!< From: Mail address */
- char externnotify[160]; /*!< Configurable notification command */
- char language[MAX_LANGUAGE]; /*!< Config: Language setting */
- char zonetag[80]; /*!< Time zone */
- char uniqueid[20]; /*!< Unique integer identifier */
- char exit[80]; /*!< Options for exiting from voicemail() */
- char attachfmt[80]; /*!< Format for voicemail audio file attachment */
- char etemplate[80]; /*!< Pager template */
- char ptemplate[80]; /*!< Voicemail format */
- unsigned int flags; /*!< MVM_ flags */
- struct ast_variable *chanvars; /*!< Variables for e-mail template */
- double volgain; /*!< Volume gain for voicemails sent via e-mail */
- AST_LIST_ENTRY(minivm_account) list;
-};
-
-/*! \brief The list of e-mail accounts */
-static AST_LIST_HEAD_STATIC(minivm_accounts, minivm_account);
-
-/*! \brief Linked list of e-mail templates in various languages
- These are used as templates for e-mails, pager messages and jabber messages
- \ref message_templates
-*/
-struct minivm_template {
- char name[80]; /*!< Template name */
- char *body; /*!< Body of this template */
- char fromaddress[100]; /*!< Who's sending the e-mail? */
- char serveremail[80]; /*!< From: Mail address */
- char subject[100]; /*!< Subject line */
- char charset[32]; /*!< Default character set for this template */
- char locale[20]; /*!< Locale for setlocale() */
- char dateformat[80]; /*!< Date format to use in this attachment */
- int attachment; /*!< Attachment of media yes/no - no for pager messages */
- AST_LIST_ENTRY(minivm_template) list; /*!< List mechanics */
-};
-
-/*! \brief The list of e-mail templates */
-static AST_LIST_HEAD_STATIC(message_templates, minivm_template);
-
-/*! \brief Options for leaving voicemail with the voicemail() application */
-struct leave_vm_options {
- unsigned int flags;
- signed char record_gain;
-};
-
-/*! \brief Structure for base64 encoding */
-struct b64_baseio {
- int iocp;
- int iolen;
- int linelength;
- int ateof;
- unsigned char iobuf[B64_BASEMAXINLINE];
-};
-
-/*! \brief Voicemail time zones */
-struct minivm_zone {
- char name[80]; /*!< Name of this time zone */
- char timezone[80]; /*!< Timezone definition */
- char msg_format[BUFSIZ]; /*!< Not used in minivm ...yet */
- AST_LIST_ENTRY(minivm_zone) list; /*!< List mechanics */
-};
-
-/*! \brief The list of e-mail time zones */
-static AST_LIST_HEAD_STATIC(minivm_zones, minivm_zone);
-
-/*! \brief Structure for gathering statistics */
-struct minivm_stats {
- int voicemailaccounts; /*!< Number of static accounts */
- int timezones; /*!< Number of time zones */
- int templates; /*!< Number of templates */
-
- struct timeval reset; /*!< Time for last reset */
- int receivedmessages; /*!< Number of received messages since reset */
- struct timeval lastreceived; /*!< Time for last voicemail sent */
-};
-
-/*! \brief Statistics for voicemail */
-static struct minivm_stats global_stats;
-
-AST_MUTEX_DEFINE_STATIC(minivmlock); /*!< Lock to protect voicemail system */
-AST_MUTEX_DEFINE_STATIC(minivmloglock); /*!< Lock to protect voicemail system log file */
-
-FILE *minivmlogfile; /*!< The minivm log file */
-
-static int global_vmminmessage; /*!< Minimum duration of messages */
-static int global_vmmaxmessage; /*!< Maximum duration of message */
-static int global_maxsilence; /*!< Maximum silence during recording */
-static int global_maxgreet; /*!< Maximum length of prompts */
-static int global_silencethreshold = 128;
-static char global_mailcmd[160]; /*!< Configurable mail cmd */
-static char global_externnotify[160]; /*!< External notification application */
-static char global_logfile[PATH_MAX]; /*!< Global log file for messages */
-static char default_vmformat[80];
-
-static struct ast_flags globalflags = {0}; /*!< Global voicemail flags */
-static int global_saydurationminfo;
-static char global_charset[32]; /*!< Global charset in messages */
-
-static double global_volgain; /*!< Volume gain for voicmemail via e-mail */
-
-/*! \brief Default dateformat, can be overridden in configuration file */
-#define DEFAULT_DATEFORMAT "%A, %B %d, %Y at %r"
-#define DEFAULT_CHARSET "ISO-8859-1"
-
-/* Forward declarations */
-static char *message_template_parse_filebody(const char *filename);
-static char *message_template_parse_emailbody(const char *body);
-static int create_vmaccount(char *name, struct ast_variable *var, int realtime);
-static struct minivm_account *find_user_realtime(const char *domain, const char *username);
-static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
-
-/*! \brief Create message template */
-static struct minivm_template *message_template_create(const char *name)
-{
- struct minivm_template *template;
-
- template = ast_calloc(1, sizeof(*template));
- if (!template)
- return NULL;
-
- /* Set some defaults for templates */
- ast_copy_string(template->name, name, sizeof(template->name));
- ast_copy_string(template->dateformat, DEFAULT_DATEFORMAT, sizeof(template->dateformat));
- ast_copy_string(template->charset, DEFAULT_CHARSET, sizeof(template->charset));
- ast_copy_string(template->subject, "New message in mailbox ${MVM_USERNAME}@${MVM_DOMAIN}", sizeof(template->subject));
- template->attachment = TRUE;
-
- return template;
-}
-
-/*! \brief Release memory allocated by message template */
-static void message_template_free(struct minivm_template *template)
-{
- if (template->body)
- ast_free(template->body);
-
- ast_free (template);
-}
-
-/*! \brief Build message template from configuration */
-static int message_template_build(const char *name, struct ast_variable *var)
-{
- struct minivm_template *template;
- int error = 0;
-
- template = message_template_create(name);
- if (!template) {
- ast_log(LOG_ERROR, "Out of memory, can't allocate message template object %s.\n", name);
- return -1;
- }
-
- while (var) {
- ast_debug(3, "-_-_- Configuring template option %s = \"%s\" for template %s\n", var->name, var->value, name);
- if (!strcasecmp(var->name, "fromaddress")) {
- ast_copy_string(template->fromaddress, var->value, sizeof(template->fromaddress));
- } else if (!strcasecmp(var->name, "fromemail")) {
- ast_copy_string(template->serveremail, var->value, sizeof(template->serveremail));
- } else if (!strcasecmp(var->name, "subject")) {
- ast_copy_string(template->subject, var->value, sizeof(template->subject));
- } else if (!strcasecmp(var->name, "locale")) {
- ast_copy_string(template->locale, var->value, sizeof(template->locale));
- } else if (!strcasecmp(var->name, "attachmedia")) {
- template->attachment = ast_true(var->value);
- } else if (!strcasecmp(var->name, "dateformat")) {
- ast_copy_string(template->dateformat, var->value, sizeof(template->dateformat));
- } else if (!strcasecmp(var->name, "charset")) {
- ast_copy_string(template->charset, var->value, sizeof(template->charset));
- } else if (!strcasecmp(var->name, "templatefile")) {
- if (template->body)
- ast_free(template->body);
- template->body = message_template_parse_filebody(var->value);
- if (!template->body) {
- ast_log(LOG_ERROR, "Error reading message body definition file %s\n", var->value);
- error++;
- }
- } else if (!strcasecmp(var->name, "messagebody")) {
- if (template->body)
- ast_free(template->body);
- template->body = message_template_parse_emailbody(var->value);
- if (!template->body) {
- ast_log(LOG_ERROR, "Error parsing message body definition:\n %s\n", var->value);
- error++;
- }
- } else {
- ast_log(LOG_ERROR, "Unknown message template configuration option \"%s=%s\"\n", var->name, var->value);
- error++;
- }
- var = var->next;
- }
- if (error)
- ast_log(LOG_ERROR, "-- %d errors found parsing message template definition %s\n", error, name);
-
- AST_LIST_LOCK(&message_templates);
- AST_LIST_INSERT_TAIL(&message_templates, template, list);
- AST_LIST_UNLOCK(&message_templates);
-
- global_stats.templates++;
-
- return error;
-}
-
-/*! \brief Find named template */
-static struct minivm_template *message_template_find(const char *name)
-{
- struct minivm_template *this, *res = NULL;
-
- if (ast_strlen_zero(name))
- return NULL;
-
- AST_LIST_LOCK(&message_templates);
- AST_LIST_TRAVERSE(&message_templates, this, list) {
- if (!strcasecmp(this->name, name)) {
- res = this;
- break;
- }
- }
- AST_LIST_UNLOCK(&message_templates);
-
- return res;
-}
-
-
-/*! \brief Clear list of templates */
-static void message_destroy_list(void)
-{
- struct minivm_template *this;
- AST_LIST_LOCK(&message_templates);
- while ((this = AST_LIST_REMOVE_HEAD(&message_templates, list)))
- message_template_free(this);
-
- AST_LIST_UNLOCK(&message_templates);
-}
-
-/*! \brief read buffer from file (base64 conversion) */
-static int b64_inbuf(struct b64_baseio *bio, FILE *fi)
-{
- int l;
-
- if (bio->ateof)
- return 0;
-
- if ((l = fread(bio->iobuf, 1, B64_BASEMAXINLINE,fi)) <= 0) {
- if (ferror(fi))
- return -1;
-
- bio->ateof = 1;
- return 0;
- }
-
- bio->iolen= l;
- bio->iocp= 0;
-
- return 1;
-}
-
-/*! \brief read character from file to buffer (base64 conversion) */
-static int b64_inchar(struct b64_baseio *bio, FILE *fi)
-{
- if (bio->iocp >= bio->iolen) {
- if (!b64_inbuf(bio, fi))
- return EOF;
- }
-
- return bio->iobuf[bio->iocp++];
-}
-
-/*! \brief write buffer to file (base64 conversion) */
-static int b64_ochar(struct b64_baseio *bio, int c, FILE *so)
-{
- if (bio->linelength >= B64_BASELINELEN) {
- if (fputs(EOL,so) == EOF)
- return -1;
-
- bio->linelength= 0;
- }
-
- if (putc(((unsigned char) c), so) == EOF)
- return -1;
-
- bio->linelength++;
-
- return 1;
-}
-
-/*! \brief Encode file to base64 encoding for email attachment (base64 conversion) */
-static int base_encode(char *filename, FILE *so)
-{
- unsigned char dtable[B64_BASEMAXINLINE];
- int i,hiteof= 0;
- FILE *fi;
- struct b64_baseio bio;
-
- memset(&bio, 0, sizeof(bio));
- bio.iocp = B64_BASEMAXINLINE;
-
- if (!(fi = fopen(filename, "rb"))) {
- ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
- return -1;
- }
-
- for (i= 0; i<9; i++) {
- dtable[i]= 'A'+i;
- dtable[i+9]= 'J'+i;
- dtable[26+i]= 'a'+i;
- dtable[26+i+9]= 'j'+i;
- }
- for (i= 0; i < 8; i++) {
- dtable[i+18]= 'S'+i;
- dtable[26+i+18]= 's'+i;
- }
- for (i= 0; i < 10; i++) {
- dtable[52+i]= '0'+i;
- }
- dtable[62]= '+';
- dtable[63]= '/';
-
- while (!hiteof){
- unsigned char igroup[3], ogroup[4];
- int c,n;
-
- igroup[0]= igroup[1]= igroup[2]= 0;
-
- for (n= 0; n < 3; n++) {
- if ((c = b64_inchar(&bio, fi)) == EOF) {
- hiteof= 1;
- break;
- }
- igroup[n]= (unsigned char)c;
- }
-
- if (n> 0) {
- ogroup[0]= dtable[igroup[0]>>2];
- ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
- ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
- ogroup[3]= dtable[igroup[2]&0x3F];
-
- if (n<3) {
- ogroup[3]= '=';
-
- if (n<2)
- ogroup[2]= '=';
- }
-
- for (i= 0;i<4;i++)
- b64_ochar(&bio, ogroup[i], so);
- }
- }
-
- /* Put end of line - line feed */
- if (fputs(EOL, so) == EOF)
- return 0;
-
- fclose(fi);
-
- return 1;
-}
-
-static int get_date(char *s, int len)
-{
- struct ast_tm tm;
- struct timeval tv = ast_tvnow();
-
- ast_localtime(&tv, &tm, NULL);
- return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
-}
-
-
-/*! \brief Free user structure - if it's allocated */
-static void free_user(struct minivm_account *vmu)
-{
- if (vmu->chanvars)
- ast_variables_destroy(vmu->chanvars);
- ast_free(vmu);
-}
-
-
-
-/*! \brief Prepare for voicemail template by adding channel variables
- to the channel
-*/
-static void prep_email_sub_vars(struct ast_channel *channel, const struct minivm_account *vmu, const char *cidnum, const char *cidname, const char *dur, const char *date, const char *counter)
-{
- char callerid[256];
- struct ast_variable *var;
-
- if (!channel) {
- ast_log(LOG_ERROR, "No allocated channel, giving up...\n");
- return;
- }
-
- for (var = vmu->chanvars ; var ; var = var->next)
- pbx_builtin_setvar_helper(channel, var->name, var->value);
-
- /* Prepare variables for substition in email body and subject */
- pbx_builtin_setvar_helper(channel, "MVM_NAME", vmu->fullname);
- pbx_builtin_setvar_helper(channel, "MVM_DUR", dur);
- pbx_builtin_setvar_helper(channel, "MVM_DOMAIN", vmu->domain);
- pbx_builtin_setvar_helper(channel, "MVM_USERNAME", vmu->username);
- pbx_builtin_setvar_helper(channel, "MVM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
- pbx_builtin_setvar_helper(channel, "MVM_CIDNAME", (cidname ? cidname : "an unknown caller"));
- pbx_builtin_setvar_helper(channel, "MVM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
- pbx_builtin_setvar_helper(channel, "MVM_DATE", date);
- if (!ast_strlen_zero(counter))
- pbx_builtin_setvar_helper(channel, "MVM_COUNTER", counter);
-}
-
-/*! \brief Set default values for Mini-Voicemail users */
-static void populate_defaults(struct minivm_account *vmu)
-{
- ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
- ast_copy_string(vmu->attachfmt, default_vmformat, sizeof(vmu->attachfmt));
- vmu->volgain = global_volgain;
-}
-
-/*! \brief Fix quote of mail headers for non-ascii characters */
-static char *mailheader_quote(const char *from, char *to, size_t len)
-{
- char *ptr = to;
- *ptr++ = '"';
- for (; ptr < to + len - 1; from++) {
- if (*from == '"')
- *ptr++ = '\\';
- else if (*from == '\0')
- break;
- *ptr++ = *from;
- }
- if (ptr < to + len - 1)
- *ptr++ = '"';
- *ptr = '\0';
- return to;
-}
-
-
-/*! \brief Allocate new vm user and set default values */
-static struct minivm_account *mvm_user_alloc(void)
-{
- struct minivm_account *new;
-
- new = ast_calloc(1, sizeof(*new));
- if (!new)
- return NULL;
- populate_defaults(new);
-
- return new;
-}
-
-
-/*! \brief Clear list of users */
-static void vmaccounts_destroy_list(void)
-{
- struct minivm_account *this;
- AST_LIST_LOCK(&minivm_accounts);
- while ((this = AST_LIST_REMOVE_HEAD(&minivm_accounts, list)))
- ast_free(this);
- AST_LIST_UNLOCK(&minivm_accounts);
-}
-
-
-/*! \brief Find user from static memory object list */
-static struct minivm_account *find_account(const char *domain, const char *username, int createtemp)
-{
- struct minivm_account *vmu = NULL, *cur;
-
-
- if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
- ast_log(LOG_NOTICE, "No username or domain? \n");
- return NULL;
- }
- ast_debug(3, "-_-_-_- Looking for voicemail user %s in domain %s\n", username, domain);
-
- AST_LIST_LOCK(&minivm_accounts);
- AST_LIST_TRAVERSE(&minivm_accounts, cur, list) {
- /* Is this the voicemail account we're looking for? */
- if (!strcasecmp(domain, cur->domain) && !strcasecmp(username, cur->username))
- break;
- }
- AST_LIST_UNLOCK(&minivm_accounts);
-
- if (cur) {
- ast_debug(3, "-_-_- Found account for %s@%s\n", username, domain);
- vmu = cur;
-
- } else
- vmu = find_user_realtime(domain, username);
-
- if (createtemp && !vmu) {
- /* Create a temporary user, send e-mail and be gone */
- vmu = mvm_user_alloc();
- ast_set2_flag(vmu, TRUE, MVM_ALLOCED);
- if (vmu) {
- ast_copy_string(vmu->username, username, sizeof(vmu->username));
- ast_copy_string(vmu->domain, domain, sizeof(vmu->domain));
- ast_debug(1, "--- Created temporary account\n");
- }
-
- }
- return vmu;
-}
-
-/*! \brief Find user in realtime storage
- Returns pointer to minivm_account structure
-*/
-static struct minivm_account *find_user_realtime(const char *domain, const char *username)
-{
- struct ast_variable *var;
- struct minivm_account *retval;
- char name[MAXHOSTNAMELEN];
-
- retval = mvm_user_alloc();
- if (!retval)
- return NULL;
-
- if (username)
- ast_copy_string(retval->username, username, sizeof(retval->username));
-
- populate_defaults(retval);
- var = ast_load_realtime("minivm", "username", username, "domain", domain, NULL);
-
- if (!var) {
- ast_free(retval);
- return NULL;
- }
-
- snprintf(name, sizeof(name), "%s@%s", username, domain);
- create_vmaccount(name, var, TRUE);
-
- ast_variables_destroy(var);
- return retval;
-}
-
-/*! \brief Send voicemail with audio file as an attachment */
-static int sendmail(struct minivm_template *template, struct minivm_account *vmu, char *cidnum, char *cidname, const char *filename, char *format, int duration, int attach_user_voicemail, enum mvm_messagetype type, const char *counter)
-{
- FILE *p = NULL;
- int pfd;
- char email[256] = "";
- char who[256] = "";
- char date[256];
- char bound[256];
- char fname[PATH_MAX];
- char dur[PATH_MAX];
- char tmp[80] = "/tmp/astmail-XXXXXX";
- char tmp2[PATH_MAX];
- struct timeval now;
- struct ast_tm tm;
- struct minivm_zone *the_zone = NULL;
- int len_passdata;
- struct ast_channel *ast;
- char *finalfilename;
- char *passdata = NULL;
- char *passdata2 = NULL;
- char *fromaddress;
- char *fromemail;
-
- if (type == MVM_MESSAGE_EMAIL) {
- if (vmu && !ast_strlen_zero(vmu->email)) {
- ast_copy_string(email, vmu->email, sizeof(email));
- } else if (!ast_strlen_zero(vmu->username) && !ast_strlen_zero(vmu->domain))
- snprintf(email, sizeof(email), "%s@%s", vmu->username, vmu->domain);
- } else if (type == MVM_MESSAGE_PAGE) {
- ast_copy_string(email, vmu->pager, sizeof(email));
- }
-
- if (ast_strlen_zero(email)) {
- ast_log(LOG_WARNING, "No address to send message to.\n");
- return -1;
- }
-
- ast_debug(3, "-_-_- Sending mail to %s@%s - Using template %s\n", vmu->username, vmu->domain, template->name);
-
- if (!strcmp(format, "wav49"))
- format = "WAV";
-
-
- /* If we have a gain option, process it now with sox */
- if (type == MVM_MESSAGE_EMAIL && (vmu->volgain < -.001 || vmu->volgain > .001) ) {
- char newtmp[PATH_MAX];
- char tmpcmd[PATH_MAX];
- int tmpfd;
-
- ast_copy_string(newtmp, "/tmp/XXXXXX", sizeof(newtmp));
- ast_debug(3, "newtmp: %s\n", newtmp);
- tmpfd = mkstemp(newtmp);
- snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, filename, format, newtmp, format);
- ast_safe_system(tmpcmd);
- finalfilename = newtmp;
- ast_debug(3, "-- VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", filename, format, vmu->volgain, vmu->username);
- } else {
- finalfilename = ast_strdupa(filename);
- }
-
- /* Create file name */
- snprintf(fname, sizeof(fname), "%s.%s", finalfilename, format);
-
- if (template->attachment)
- ast_debug(1, "-- Attaching file '%s', format '%s', uservm is '%d'\n", finalfilename, format, attach_user_voicemail);
-
- /* Make a temporary file instead of piping directly to sendmail, in case the mail
- command hangs */
- pfd = mkstemp(tmp);
- if (pfd > -1) {
- p = fdopen(pfd, "w");
- if (!p) {
- close(pfd);
- pfd = -1;
- }
- ast_debug(1, "-_-_- Opening temp file for e-mail: %s\n", tmp);
- }
- if (!p) {
- ast_log(LOG_WARNING, "Unable to open temporary file '%s'\n", tmp);
- return -1;
- }
- /* Allocate channel used for chanvar substitution */
- ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0);
-
-
- snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
-
- /* Does this user have a timezone specified? */
- if (!ast_strlen_zero(vmu->zonetag)) {
- /* Find the zone in the list */
- struct minivm_zone *z;
- AST_LIST_LOCK(&minivm_zones);
- AST_LIST_TRAVERSE(&minivm_zones, z, list) {
- if (strcmp(z->name, vmu->zonetag))
- continue;
- the_zone = z;
- }
- AST_LIST_UNLOCK(&minivm_zones);
- }
-
- now = ast_tvnow();
- ast_localtime(&now, &tm, the_zone ? the_zone->timezone : NULL);
- ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
-
- /* Start printing the email to the temporary file */
- fprintf(p, "Date: %s\n", date);
-
- /* Set date format for voicemail mail */
- ast_strftime(date, sizeof(date), template->dateformat, &tm);
-
-
- /* Populate channel with channel variables for substitution */
- prep_email_sub_vars(ast, vmu, cidnum, cidname, dur, date, counter);
-
- /* Find email address to use */
- /* If there's a server e-mail adress in the account, user that, othterwise template */
- fromemail = ast_strlen_zero(vmu->serveremail) ? template->serveremail : vmu->serveremail;
-
- /* Find name to user for server e-mail */
- fromaddress = ast_strlen_zero(template->fromaddress) ? "" : template->fromaddress;
-
- /* If needed, add hostname as domain */
- if (ast_strlen_zero(fromemail))
- fromemail = "asterisk";
-
- if (strchr(fromemail, '@'))
- ast_copy_string(who, fromemail, sizeof(who));
- else {
- char host[MAXHOSTNAMELEN];
- gethostname(host, sizeof(host)-1);
- snprintf(who, sizeof(who), "%s@%s", fromemail, host);
- }
-
- if (ast_strlen_zero(fromaddress)) {
- fprintf(p, "From: Asterisk PBX <%s>\n", who);
- } else {
- /* Allocate a buffer big enough for variable substitution */
- int vmlen = strlen(fromaddress) * 3 + 200;
-
- ast_debug(4, "-_-_- Fromaddress template: %s\n", fromaddress);
- if ((passdata = alloca(vmlen))) {
- pbx_substitute_variables_helper(ast, fromaddress, passdata, vmlen);
- len_passdata = strlen(passdata) * 2 + 3;
- passdata2 = alloca(len_passdata);
- fprintf(p, "From: %s <%s>\n", mailheader_quote(passdata, passdata2, len_passdata), who);
- } else {
- ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
- fclose(p);
- return -1;
- }
- }
- ast_debug(4, "-_-_- Fromstring now: %s\n", ast_strlen_zero(passdata) ? "-default-" : passdata);
-
- fprintf(p, "Message-ID: <Asterisk-%d-%s-%d-%s>\n", (unsigned int)rand(), vmu->username, (int)getpid(), who);
- len_passdata = strlen(vmu->fullname) * 2 + 3;
- passdata2 = alloca(len_passdata);
- if (!ast_strlen_zero(vmu->email))
- fprintf(p, "To: %s <%s>\n", mailheader_quote(vmu->fullname, passdata2, len_passdata), vmu->email);
- else
- fprintf(p, "To: %s <%s@%s>\n", mailheader_quote(vmu->fullname, passdata2, len_passdata), vmu->username, vmu->domain);
-
- if (!ast_strlen_zero(template->subject)) {
- char *passdata;
- int vmlen = strlen(template->subject) * 3 + 200;
- if ((passdata = alloca(vmlen))) {
- pbx_substitute_variables_helper(ast, template->subject, passdata, vmlen);
- fprintf(p, "Subject: %s\n", passdata);
- } else {
- ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
- fclose(p);
- return -1;
- }
-
- ast_debug(4, "-_-_- Subject now: %s\n", passdata);
-
- } else {
- fprintf(p, "Subject: New message in mailbox %s@%s\n", vmu->username, vmu->domain);
- ast_debug(1, "-_-_- Using default subject for this email \n");
- }
-
-
- if (option_debug > 2)
- fprintf(p, "X-Asterisk-debug: template %s user account %s@%s\n", template->name, vmu->username, vmu->domain);
- fprintf(p, "MIME-Version: 1.0\n");
-
- /* Something unique. */
- snprintf(bound, sizeof(bound), "voicemail_%s%d%d", vmu->username, (int)getpid(), (unsigned int)rand());
-
- fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
-
- fprintf(p, "--%s\n", bound);
- fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", global_charset);
- if (!ast_strlen_zero(template->body)) {
- char *passdata;
- int vmlen = strlen(template->body)*3 + 200;
- if ((passdata = alloca(vmlen))) {
- pbx_substitute_variables_helper(ast, template->body, passdata, vmlen);
- ast_debug(3, "Message now: %s\n-----\n", passdata);
- fprintf(p, "%s\n", passdata);
- } else
- ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
- } else {
- fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message \n"
-
- "in mailbox %s from %s, on %s so you might\n"
- "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
- dur, vmu->username, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
- ast_debug(3, "Using default message body (no template)\n-----\n");
- }
- /* Eww. We want formats to tell us their own MIME type */
- if (template->attachment) {
- char *ctype = "audio/x-";
- ast_debug(3, "-_-_- Attaching file to message: %s\n", fname);
- if (!strcasecmp(format, "ogg"))
- ctype = "application/";
-
- fprintf(p, "--%s\n", bound);
- fprintf(p, "Content-Type: %s%s; name=\"voicemailmsg.%s\"\n", ctype, format, format);
- fprintf(p, "Content-Transfer-Encoding: base64\n");
- fprintf(p, "Content-Description: Voicemail sound attachment.\n");
- fprintf(p, "Content-Disposition: attachment; filename=\"voicemail%s.%s\"\n\n", counter ? counter : "", format);
-
- base_encode(fname, p);
- fprintf(p, "\n\n--%s--\n.\n", bound);
- }
- fclose(p);
- snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", global_mailcmd, tmp, tmp);
- ast_safe_system(tmp2);
- ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : "");
- ast_debug(3, "-_-_- Actual command used: %s\n", tmp2);
- if (ast)
- ast_channel_free(ast);
- return 0;
-}
-
-/*! \brief Create directory based on components */
-static int make_dir(char *dest, int len, const char *domain, const char *username, const char *folder)
-{
- return snprintf(dest, len, "%s%s/%s%s%s", MVM_SPOOL_DIR, domain, username, ast_strlen_zero(folder) ? "" : "/", folder ? folder : "");
-}
-
-/*! \brief Checks if directory exists. Does not create directory, but builds string in dest
- * \param dest String. base directory.
- * \param len Int. Length base directory string.
- * \param domain String. Ignored if is null or empty string.
- * \param username String. Ignored if is null or empty string.
- * \param folder String. Ignored if is null or empty string.
- * \return 0 on failure, 1 on success.
- */
-static int check_dirpath(char *dest, int len, char *domain, char *username, char *folder)
-{
- struct stat filestat;
- make_dir(dest, len, domain, username, folder ? folder : "");
- if (stat(dest, &filestat)== -1)
- return FALSE;
- else
- return TRUE;
-}
-
-/*! \brief basically mkdir -p $dest/$domain/$username/$folder
- * \param dest String. base directory.
- * \param len Length of directory string
- * \param domain String. Ignored if is null or empty string.
- * \param folder String. Ignored if is null or empty string.
- * \param username String. Ignored if is null or empty string.
- * \return -1 on failure, 0 on success.
- */
-static int create_dirpath(char *dest, int len, char *domain, char *username, char *folder)
-{
- int res;
- make_dir(dest, len, domain, username, folder);
- if ((res = ast_mkdir(dest, 0777))) {
- ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
- return -1;
- }
- ast_debug(2, "Creating directory for %s@%s folder %s : %s\n", username, domain, folder, dest);
- return 0;
-}
-
-
-/*! \brief Play intro message before recording voicemail
-*/
-static int invent_message(struct ast_channel *chan, char *domain, char *username, int busy, char *ecodes)
-{
- int res;
- char fn[PATH_MAX];
-
- ast_debug(2, "-_-_- Still preparing to play message ...\n");
-
- snprintf(fn, sizeof(fn), "%s%s/%s/greet", MVM_SPOOL_DIR, domain, username);
-
- if (ast_fileexists(fn, NULL, NULL) > 0) {
- res = ast_streamfile(chan, fn, chan->language);
- if (res)
- return -1;
- res = ast_waitstream(chan, ecodes);
- if (res)
- return res;
- } else {
- int numericusername = 1;
- char *i = username;
-
- ast_debug(2, "-_-_- No personal prompts. Using default prompt set for language\n");
-
- while (*i) {
- ast_debug(2, "-_-_- Numeric? Checking %c\n", *i);
- if (!isdigit(*i)) {
- numericusername = FALSE;
- break;
- }
- i++;
- }
-
- if (numericusername) {
- if(ast_streamfile(chan, "vm-theperson", chan->language))
- return -1;
- if ((res = ast_waitstream(chan, ecodes)))
- return res;
-
- res = ast_say_digit_str(chan, username, ecodes, chan->language);
- if (res)
- return res;
- } else {
- if(ast_streamfile(chan, "vm-theextensionis", chan->language))
- return -1;
- if ((res = ast_waitstream(chan, ecodes)))
- return res;
- }
- }
-
- res = ast_streamfile(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language);
- if (res)
- return -1;
- res = ast_waitstream(chan, ecodes);
- return res;
-}
-
-/*! \brief Delete media files and attribute file */
-static int vm_delete(char *file)
-{
- int res;
-
- ast_debug(1, "-_-_- Deleting voicemail file %s\n", file);
-
- res = unlink(file); /* Remove the meta data file */
- res |= ast_filedelete(file, NULL); /* remove the media file */
- return res;
-}
-
-
-/*! \brief Record voicemail message & let caller review or re-record it, or set options if applicable */
-static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
- int outsidecaller, struct minivm_account *vmu, int *duration, const char *unlockdir,
- signed char record_gain)
-{
- int cmd = 0;
- int max_attempts = 3;
- int attempts = 0;
- int recorded = 0;
- int message_exists = 0;
- signed char zero_gain = 0;
- char *acceptdtmf = "#";
- char *canceldtmf = "";
-
- /* Note that urgent and private are for flagging messages as such in the future */
-
- /* barf if no pointer passed to store duration in */
- if (duration == NULL) {
- ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
- return -1;
- }
-
- cmd = '3'; /* Want to start by recording */
-
- while ((cmd >= 0) && (cmd != 't')) {
- switch (cmd) {
- case '2':
- /* Review */
- ast_verb(3, "Reviewing the message\n");
- ast_streamfile(chan, recordfile, chan->language);
- cmd = ast_waitstream(chan, AST_DIGIT_ANY);
- break;
- case '3':
- message_exists = 0;
- /* Record */
- if (option_verbose > 2) {
- if (recorded == 1)
- ast_verb(3, "Re-recording the message\n");
- else
- ast_verb(3, "Recording the message\n");
- }
- if (recorded && outsidecaller)
- cmd = ast_play_and_wait(chan, "beep");
- recorded = 1;
- /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
- if (record_gain)
- ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
- if (ast_test_flag(vmu, MVM_OPERATOR))
- canceldtmf = "0";
- cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf);
- if (record_gain)
- ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
- if (cmd == -1) /* User has hung up, no options to give */
- return cmd;
- if (cmd == '0')
- break;
- else if (cmd == '*')
- break;
- else {
- /* If all is well, a message exists */
- message_exists = 1;
- cmd = 0;
- }
- break;
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '*':
- case '#':
- cmd = ast_play_and_wait(chan, "vm-sorry");
- break;
- case '0':
- if(!ast_test_flag(vmu, MVM_OPERATOR)) {
- cmd = ast_play_and_wait(chan, "vm-sorry");
- break;
- }
- if (message_exists || recorded) {
- cmd = ast_play_and_wait(chan, "vm-saveoper");
- if (!cmd)
- cmd = ast_waitfordigit(chan, 3000);
- if (cmd == '1') {
- ast_play_and_wait(chan, "vm-msgsaved");
- cmd = '0';
- } else {
- ast_play_and_wait(chan, "vm-deleted");
- vm_delete(recordfile);
- cmd = '0';
- }
- }
- return cmd;
- default:
- /* If the caller is an ouside caller, and the review option is enabled,
- allow them to review the message, but let the owner of the box review
- their OGM's */
- if (outsidecaller && !ast_test_flag(vmu, MVM_REVIEW))
- return cmd;
- if (message_exists) {
- cmd = ast_play_and_wait(chan, "vm-review");
- } else {
- cmd = ast_play_and_wait(chan, "vm-torerecord");
- if (!cmd)
- cmd = ast_waitfordigit(chan, 600);
- }
-
- if (!cmd && outsidecaller && ast_test_flag(vmu, MVM_OPERATOR)) {
- cmd = ast_play_and_wait(chan, "vm-reachoper");
- if (!cmd)
- cmd = ast_waitfordigit(chan, 600);
- }
- if (!cmd)
- cmd = ast_waitfordigit(chan, 6000);
- if (!cmd) {
- attempts++;
- }
- if (attempts > max_attempts) {
- cmd = 't';
- }
- }
- }
- if (outsidecaller)
- ast_play_and_wait(chan, "vm-goodbye");
- if (cmd == 't')
- cmd = 0;
- return cmd;
-}
-
-/*! \brief Run external notification for voicemail message */
-static void run_externnotify(struct ast_channel *chan, struct minivm_account *vmu)
-{
- char arguments[BUFSIZ];
-
- if (ast_strlen_zero(vmu->externnotify) && ast_strlen_zero(global_externnotify))
- return;
-
- snprintf(arguments, sizeof(arguments), "%s %s@%s %s %s&",
- ast_strlen_zero(vmu->externnotify) ? global_externnotify : vmu->externnotify,
- vmu->username, vmu->domain,
- chan->cid.cid_name, chan->cid.cid_num);
-
- ast_debug(1, "Executing: %s\n", arguments);
- ast_safe_system(arguments);
-}
-
-/*! \brief Send message to voicemail account owner */
-static int notify_new_message(struct ast_channel *chan, const char *templatename, struct minivm_account *vmu, const char *filename, long duration, const char *format, char *cidnum, char *cidname)
-{
- char *stringp;
- struct minivm_template *etemplate;
- char *messageformat;
- int res = 0;
- char oldlocale[100];
- const char *counter;
-
- if (!ast_strlen_zero(vmu->attachfmt)) {
- if (strstr(format, vmu->attachfmt)) {
- format = vmu->attachfmt;
- } else
- ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, format, vmu->username, vmu->domain);
- }
-
- etemplate = message_template_find(vmu->etemplate);
- if (!etemplate)
- etemplate = message_template_find(templatename);
- if (!etemplate)
- etemplate = message_template_find("email-default");
-
- /* Attach only the first format */
- stringp = messageformat = ast_strdupa(format);
- strsep(&stringp, "|");
-
- if (!ast_strlen_zero(etemplate->locale)) {
- char *newlocale;
- ast_copy_string(oldlocale, setlocale(LC_TIME, NULL), sizeof(oldlocale));
- ast_debug(2, "-_-_- Changing locale from %s to %s\n", oldlocale, etemplate->locale);
- newlocale = setlocale(LC_TIME, etemplate->locale);
- if (newlocale == NULL) {
- ast_log(LOG_WARNING, "-_-_- Changing to new locale did not work. Locale: %s\n", etemplate->locale);
- }
- }
-
-
-
- /* Read counter if available */
- counter = pbx_builtin_getvar_helper(chan, "MVM_COUNTER");
- if (ast_strlen_zero(counter)) {
- ast_debug(2, "-_-_- MVM_COUNTER not found\n");
- } else {
- ast_debug(2, "-_-_- MVM_COUNTER found - will use it with value %s\n", counter);
- }
-
- res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_EMAIL, counter);
-
- if (res == 0 && !ast_strlen_zero(vmu->pager)) {
- /* Find template for paging */
- etemplate = message_template_find(vmu->ptemplate);
- if (!etemplate)
- etemplate = message_template_find("pager-default");
- if (etemplate->locale) {
- ast_copy_string(oldlocale, setlocale(LC_TIME, ""), sizeof(oldlocale));
- setlocale(LC_TIME, etemplate->locale);
- }
-
- res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_PAGE, counter);
- }
-
- manager_event(EVENT_FLAG_CALL, "MiniVoiceMail", "Action: SentNotification\rn\nMailbox: %s@%s\r\nCounter: %s\r\n", vmu->username, vmu->domain, counter);
-
- run_externnotify(chan, vmu); /* Run external notification */
-
- if (etemplate->locale)
- setlocale(LC_TIME, oldlocale); /* Rest to old locale */
- return res;
-}
-
-
-/*! \brief Record voicemail message, store into file prepared for sending e-mail */
-static int leave_voicemail(struct ast_channel *chan, char *username, struct leave_vm_options *options)
-{
- char tmptxtfile[PATH_MAX];
- char callerid[256];
- FILE *txt;
- int res = 0, txtdes;
- int msgnum;
- int duration = 0;
- char date[256];
- char tmpdir[PATH_MAX];
- char ext_context[256] = "";
- char fmt[80];
- char *domain;
- char tmp[256] = "";
- struct minivm_account *vmu;
- int userdir;
-
- ast_copy_string(tmp, username, sizeof(tmp));
- username = tmp;
- domain = strchr(tmp, '@');
- if (domain) {
- *domain = '\0';
- domain++;
- }
-
- if (!(vmu = find_account(domain, username, TRUE))) {
- /* We could not find user, let's exit */
- ast_log(LOG_ERROR, "Can't allocate temporary account for '%s@%s'\n", username, domain);
- pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
- return 0;
- }
-
- /* Setup pre-file if appropriate */
- if (strcmp(vmu->domain, "localhost"))
- snprintf(ext_context, sizeof(ext_context), "%s@%s", username, vmu->domain);
- else
- ast_copy_string(ext_context, vmu->domain, sizeof(ext_context));
-
- /* The meat of recording the message... All the announcements and beeps have been played*/
- if (ast_strlen_zero(vmu->attachfmt))
- ast_copy_string(fmt, default_vmformat, sizeof(fmt));
- else
- ast_copy_string(fmt, vmu->attachfmt, sizeof(fmt));
-
- if (ast_strlen_zero(fmt)) {
- ast_log(LOG_WARNING, "No format for saving voicemail? Default %s\n", default_vmformat);
- pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
- return res;
- }
- msgnum = 0;
-
- userdir = check_dirpath(tmpdir, sizeof(tmpdir), vmu->domain, username, "tmp");
-
- /* If we have no user directory, use generic temporary directory */
- if (!userdir) {
- create_dirpath(tmpdir, sizeof(tmpdir), "0000_minivm_temp", "mediafiles", "");
- ast_debug(3, "Creating temporary directory %s\n", tmpdir);
- }
-
-
- snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
-
-
- /* XXX This file needs to be in temp directory */
- txtdes = mkstemp(tmptxtfile);
- if (txtdes < 0) {
- ast_log(LOG_ERROR, "Unable to create message file %s: %s\n", tmptxtfile, strerror(errno));
- res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
- pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
- return res;
- }
-
- if (res >= 0) {
- /* Unless we're *really* silent, try to send the beep */
- res = ast_streamfile(chan, "beep", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
- }
-
- /* OEJ XXX Maybe this can be turned into a log file? Hmm. */
- /* Store information */
- ast_debug(2, "Open file for metadata: %s\n", tmptxtfile);
-
- res = play_record_review(chan, NULL, tmptxtfile, global_vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
-
- txt = fdopen(txtdes, "w+");
- if (!txt) {
- ast_log(LOG_WARNING, "Error opening text file for output\n");
- } else {
- struct ast_tm tm;
- struct timeval now = ast_tvnow();
- char timebuf[30];
- char logbuf[BUFSIZ];
- get_date(date, sizeof(date));
- ast_localtime(&now, &tm, NULL);
- ast_strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm);
-
- snprintf(logbuf, sizeof(logbuf),
- /* "Mailbox:domain:macrocontext:exten:priority:callerchan:callerid:origdate:origtime:duration:durationstatus:accountcode" */
- "%s:%s:%s:%s:%d:%s:%s:%s:%s:%d:%s:%s\n",
- username,
- chan->context,
- chan->macrocontext,
- chan->exten,
- chan->priority,
- chan->name,
- ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
- date,
- timebuf,
- duration,
- duration < global_vmminmessage ? "IGNORED" : "OK",
- vmu->accountcode
- );
- fprintf(txt, logbuf);
- if (minivmlogfile) {
- ast_mutex_lock(&minivmloglock);
- fprintf(minivmlogfile, logbuf);
- ast_mutex_unlock(&minivmloglock);
- }
-
- if (duration < global_vmminmessage) {
- ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, global_vmminmessage);
- fclose(txt);
- ast_filedelete(tmptxtfile, NULL);
- unlink(tmptxtfile);
- pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
- return 0;
- }
- fclose(txt); /* Close log file */
- if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
- ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
- unlink(tmptxtfile);
- pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
- if(ast_test_flag(vmu, MVM_ALLOCED))
- free_user(vmu);
- return 0;
- }
-
- /* Set channel variables for the notify application */
- pbx_builtin_setvar_helper(chan, "MVM_FILENAME", tmptxtfile);
- snprintf(timebuf, sizeof(timebuf), "%d", duration);
- pbx_builtin_setvar_helper(chan, "MVM_DURATION", timebuf);
- pbx_builtin_setvar_helper(chan, "MVM_FORMAT", fmt);
-
- }
- global_stats.lastreceived = ast_tvnow();
- global_stats.receivedmessages++;
-// /* Go ahead and delete audio files from system, they're not needed any more */
-// if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
-// ast_filedelete(tmptxtfile, NULL);
-// /* Even not being used at the moment, it's better to convert ast_log to ast_debug anyway */
-// ast_debug(2, "-_-_- Deleted audio file after notification :: %s \n", tmptxtfile);
-// }
-
- if (res > 0)
- res = 0;
-
- if(ast_test_flag(vmu, MVM_ALLOCED))
- free_user(vmu);
-
- pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "SUCCESS");
- return res;
-}
-
-/*! \brief Notify voicemail account owners - either generic template or user specific */
-static int minivm_notify_exec(struct ast_channel *chan, void *data)
-{
- int argc;
- char *argv[2];
- int res = 0;
- char tmp[PATH_MAX];
- char *domain;
- char *tmpptr;
- struct minivm_account *vmu;
- char *username = argv[0];
- const char *template = "";
- const char *filename;
- const char *format;
- const char *duration_string;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
- return -1;
- }
- tmpptr = ast_strdupa((char *)data);
- if (!tmpptr) {
- ast_log(LOG_ERROR, "Out of memory\n");
- return -1;
- }
- argc = ast_app_separate_args(tmpptr, ',', argv, sizeof(argv) / sizeof(argv[0]));
-
- if (argc == 2 && !ast_strlen_zero(argv[1]))
- template = argv[1];
-
- ast_copy_string(tmp, argv[0], sizeof(tmp));
- username = tmp;
- domain = strchr(tmp, '@');
- if (domain) {
- *domain = '\0';
- domain++;
- }
- if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
- ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
- return -1;
- }
-
- if(!(vmu = find_account(domain, username, TRUE))) {
- /* We could not find user, let's exit */
- ast_log(LOG_WARNING, "Could not allocate temporary memory for '%s@%s'\n", username, domain);
- pbx_builtin_setvar_helper(chan, "MINIVM_NOTIFY_STATUS", "FAILED");
- return -1;
- }
-
- filename = pbx_builtin_getvar_helper(chan, "MVM_FILENAME");
- format = pbx_builtin_getvar_helper(chan, "MVM_FORMAT");
- duration_string = pbx_builtin_getvar_helper(chan, "MVM_DURATION");
- /* Notify of new message to e-mail and pager */
- if (!ast_strlen_zero(filename)) {
- res = notify_new_message(chan, template, vmu, filename, atoi(duration_string), format, chan->cid.cid_num, chan->cid.cid_name);
- };
-
- pbx_builtin_setvar_helper(chan, "MINIVM_NOTIFY_STATUS", res == 0 ? "SUCCESS" : "FAILED");
-
-
- if(ast_test_flag(vmu, MVM_ALLOCED))
- free_user(vmu);
-
- /* Ok, we're ready to rock and roll. Return to dialplan */
-
- return res;
-
-}
-
-/*! \brief Dialplan function to record voicemail */
-static int minivm_record_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char *tmp;
- struct leave_vm_options leave_options;
- int argc;
- char *argv[2];
- struct ast_flags flags = { 0 };
- char *opts[OPT_ARG_ARRAY_SIZE];
-
- memset(&leave_options, 0, sizeof(leave_options));
-
- /* Answer channel if it's not already answered */
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
- return -1;
- }
- tmp = ast_strdupa((char *)data);
- if (!tmp) {
- ast_log(LOG_ERROR, "Out of memory\n");
- return -1;
- }
- argc = ast_app_separate_args(tmp, ',', argv, sizeof(argv) / sizeof(argv[0]));
- if (argc == 2) {
- if (ast_app_parse_options(minivm_app_options, &flags, opts, argv[1])) {
- return -1;
- }
- ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
- if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
- int gain;
-
- if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
- ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
- return -1;
- } else
- leave_options.record_gain = (signed char) gain;
- }
- }
-
- /* Now run the appliation and good luck to you! */
- res = leave_voicemail(chan, argv[0], &leave_options);
-
- if (res == ERROR_LOCK_PATH) {
- ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
- pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
- res = 0;
- }
- pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "SUCCESS");
-
- return res;
-}
-
-/*! \brief Play voicemail prompts - either generic or user specific */
-static int minivm_greet_exec(struct ast_channel *chan, void *data)
-{
- struct leave_vm_options leave_options = { 0, '\0'};
- int argc;
- char *argv[2];
- struct ast_flags flags = { 0 };
- char *opts[OPT_ARG_ARRAY_SIZE];
- int res = 0;
- int ausemacro = 0;
- int ousemacro = 0;
- int ouseexten = 0;
- char tmp[PATH_MAX];
- char dest[PATH_MAX];
- char prefile[PATH_MAX];
- char tempfile[PATH_MAX] = "";
- char ext_context[256] = "";
- char *domain;
- char ecodes[16] = "#";
- char *tmpptr;
- struct minivm_account *vmu;
- char *username = argv[0];
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
- return -1;
- }
- tmpptr = ast_strdupa((char *)data);
- if (!tmpptr) {
- ast_log(LOG_ERROR, "Out of memory\n");
- return -1;
- }
- argc = ast_app_separate_args(tmpptr, ',', argv, sizeof(argv) / sizeof(argv[0]));
-
- if (argc == 2) {
- if (ast_app_parse_options(minivm_app_options, &flags, opts, argv[1]))
- return -1;
- ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
- }
-
- ast_copy_string(tmp, argv[0], sizeof(tmp));
- username = tmp;
- domain = strchr(tmp, '@');
- if (domain) {
- *domain = '\0';
- domain++;
- }
- if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
- ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument: %s\n", argv[0]);
- return -1;
- }
- ast_debug(1, "-_-_- Trying to find configuration for user %s in domain %s\n", username, domain);
-
- if (!(vmu = find_account(domain, username, TRUE))) {
- ast_log(LOG_ERROR, "Could not allocate memory. \n");
- return -1;
- }
-
- /* Answer channel if it's not already answered */
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- /* Setup pre-file if appropriate */
- if (strcmp(vmu->domain, "localhost"))
- snprintf(ext_context, sizeof(ext_context), "%s@%s", username, vmu->domain);
- else
- ast_copy_string(ext_context, vmu->domain, sizeof(ext_context));
-
- if (ast_test_flag(&leave_options, OPT_BUSY_GREETING)) {
- res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "busy");
- if (res)
- snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", MVM_SPOOL_DIR, vmu->domain, username);
- } else if (ast_test_flag(&leave_options, OPT_UNAVAIL_GREETING)) {
- res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "unavail");
- if (res)
- snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", MVM_SPOOL_DIR, vmu->domain, username);
- }
- /* Check for temporary greeting - it overrides busy and unavail */
- snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", MVM_SPOOL_DIR, vmu->domain, username);
- if (!(res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "temp"))) {
- ast_debug(2, "Temporary message directory does not exist, using default (%s)\n", tempfile);
- ast_copy_string(prefile, tempfile, sizeof(prefile));
- }
- ast_debug(2, "-_-_- Preparing to play message ...\n");
-
- /* Check current or macro-calling context for special extensions */
- if (ast_test_flag(vmu, MVM_OPERATOR)) {
- if (!ast_strlen_zero(vmu->exit)) {
- if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
- strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
- ouseexten = 1;
- }
- } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
- strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
- ouseexten = 1;
- }
- else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
- strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
- ousemacro = 1;
- }
- }
-
- if (!ast_strlen_zero(vmu->exit)) {
- if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
- strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
- } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
- strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
- else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
- strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
- ausemacro = 1;
- }
-
- res = 0; /* Reset */
- /* Play the beginning intro if desired */
- if (!ast_strlen_zero(prefile)) {
- if (ast_streamfile(chan, prefile, chan->language) > -1)
- res = ast_waitstream(chan, ecodes);
- } else {
- ast_debug(2, "%s doesn't exist, doing what we can\n", prefile);
- res = invent_message(chan, vmu->domain, username, ast_test_flag(&leave_options, OPT_BUSY_GREETING), ecodes);
- }
- if (res < 0) {
- ast_debug(2, "Hang up during prefile playback\n");
- pbx_builtin_setvar_helper(chan, "MINIVM_GREET_STATUS", "FAILED");
- if(ast_test_flag(vmu, MVM_ALLOCED))
- free_user(vmu);
- return -1;
- }
- if (res == '#') {
- /* On a '#' we skip the instructions */
- ast_set_flag(&leave_options, OPT_SILENT);
- res = 0;
- }
- if (!res && !ast_test_flag(&leave_options, OPT_SILENT)) {
- res = ast_streamfile(chan, SOUND_INTRO, chan->language);
- if (!res)
- res = ast_waitstream(chan, ecodes);
- if (res == '#') {
- ast_set_flag(&leave_options, OPT_SILENT);
- res = 0;
- }
- }
- if (res > 0)
- ast_stopstream(chan);
- /* Check for a '*' here in case the caller wants to escape from voicemail to something
- other than the operator -- an automated attendant or mailbox login for example */
- if (res == '*') {
- chan->exten[0] = 'a';
- chan->exten[1] = '\0';
- if (!ast_strlen_zero(vmu->exit)) {
- ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
- } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
- ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
- }
- chan->priority = 0;
- pbx_builtin_setvar_helper(chan, "MINIVM_GREET_STATUS", "USEREXIT");
- res = 0;
- } else if (res == '0') { /* Check for a '0' here */
- if(ouseexten || ousemacro) {
- chan->exten[0] = 'o';
- chan->exten[1] = '\0';
- if (!ast_strlen_zero(vmu->exit)) {
- ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
- } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
- ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
- }
- ast_play_and_wait(chan, "transfer");
- chan->priority = 0;
- pbx_builtin_setvar_helper(chan, "MINIVM_GREET_STATUS", "USEREXIT");
- }
- res = 0;
- } else if (res < 0) {
- pbx_builtin_setvar_helper(chan, "MINIVM_GREET_STATUS", "FAILED");
- res = -1;
- } else
- pbx_builtin_setvar_helper(chan, "MINIVM_GREET_STATUS", "SUCCESS");
-
- if(ast_test_flag(vmu, MVM_ALLOCED))
- free_user(vmu);
-
-
- /* Ok, we're ready to rock and roll. Return to dialplan */
- return res;
-
-}
-
-/*! \brief Dialplan application to delete voicemail */
-static int minivm_delete_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char filename[BUFSIZ];
-
- if (!ast_strlen_zero(data))
- ast_copy_string(filename, (char *) data, sizeof(filename));
- else
- ast_copy_string(filename, pbx_builtin_getvar_helper(chan, "MVM_FILENAME"), sizeof(filename));
-
- if (ast_strlen_zero(filename)) {
- ast_log(LOG_ERROR, "No filename given in application arguments or channel variable MVM_FILENAME\n");
- return res;
- }
-
- /* Go ahead and delete audio files from system, they're not needed any more */
- /* We should look for both audio and text files here */
- if (ast_fileexists(filename, NULL, NULL) > 0) {
- res = vm_delete(filename);
- if (res) {
- ast_debug(2, "-_-_- Can't delete file: %s\n", filename);
- pbx_builtin_setvar_helper(chan, "MINIVM_DELETE_STATUS", "FAILED");
- } else {
- ast_debug(2, "-_-_- Deleted voicemail file :: %s \n", filename);
- pbx_builtin_setvar_helper(chan, "MINIVM_DELETE_STATUS", "SUCCESS");
- }
- } else {
- ast_debug(2, "-_-_- Filename does not exist: %s\n", filename);
- pbx_builtin_setvar_helper(chan, "MINIVM_DELETE_STATUS", "FAILED");
- }
-
- return res;
-}
-
-/*! \brief Record specific messages for voicemail account */
-static int minivm_accmess_exec(struct ast_channel *chan, void *data)
-{
- int argc = 0;
- char *argv[2];
- int res = 0;
- char filename[PATH_MAX];
- char tmp[PATH_MAX];
- char *domain;
- char *tmpptr = NULL;
- struct minivm_account *vmu;
- char *username = argv[0];
- struct ast_flags flags = { 0 };
- char *opts[OPT_ARG_ARRAY_SIZE];
- int error = FALSE;
- char *message = NULL;
- char *prompt = NULL;
- int duration;
- int cmd;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "MinivmAccmess needs at least two arguments: account and option\n");
- error = TRUE;
- } else
- tmpptr = ast_strdupa((char *)data);
- if (!error) {
- if (!tmpptr) {
- ast_log(LOG_ERROR, "Out of memory\n");
- error = TRUE;
- } else
- argc = ast_app_separate_args(tmpptr, ',', argv, sizeof(argv) / sizeof(argv[0]));
- }
-
- if (argc <=1) {
- ast_log(LOG_ERROR, "MinivmAccmess needs at least two arguments: account and option\n");
- error = TRUE;
- }
- if (!error && strlen(argv[1]) > 1) {
- ast_log(LOG_ERROR, "MinivmAccmess can only handle one option at a time. Bad option string: %s\n", argv[1]);
- error = TRUE;
- }
-
- if (!error && ast_app_parse_options(minivm_accmess_options, &flags, opts, argv[1])) {
- ast_log(LOG_ERROR, "Can't parse option %s\n", argv[1]);
- error = TRUE;
- }
-
- if (error)
- return -1;
-
- ast_copy_string(tmp, argv[0], sizeof(tmp));
- username = tmp;
- domain = strchr(tmp, '@');
- if (domain) {
- *domain = '\0';
- domain++;
- }
- if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
- ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
- return -1;
- }
-
- if(!(vmu = find_account(domain, username, TRUE))) {
- /* We could not find user, let's exit */
- ast_log(LOG_WARNING, "Could not allocate temporary memory for '%s@%s'\n", username, domain);
- pbx_builtin_setvar_helper(chan, "MINIVM_NOTIFY_STATUS", "FAILED");
- return -1;
- }
-
- /* Answer channel if it's not already answered */
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- /* Here's where the action is */
- if (ast_test_flag(&flags, OPT_BUSY_GREETING)) {
- message = "busy";
- prompt = "vm-rec-busy";
- } else if (ast_test_flag(&flags, OPT_UNAVAIL_GREETING)) {
- message = "unavailable";
- prompt = "vm-rec-unavail";
- } else if (ast_test_flag(&flags, OPT_TEMP_GREETING)) {
- message = "temp";
- prompt = "vm-temp-greeting";
- } else if (ast_test_flag(&flags, OPT_NAME_GREETING)) {
- message = "greet";
- prompt = "vm-rec-name";
- }
- snprintf(filename,sizeof(filename), "%s%s/%s/%s", MVM_SPOOL_DIR, vmu->domain, vmu->username, message);
- /* Maybe we should check the result of play_record_review ? */
- cmd = play_record_review(chan, prompt, filename, global_maxgreet, default_vmformat, 0, vmu, &duration, NULL, FALSE);
-
- ast_debug(1, "Recorded new %s message in %s (duration %d)\n", message, filename, duration);
-
- if(ast_test_flag(vmu, MVM_ALLOCED))
- free_user(vmu);
-
-
- /* Ok, we're ready to rock and roll. Return to dialplan */
- return res;
-
-}
-
-/*! \brief Append new mailbox to mailbox list from configuration file */
-static int create_vmaccount(char *name, struct ast_variable *var, int realtime)
-{
- struct minivm_account *vmu;
- char *domain;
- char *username;
- char accbuf[BUFSIZ];
-
- ast_debug(3, "Creating %s account for [%s]\n", realtime ? "realtime" : "static", name);
-
- ast_copy_string(accbuf, name, sizeof(accbuf));
- username = accbuf;
- domain = strchr(accbuf, '@');
- if (domain) {
- *domain = '\0';
- domain++;
- }
- if (ast_strlen_zero(domain)) {
- ast_log(LOG_ERROR, "No domain given for mini-voicemail account %s. Not configured.\n", name);
- return 0;
- }
-
- ast_debug(3, "Creating static account for user %s domain %s\n", username, domain);
-
- /* Allocate user account */
- vmu = ast_calloc(1, sizeof(*vmu));
- if (!vmu)
- return 0;
-
- ast_copy_string(vmu->domain, domain, sizeof(vmu->domain));
- ast_copy_string(vmu->username, username, sizeof(vmu->username));
-
- populate_defaults(vmu);
-
- ast_debug(3, "...Configuring account %s\n", name);
-
- while (var) {
- ast_debug(3, "---- Configuring %s = \"%s\" for account %s\n", var->name, var->value, name);
- if (!strcasecmp(var->name, "serveremail")) {
- ast_copy_string(vmu->serveremail, var->value, sizeof(vmu->serveremail));
- } else if (!strcasecmp(var->name, "email")) {
- ast_copy_string(vmu->email, var->value, sizeof(vmu->email));
- } else if (!strcasecmp(var->name, "accountcode")) {
- ast_copy_string(vmu->accountcode, var->value, sizeof(vmu->accountcode));
- } else if (!strcasecmp(var->name, "pincode")) {
- ast_copy_string(vmu->pincode, var->value, sizeof(vmu->pincode));
- } else if (!strcasecmp(var->name, "domain")) {
- ast_copy_string(vmu->domain, var->value, sizeof(vmu->domain));
- } else if (!strcasecmp(var->name, "language")) {
- ast_copy_string(vmu->language, var->value, sizeof(vmu->language));
- } else if (!strcasecmp(var->name, "timezone")) {
- ast_copy_string(vmu->zonetag, var->value, sizeof(vmu->zonetag));
- } else if (!strcasecmp(var->name, "externnotify")) {
- ast_copy_string(vmu->externnotify, var->value, sizeof(vmu->externnotify));
- } else if (!strcasecmp(var->name, "etemplate")) {
- ast_copy_string(vmu->etemplate, var->value, sizeof(vmu->etemplate));
- } else if (!strcasecmp(var->name, "ptemplate")) {
- ast_copy_string(vmu->ptemplate, var->value, sizeof(vmu->ptemplate));
- } else if (!strcasecmp(var->name, "fullname")) {
- ast_copy_string(vmu->fullname, var->value, sizeof(vmu->fullname));
- } else if (!strcasecmp(var->name, "setvar")) {
- char *varval;
- char *varname = ast_strdupa(var->value);
- struct ast_variable *tmpvar;
-
- if (varname && (varval = strchr(varname, '='))) {
- *varval = '\0';
- varval++;
- if ((tmpvar = ast_variable_new(varname, varval, ""))) {
- tmpvar->next = vmu->chanvars;
- vmu->chanvars = tmpvar;
- }
- }
- } else if (!strcasecmp(var->name, "pager")) {
- ast_copy_string(vmu->pager, var->value, sizeof(vmu->pager));
- } else if (!strcasecmp(var->name, "volgain")) {
- sscanf(var->value, "%lf", &vmu->volgain);
- } else {
- ast_log(LOG_ERROR, "Unknown configuration option for minivm account %s : %s\n", name, var->name);
- }
- var = var->next;
- }
- ast_debug(3, "...Linking account %s\n", name);
-
- AST_LIST_LOCK(&minivm_accounts);
- AST_LIST_INSERT_TAIL(&minivm_accounts, vmu, list);
- AST_LIST_UNLOCK(&minivm_accounts);
-
- global_stats.voicemailaccounts++;
-
- ast_debug(2, "MINIVM :: Created account %s@%s - tz %s etemplate %s %s\n", username, domain, ast_strlen_zero(vmu->zonetag) ? "" : vmu->zonetag, ast_strlen_zero(vmu->etemplate) ? "" : vmu->etemplate, realtime ? "(realtime)" : "");
- return 0;
-}
-
-/*! \brief Free Mini Voicemail timezone */
-static void free_zone(struct minivm_zone *z)
-{
- ast_free(z);
-}
-
-/*! \brief Clear list of timezones */
-static void timezone_destroy_list(void)
-{
- struct minivm_zone *this;
-
- AST_LIST_LOCK(&minivm_zones);
- while ((this = AST_LIST_REMOVE_HEAD(&minivm_zones, list)))
- free_zone(this);
-
- AST_LIST_UNLOCK(&minivm_zones);
-}
-
-/*! \brief Add time zone to memory list */
-static int timezone_add(const char *zonename, const char *config)
-{
-
- struct minivm_zone *newzone;
- char *msg_format, *timezone;
-
- newzone = ast_calloc(1, sizeof(*newzone));
- if (newzone == NULL)
- return 0;
-
- msg_format = ast_strdupa(config);
- if (msg_format == NULL) {
- ast_log(LOG_WARNING, "Out of memory.\n");
- ast_free(newzone);
- return 0;
- }
-
- timezone = strsep(&msg_format, "|");
- if (!msg_format) {
- ast_log(LOG_WARNING, "Invalid timezone definition : %s\n", zonename);
- ast_free(newzone);
- return 0;
- }
-
- ast_copy_string(newzone->name, zonename, sizeof(newzone->name));
- ast_copy_string(newzone->timezone, timezone, sizeof(newzone->timezone));
- ast_copy_string(newzone->msg_format, msg_format, sizeof(newzone->msg_format));
-
- AST_LIST_LOCK(&minivm_zones);
- AST_LIST_INSERT_TAIL(&minivm_zones, newzone, list);
- AST_LIST_UNLOCK(&minivm_zones);
-
- global_stats.timezones++;
-
- return 0;
-}
-
-/*! \brief Read message template from file */
-static char *message_template_parse_filebody(const char *filename) {
- char buf[BUFSIZ * 6];
- char readbuf[BUFSIZ];
- char filenamebuf[BUFSIZ];
- char *writepos;
- char *messagebody;
- FILE *fi;
- int lines = 0;
-
- if (ast_strlen_zero(filename))
- return NULL;
- if (*filename == '/')
- ast_copy_string(filenamebuf, filename, sizeof(filenamebuf));
- else
- snprintf(filenamebuf, sizeof(filenamebuf), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
-
- if (!(fi = fopen(filenamebuf, "r"))) {
- ast_log(LOG_ERROR, "Can't read message template from file: %s\n", filenamebuf);
- return NULL;
- }
- writepos = buf;
- while (fgets(readbuf, sizeof(readbuf), fi)) {
- lines ++;
- if (writepos != buf) {
- *writepos = '\n'; /* Replace EOL with new line */
- writepos++;
- }
- ast_copy_string(writepos, readbuf, sizeof(buf) - (writepos - buf));
- writepos += strlen(readbuf) - 1;
- }
- fclose(fi);
- messagebody = ast_calloc(1, strlen(buf + 1));
- ast_copy_string(messagebody, buf, strlen(buf) + 1);
- ast_debug(4, "---> Size of allocation %d\n", (int) strlen(buf + 1) );
- ast_debug(4, "---> Done reading message template : \n%s\n---- END message template--- \n", messagebody);
-
- return messagebody;
-}
-
-/*! \brief Parse emailbody template from configuration file */
-static char *message_template_parse_emailbody(const char *configuration)
-{
- char *tmpread, *tmpwrite;
- char *emailbody = ast_strdup(configuration);
-
- /* substitute strings \t and \n into the apropriate characters */
- tmpread = tmpwrite = emailbody;
- while ((tmpwrite = strchr(tmpread,'\\'))) {
- int len = strlen("\n");
- switch (tmpwrite[1]) {
- case 'n':
- memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
- strncpy(tmpwrite, "\n", len);
- break;
- case 't':
- memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
- strncpy(tmpwrite, "\t", len);
- break;
- default:
- ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
- }
- tmpread = tmpwrite + len;
- }
- return emailbody;
-}
-
-/*! \brief Apply general configuration options */
-static int apply_general_options(struct ast_variable *var)
-{
- int error = 0;
-
- while (var) {
- /* Mail command */
- if (!strcmp(var->name, "mailcmd")) {
- ast_copy_string(global_mailcmd, var->value, sizeof(global_mailcmd)); /* User setting */
- } else if (!strcmp(var->name, "maxgreet")) {
- global_maxgreet = atoi(var->value);
- } else if (!strcmp(var->name, "maxsilence")) {
- global_maxsilence = atoi(var->value);
- if (global_maxsilence > 0)
- global_maxsilence *= 1000;
- } else if (!strcmp(var->name, "logfile")) {
- if (!ast_strlen_zero(var->value) ) {
- if(*(var->value) == '/')
- ast_copy_string(global_logfile, var->value, sizeof(global_logfile));
- else
- snprintf(global_logfile, sizeof(global_logfile), "%s/%s", ast_config_AST_LOG_DIR, var->value);
- }
- } else if (!strcmp(var->name, "externnotify")) {
- /* External voicemail notify application */
- ast_copy_string(global_externnotify, var->value, sizeof(global_externnotify));
- } else if (!strcmp(var->name, "silencetreshold")) {
- /* Silence treshold */
- global_silencethreshold = atoi(var->value);
- } else if (!strcmp(var->name, "maxmessage")) {
- int x;
- if (sscanf(var->value, "%d", &x) == 1) {
- global_vmmaxmessage = x;
- } else {
- error ++;
- ast_log(LOG_WARNING, "Invalid max message time length\n");
- }
- } else if (!strcmp(var->name, "minmessage")) {
- int x;
- if (sscanf(var->value, "%d", &x) == 1) {
- global_vmminmessage = x;
- if (global_maxsilence <= global_vmminmessage)
- ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
- } else {
- error ++;
- ast_log(LOG_WARNING, "Invalid min message time length\n");
- }
- } else if (!strcmp(var->name, "format")) {
- ast_copy_string(default_vmformat, var->value, sizeof(default_vmformat));
- } else if (!strcmp(var->name, "review")) {
- ast_set2_flag((&globalflags), ast_true(var->value), MVM_REVIEW);
- } else if (!strcmp(var->name, "operator")) {
- ast_set2_flag((&globalflags), ast_true(var->value), MVM_OPERATOR);
- }
- var = var->next;
- }
- return error;
-}
-
-/*! \brief Load minivoicemail configuration */
-static int load_config(int reload)
-{
- struct ast_config *cfg;
- struct ast_variable *var;
- char *cat;
- const char *chanvar;
- int error = 0;
- struct minivm_template *template;
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
-
- cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
- if (cfg == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
-
- ast_mutex_lock(&minivmlock);
-
- /* Destroy lists to reconfigure */
- message_destroy_list(); /* Destroy list of voicemail message templates */
- timezone_destroy_list(); /* Destroy list of timezones */
- vmaccounts_destroy_list(); /* Destroy list of voicemail accounts */
- ast_debug(2, "Destroyed memory objects...\n");
-
- /* First, set some default settings */
- global_externnotify[0] = '\0';
- global_logfile[0] = '\0';
- global_silencethreshold = 256;
- global_vmmaxmessage = 2000;
- global_maxgreet = 2000;
- global_vmminmessage = 0;
- strcpy(global_mailcmd, SENDMAIL);
- global_maxsilence = 0;
- global_saydurationminfo = 2;
- ast_copy_string(default_vmformat, "wav", sizeof(default_vmformat));
- ast_set2_flag((&globalflags), FALSE, MVM_REVIEW);
- ast_set2_flag((&globalflags), FALSE, MVM_OPERATOR);
- strcpy(global_charset, "ISO-8859-1");
- /* Reset statistics */
- memset(&global_stats, 0, sizeof(global_stats));
- global_stats.reset = ast_tvnow();
-
- /* Make sure we could load configuration file */
- if (!cfg) {
- ast_log(LOG_WARNING, "Failed to load configuration file. Module activated with default settings.\n");
- ast_mutex_unlock(&minivmlock);
- return 0;
- }
-
- ast_debug(2, "-_-_- Loaded configuration file, now parsing\n");
-
- /* General settings */
-
- cat = ast_category_browse(cfg, NULL);
- while (cat) {
- ast_debug(3, "-_-_- Found configuration section [%s]\n", cat);
- if (!strcasecmp(cat, "general")) {
- /* Nothing right now */
- error += apply_general_options(ast_variable_browse(cfg, cat));
- } else if (!strncasecmp(cat, "template-", 9)) {
- /* Template */
- char *name = cat + 9;
-
- /* Now build and link template to list */
- error += message_template_build(name, ast_variable_browse(cfg, cat));
- } else {
- var = ast_variable_browse(cfg, cat);
- if (!strcasecmp(cat, "zonemessages")) {
- /* Timezones in this context */
- while (var) {
- timezone_add(var->name, var->value);
- var = var->next;
- }
- } else {
- /* Create mailbox from this */
- error += create_vmaccount(cat, var, FALSE);
- }
- }
- /* Find next section in configuration file */
- cat = ast_category_browse(cfg, cat);
- }
-
- /* Configure the default email template */
- message_template_build("email-default", NULL);
- template = message_template_find("email-default");
-
- /* Load date format config for voicemail mail */
- if ((chanvar = ast_variable_retrieve(cfg, "general", "emaildateformat")))
- ast_copy_string(template->dateformat, chanvar, sizeof(template->dateformat));
- if ((chanvar = ast_variable_retrieve(cfg, "general", "emailfromstring")))
- ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
- if ((chanvar = ast_variable_retrieve(cfg, "general", "emailaaddress")))
- ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
- if ((chanvar = ast_variable_retrieve(cfg, "general", "emailcharset")))
- ast_copy_string(template->charset, chanvar, sizeof(template->charset));
- if ((chanvar = ast_variable_retrieve(cfg, "general", "emailsubject")))
- ast_copy_string(template->subject, chanvar, sizeof(template->subject));
- if ((chanvar = ast_variable_retrieve(cfg, "general", "emailbody")))
- template->body = message_template_parse_emailbody(chanvar);
- template->attachment = TRUE;
-
- message_template_build("pager-default", NULL);
- template = message_template_find("pager-default");
- if ((chanvar = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
- ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
- if ((chanvar = ast_variable_retrieve(cfg, "general", "pageraddress")))
- ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
- if ((chanvar = ast_variable_retrieve(cfg, "general", "pagercharset")))
- ast_copy_string(template->charset, chanvar, sizeof(template->charset));
- if ((chanvar = ast_variable_retrieve(cfg, "general", "pagersubject")))
- ast_copy_string(template->subject, chanvar,sizeof(template->subject));
- if ((chanvar = ast_variable_retrieve(cfg, "general", "pagerbody")))
- template->body = message_template_parse_emailbody(chanvar);
- template->attachment = FALSE;
-
- if (error)
- ast_log(LOG_ERROR, "--- A total of %d errors found in mini-voicemail configuration\n", error);
-
- ast_mutex_unlock(&minivmlock);
- ast_config_destroy(cfg);
-
- /* Close log file if it's open and disabled */
- if(minivmlogfile)
- fclose(minivmlogfile);
-
- /* Open log file if it's enabled */
- if(!ast_strlen_zero(global_logfile)) {
- minivmlogfile = fopen(global_logfile, "a");
- if(!minivmlogfile)
- ast_log(LOG_ERROR, "Failed to open minivm log file %s : %s\n", global_logfile, strerror(errno));
- if (minivmlogfile)
- ast_debug(3, "-_-_- Opened log file %s \n", global_logfile);
- }
-
- return 0;
-}
-
-/*! \brief CLI routine for listing templates */
-static char *handle_minivm_list_templates(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct minivm_template *this;
- char *output_format = "%-15s %-10s %-10s %-15.15s %-50s\n";
- int count = 0;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "minivm list templates";
- e->usage =
- "Usage: minivm list templates\n"
- " Lists message templates for e-mail, paging and IM\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc > 3)
- return CLI_SHOWUSAGE;
-
- AST_LIST_LOCK(&message_templates);
- if (AST_LIST_EMPTY(&message_templates)) {
- ast_cli(a->fd, "There are no message templates defined\n");
- AST_LIST_UNLOCK(&message_templates);
- return CLI_FAILURE;
- }
- ast_cli(a->fd, output_format, "Template name", "Charset", "Locale", "Attach media", "Subject");
- ast_cli(a->fd, output_format, "-------------", "-------", "------", "------------", "-------");
- AST_LIST_TRAVERSE(&message_templates, this, list) {
- ast_cli(a->fd, output_format, this->name,
- this->charset ? this->charset : "-",
- this->locale ? this->locale : "-",
- this->attachment ? "Yes" : "No",
- this->subject ? this->subject : "-");
- count++;
- }
- AST_LIST_UNLOCK(&message_templates);
- ast_cli(a->fd, "\n * Total: %d minivoicemail message templates\n", count);
- return CLI_SUCCESS;
-}
-
-static char *complete_minivm_show_users(const char *line, const char *word, int pos, int state)
-{
- int which = 0;
- int wordlen;
- struct minivm_account *vmu;
- const char *domain = "";
-
- /* 0 - voicemail; 1 - list; 2 - accounts; 3 - for; 4 - <domain> */
- if (pos > 4)
- return NULL;
- if (pos == 3)
- return (state == 0) ? ast_strdup("for") : NULL;
- wordlen = strlen(word);
- AST_LIST_TRAVERSE(&minivm_accounts, vmu, list) {
- if (!strncasecmp(word, vmu->domain, wordlen)) {
- if (domain && strcmp(domain, vmu->domain) && ++which > state)
- return ast_strdup(vmu->domain);
- /* ignore repeated domains ? */
- domain = vmu->domain;
- }
- }
- return NULL;
-}
-
-/*! \brief CLI command to list voicemail accounts */
-static char *handle_minivm_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct minivm_account *vmu;
- char *output_format = "%-23s %-15s %-15s %-10s %-10s %-50s\n";
- int count = 0;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "minivm list accounts";
- e->usage =
- "Usage: minivm list accounts\n"
- " Lists all mailboxes currently set up\n";
- return NULL;
- case CLI_GENERATE:
- return complete_minivm_show_users(a->line, a->word, a->pos, a->n);
- }
-
- if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
- return CLI_SHOWUSAGE;
- if ((a->argc == 5) && strcmp(a->argv[3],"for"))
- return CLI_SHOWUSAGE;
-
- AST_LIST_LOCK(&minivm_accounts);
- if (AST_LIST_EMPTY(&minivm_accounts)) {
- ast_cli(a->fd, "There are no voicemail users currently defined\n");
- AST_LIST_UNLOCK(&minivm_accounts);
- return CLI_FAILURE;
- }
- ast_cli(a->fd, output_format, "User", "E-Template", "P-template", "Zone", "Format", "Full name");
- ast_cli(a->fd, output_format, "----", "----------", "----------", "----", "------", "---------");
- AST_LIST_TRAVERSE(&minivm_accounts, vmu, list) {
- char tmp[256] = "";
- if ((a->argc == 3) || ((a->argc == 5) && !strcmp(a->argv[4], vmu->domain))) {
- count++;
- snprintf(tmp, sizeof(tmp), "%s@%s", vmu->username, vmu->domain);
- ast_cli(a->fd, output_format, tmp, vmu->etemplate ? vmu->etemplate : "-",
- vmu->ptemplate ? vmu->ptemplate : "-",
- vmu->zonetag ? vmu->zonetag : "-",
- vmu->attachfmt ? vmu->attachfmt : "-",
- vmu->fullname);
- }
- }
- AST_LIST_UNLOCK(&minivm_accounts);
- ast_cli(a->fd, "\n * Total: %d minivoicemail accounts\n", count);
- return CLI_SUCCESS;
-}
-
-/*! \brief Show a list of voicemail zones in the CLI */
-static char *handle_minivm_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct minivm_zone *zone;
- char *output_format = "%-15s %-20s %-45s\n";
- char *res = CLI_SUCCESS;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "minivm list zones";
- e->usage =
- "Usage: minivm list zones\n"
- " Lists zone message formats\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
-
- AST_LIST_LOCK(&minivm_zones);
- if (!AST_LIST_EMPTY(&minivm_zones)) {
- ast_cli(a->fd, output_format, "Zone", "Timezone", "Message Format");
- ast_cli(a->fd, output_format, "----", "--------", "--------------");
- AST_LIST_TRAVERSE(&minivm_zones, zone, list) {
- ast_cli(a->fd, output_format, zone->name, zone->timezone, zone->msg_format);
- }
- } else {
- ast_cli(a->fd, "There are no voicemail zones currently defined\n");
- res = CLI_FAILURE;
- }
- AST_LIST_UNLOCK(&minivm_zones);
-
- return res;
-}
-
-/*! \brief CLI Show settings */
-static char *handle_minivm_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- switch (cmd) {
- case CLI_INIT:
- e->command = "minivm show settings";
- e->usage =
- "Usage: minivm show settings\n"
- " Display Mini-Voicemail general settings\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- ast_cli(a->fd, "* Mini-Voicemail general settings\n");
- ast_cli(a->fd, " -------------------------------\n");
- ast_cli(a->fd, "\n");
- ast_cli(a->fd, " Mail command (shell): %s\n", global_mailcmd);
- ast_cli(a->fd, " Max silence: %d\n", global_maxsilence);
- ast_cli(a->fd, " Silence treshold: %d\n", global_silencethreshold);
- ast_cli(a->fd, " Max message length (secs): %d\n", global_vmmaxmessage);
- ast_cli(a->fd, " Min message length (secs): %d\n", global_vmminmessage);
- ast_cli(a->fd, " Default format: %s\n", default_vmformat);
- ast_cli(a->fd, " Extern notify (shell): %s\n", global_externnotify);
- ast_cli(a->fd, " Logfile: %s\n", global_logfile[0] ? global_logfile : "<disabled>");
- ast_cli(a->fd, " Operator exit: %s\n", ast_test_flag(&globalflags, MVM_OPERATOR) ? "Yes" : "No");
- ast_cli(a->fd, " Message review: %s\n", ast_test_flag(&globalflags, MVM_REVIEW) ? "Yes" : "No");
-
- ast_cli(a->fd, "\n");
- return CLI_SUCCESS;
-}
-
-/*! \brief Show stats */
-static char *handle_minivm_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_tm time;
- char buf[BUFSIZ];
-
- switch (cmd) {
-
- case CLI_INIT:
- e->command = "minivm show stats";
- e->usage =
- "Usage: minivm show stats\n"
- " Display Mini-Voicemail counters\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- ast_cli(a->fd, "* Mini-Voicemail statistics\n");
- ast_cli(a->fd, " -------------------------\n");
- ast_cli(a->fd, "\n");
- ast_cli(a->fd, " Voicemail accounts: %5d\n", global_stats.voicemailaccounts);
- ast_cli(a->fd, " Templates: %5d\n", global_stats.templates);
- ast_cli(a->fd, " Timezones: %5d\n", global_stats.timezones);
- if (global_stats.receivedmessages == 0) {
- ast_cli(a->fd, " Received messages since last reset: <none>\n");
- } else {
- ast_cli(a->fd, " Received messages since last reset: %d\n", global_stats.receivedmessages);
- ast_localtime(&global_stats.lastreceived, &time, NULL);
- ast_strftime(buf, sizeof(buf), "%a %b %e %r %Z %Y", &time);
- ast_cli(a->fd, " Last received voicemail: %s\n", buf);
- }
- ast_localtime(&global_stats.reset, &time, NULL);
- ast_strftime(buf, sizeof(buf), "%a %b %e %r %Z %Y", &time);
- ast_cli(a->fd, " Last reset: %s\n", buf);
-
- ast_cli(a->fd, "\n");
- return CLI_SUCCESS;
-}
-
-/*! \brief ${MINIVMACCOUNT()} Dialplan function - reads account data */
-static int minivm_account_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
-{
- struct minivm_account *vmu;
- char *username, *domain, *colname;
-
- if (!(username = ast_strdupa(data))) {
- ast_log(LOG_ERROR, "Memory Error!\n");
- return -1;
- }
-
- if ((colname = strchr(username, ':'))) {
- *colname = '\0';
- colname++;
- } else {
- colname = "path";
- }
- if ((domain = strchr(username, '@'))) {
- *domain = '\0';
- domain++;
- }
- if (ast_strlen_zero(username) || ast_strlen_zero(domain)) {
- ast_log(LOG_ERROR, "This function needs a username and a domain: username@domain\n");
- return 0;
- }
-
- if (!(vmu = find_account(domain, username, TRUE)))
- return 0;
-
- if (!strcasecmp(colname, "hasaccount")) {
- ast_copy_string(buf, (ast_test_flag(vmu, MVM_ALLOCED) ? "0" : "1"), len);
- } else if (!strcasecmp(colname, "fullname")) {
- ast_copy_string(buf, vmu->fullname, len);
- } else if (!strcasecmp(colname, "email")) {
- if (!ast_strlen_zero(vmu->email))
- ast_copy_string(buf, vmu->email, len);
- else
- snprintf(buf, len, "%s@%s", vmu->username, vmu->domain);
- } else if (!strcasecmp(colname, "pager")) {
- ast_copy_string(buf, vmu->pager, len);
- } else if (!strcasecmp(colname, "etemplate")) {
- if (!ast_strlen_zero(vmu->etemplate))
- ast_copy_string(buf, vmu->etemplate, len);
- else
- ast_copy_string(buf, "email-default", len);
- } else if (!strcasecmp(colname, "language")) {
- ast_copy_string(buf, vmu->language, len);
- } else if (!strcasecmp(colname, "timezone")) {
- ast_copy_string(buf, vmu->zonetag, len);
- } else if (!strcasecmp(colname, "ptemplate")) {
- if (!ast_strlen_zero(vmu->ptemplate))
- ast_copy_string(buf, vmu->ptemplate, len);
- else
- ast_copy_string(buf, "email-default", len);
- } else if (!strcasecmp(colname, "accountcode")) {
- ast_copy_string(buf, vmu->accountcode, len);
- } else if (!strcasecmp(colname, "pincode")) {
- ast_copy_string(buf, vmu->pincode, len);
- } else if (!strcasecmp(colname, "path")) {
- check_dirpath(buf, len, vmu->domain, vmu->username, NULL);
- } else { /* Look in channel variables */
- struct ast_variable *var;
- int found = 0;
-
- for (var = vmu->chanvars ; var ; var = var->next)
- if (!strcmp(var->name, colname)) {
- ast_copy_string(buf, var->value, len);
- found = 1;
- break;
- }
- }
-
- if(ast_test_flag(vmu, MVM_ALLOCED))
- free_user(vmu);
-
- return 0;
-}
-
-/*! \brief lock directory
-
- only return failure if ast_lock_path returns 'timeout',
- not if the path does not exist or any other reason
-*/
-static int vm_lock_path(const char *path)
-{
- switch (ast_lock_path(path)) {
- case AST_LOCK_TIMEOUT:
- return -1;
- default:
- return 0;
- }
-}
-
-/*! \brief Access counter file, lock directory, read and possibly write it again changed
- \param directory Directory to crate file in
- \param countername filename
- \param value If set to zero, we only read the variable
- \param operand 0 to read, 1 to set new value, 2 to change
- \return -1 on error, otherwise counter value
-*/
-static int access_counter_file(char *directory, char *countername, int value, int operand)
-{
- char filename[BUFSIZ];
- char readbuf[BUFSIZ];
- FILE *counterfile;
- int old = 0, counter = 0;
-
- /* Lock directory */
- if (vm_lock_path(directory)) {
- return -1; /* Could not lock directory */
- }
- snprintf(filename, sizeof(filename), "%s/%s.counter", directory, countername);
- if (operand != 1) {
- counterfile = fopen(filename, "r");
- if (counterfile) {
- if(fgets(readbuf, sizeof(readbuf), counterfile)) {
- ast_debug(3, "Read this string from counter file: %s\n", readbuf);
- old = counter = atoi(readbuf);
- }
- fclose(counterfile);
- }
- }
- switch (operand) {
- case 0: /* Read only */
- ast_unlock_path(directory);
- ast_debug(2, "MINIVM Counter %s/%s: Value %d\n", directory, countername, counter);
- return counter;
- break;
- case 1: /* Set new value */
- counter = value;
- break;
- case 2: /* Change value */
- counter += value;
- if (counter < 0) /* Don't allow counters to fall below zero */
- counter = 0;
- break;
- }
-
- /* Now, write the new value to the file */
- counterfile = fopen(filename, "w");
- if (!counterfile) {
- ast_log(LOG_ERROR, "Could not open counter file for writing : %s - %s\n", filename, strerror(errno));
- ast_unlock_path(directory);
- return -1; /* Could not open file for writing */
- }
- fprintf(counterfile, "%d\n\n", counter);
- fclose(counterfile);
- ast_unlock_path(directory);
- ast_debug(2, "MINIVM Counter %s/%s: Old value %d New value %d\n", directory, countername, old, counter);
- return counter;
-}
-
-/*! \brief ${MINIVMCOUNTER()} Dialplan function - read counters */
-static int minivm_counter_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
-{
- char *username, *domain, *countername;
- struct minivm_account *vmu = NULL;
- char userpath[BUFSIZ];
- int res;
-
- *buf = '\0';
-
- if (!(username = ast_strdupa(data))) { /* Copy indata to local buffer */
- ast_log(LOG_WARNING, "Memory error!\n");
- return -1;
- }
- if ((countername = strchr(username, ':'))) {
- *countername = '\0';
- countername++;
- }
-
- if ((domain = strchr(username, '@'))) {
- *domain = '\0';
- domain++;
- }
-
- /* If we have neither username nor domain now, let's give up */
- if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
- ast_log(LOG_ERROR, "No account given\n");
- return -1;
- }
-
- if (ast_strlen_zero(countername)) {
- ast_log(LOG_ERROR, "This function needs two arguments: Account:countername\n");
- return -1;
- }
-
- /* We only have a domain, no username */
- if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
- domain = username;
- username = NULL;
- }
-
- /* If we can't find account or if the account is temporary, return. */
- if (!ast_strlen_zero(username) && !(vmu = find_account(domain, username, FALSE))) {
- ast_log(LOG_ERROR, "Minivm account does not exist: %s@%s\n", username, domain);
- return 0;
- }
-
- create_dirpath(userpath, sizeof(userpath), domain, username, NULL);
-
- /* We have the path, now read the counter file */
- res = access_counter_file(userpath, countername, 0, 0);
- if (res >= 0)
- snprintf(buf, len, "%d", res);
- return 0;
-}
-
-/*! \brief ${MINIVMCOUNTER()} Dialplan function - changes counter data */
-static int minivm_counter_func_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
-{
- char *username, *domain, *countername, *operand;
- char userpath[BUFSIZ];
- struct minivm_account *vmu;
- int change = 0;
- int operation = 0;
-
- if(!value)
- return -1;
- change = atoi(value);
-
- if (!(username = ast_strdupa(data))) { /* Copy indata to local buffer */
- ast_log(LOG_WARNING, "Memory error!\n");
- return -1;
- }
-
- if ((countername = strchr(username, ':'))) {
- *countername = '\0';
- countername++;
- }
- if ((operand = strchr(countername, ':'))) {
- *operand = '\0';
- operand++;
- }
-
- if ((domain = strchr(username, '@'))) {
- *domain = '\0';
- domain++;
- }
-
- /* If we have neither username nor domain now, let's give up */
- if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
- ast_log(LOG_ERROR, "No account given\n");
- return -1;
- }
-
- /* We only have a domain, no username */
- if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
- domain = username;
- username = NULL;
- }
-
- if (ast_strlen_zero(operand) || ast_strlen_zero(countername)) {
- ast_log(LOG_ERROR, "Writing to this function requires three arguments: Account:countername:operand\n");
- return -1;
- }
-
- /* If we can't find account or if the account is temporary, return. */
- if (!ast_strlen_zero(username) && !(vmu = find_account(domain, username, FALSE))) {
- ast_log(LOG_ERROR, "Minivm account does not exist: %s@%s\n", username, domain);
- return 0;
- }
-
- create_dirpath(userpath, sizeof(userpath), domain, username, NULL);
- /* Now, find out our operator */
- if (*operand == 'i') /* Increment */
- operation = 2;
- else if (*operand == 'd') {
- change = change * -1;
- operation = 2;
- } else if (*operand == 's')
- operation = 1;
- else {
- ast_log(LOG_ERROR, "Unknown operator: %s\n", operand);
- return -1;
- }
-
- /* We have the path, now read the counter file */
- access_counter_file(userpath, countername, change, operation);
- return 0;
-}
-
-
-/*! \brief CLI commands for Mini-voicemail */
-static struct ast_cli_entry cli_minivm[] = {
- AST_CLI_DEFINE(handle_minivm_show_users, "List defined mini-voicemail boxes"),
- AST_CLI_DEFINE(handle_minivm_show_zones, "List zone message formats"),
- AST_CLI_DEFINE(handle_minivm_list_templates, "List message templates"),
- AST_CLI_DEFINE(handle_minivm_reload, "Reload Mini-voicemail configuration"),
- AST_CLI_DEFINE(handle_minivm_show_stats, "Show some mini-voicemail statistics"),
- AST_CLI_DEFINE(handle_minivm_show_settings, "Show mini-voicemail general settings"),
-};
-
-static struct ast_custom_function minivm_counter_function = {
- .name = "MINIVMCOUNTER",
- .synopsis = "Reads or sets counters for MiniVoicemail message",
- .syntax = "MINIVMCOUNTER(<account>:name[:operand])",
- .read = minivm_counter_func_read,
- .write = minivm_counter_func_write,
- .desc = "Valid operands for changing the value of a counter when assigning a value are:\n"
- "- i Increment by value\n"
- "- d Decrement by value\n"
- "- s Set to value\n"
- "\nThe counters never goes below zero.\n"
- "- The name of the counter is a string, up to 10 characters\n"
- "- If account is given and it exists, the counter is specific for the account\n"
- "- If account is a domain and the domain directory exists, counters are specific for a domain\n"
- "The operation is atomic and the counter is locked while changing the value\n"
- "\nThe counters are stored as text files in the minivm account directories. It might be better to use\n"
- "realtime functions if you are using a database to operate your Asterisk\n",
-};
-
-static struct ast_custom_function minivm_account_function = {
- .name = "MINIVMACCOUNT",
- .synopsis = "Gets MiniVoicemail account information",
- .syntax = "MINIVMACCOUNT(<account>:item)",
- .read = minivm_account_func_read,
- .desc = "Valid items are:\n"
- "- path Path to account mailbox (if account exists, otherwise temporary mailbox)\n"
- "- hasaccount 1 if static Minivm account exists, 0 otherwise\n"
- "- fullname Full name of account owner\n"
- "- email Email address used for account\n"
- "- etemplate E-mail template for account (default template if none is configured)\n"
- "- ptemplate Pager template for account (default template if none is configured)\n"
- "- accountcode Account code for voicemail account\n"
- "- pincode Pin code for voicemail account\n"
- "- timezone Time zone for voicemail account\n"
- "- language Language for voicemail account\n"
- "- <channel variable name> Channel variable value (set in configuration for account)\n"
- "\n",
-};
-
-/*! \brief Load mini voicemail module */
-static int load_module(void)
-{
- int res;
-
- res = ast_register_application(app_minivm_record, minivm_record_exec, synopsis_minivm_record, descrip_minivm_record);
- res = ast_register_application(app_minivm_greet, minivm_greet_exec, synopsis_minivm_greet, descrip_minivm_greet);
- res = ast_register_application(app_minivm_notify, minivm_notify_exec, synopsis_minivm_notify, descrip_minivm_notify);
- res = ast_register_application(app_minivm_delete, minivm_delete_exec, synopsis_minivm_delete, descrip_minivm_delete);
- res = ast_register_application(app_minivm_accmess, minivm_accmess_exec, synopsis_minivm_accmess, descrip_minivm_accmess);
-
- ast_custom_function_register(&minivm_account_function);
- ast_custom_function_register(&minivm_counter_function);
- if (res)
- return(res);
-
- if ((res = load_config(0)))
- return(res);
-
- ast_cli_register_multiple(cli_minivm, sizeof(cli_minivm)/sizeof(cli_minivm[0]));
-
- /* compute the location of the voicemail spool directory */
- snprintf(MVM_SPOOL_DIR, sizeof(MVM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
-
- return res;
-}
-
-/*! \brief Reload mini voicemail module */
-static int reload(void)
-{
- return(load_config(1));
-}
-
-/*! \brief Reload cofiguration */
-static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "minivm reload";
- e->usage =
- "Usage: minivm reload\n"
- " Reload mini-voicemail configuration and reset statistics\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- reload();
- ast_cli(a->fd, "\n-- Mini voicemail re-configured \n");
- return CLI_SUCCESS;
-}
-
-/*! \brief Unload mini voicemail module */
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(app_minivm_record);
- res |= ast_unregister_application(app_minivm_greet);
- res |= ast_unregister_application(app_minivm_notify);
- res |= ast_unregister_application(app_minivm_delete);
- res |= ast_unregister_application(app_minivm_accmess);
- ast_cli_unregister_multiple(cli_minivm, sizeof(cli_minivm)/sizeof(cli_minivm[0]));
- ast_custom_function_unregister(&minivm_account_function);
- ast_custom_function_unregister(&minivm_counter_function);
-
- message_destroy_list(); /* Destroy list of voicemail message templates */
- timezone_destroy_list(); /* Destroy list of timezones */
- vmaccounts_destroy_list(); /* Destroy list of voicemail accounts */
-
- return res;
-}
-
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mini VoiceMail (A minimal Voicemail e-mail System)",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
diff --git a/trunk/apps/app_mixmonitor.c b/trunk/apps/app_mixmonitor.c
deleted file mode 100644
index 012af01c5..000000000
--- a/trunk/apps/app_mixmonitor.c
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2005, Anthony Minessale II
- * Copyright (C) 2005 - 2006, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- * Kevin P. Fleming <kpfleming@digium.com>
- *
- * Based on app_muxmon.c provided by
- * Anthony Minessale II <anthmct@yahoo.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief MixMonitor() - Record a call and mix the audio during the recording
- * \ingroup applications
- *
- * \author Mark Spencer <markster@digium.com>
- * \author Kevin P. Fleming <kpfleming@digium.com>
- *
- * \note Based on app_muxmon.c provided by
- * Anthony Minessale II <anthmct@yahoo.com>
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
-#include "asterisk/file.h"
-#include "asterisk/audiohook.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/cli.h"
-#include "asterisk/app.h"
-#include "asterisk/channel.h"
-
-#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
-
-static const char *app = "MixMonitor";
-static const char *synopsis = "Record a call and mix the audio during the recording";
-static const char *desc = ""
-" MixMonitor(<file>.<ext>[,<options>[,<command>]]):\n"
-"Records the audio on the current channel to the specified file.\n"
-"If the filename is an absolute path, uses that path, otherwise\n"
-"creates the file in the configured monitoring directory from\n"
-"asterisk.conf.\n\n"
-"Valid options:\n"
-" a - Append to the file instead of overwriting it.\n"
-" b - Only save audio to the file while the channel is bridged.\n"
-" Note: Does not include conferences or sounds played to each bridged\n"
-" party.\n"
-" v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"
-" V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"
-" W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
-" (range -4 to 4)\n\n"
-"<command> will be executed when the recording is over\n"
-"Any strings matching ^{X} will be unescaped to ${X}.\n"
-"All variables will be evaluated at the time MixMonitor is called.\n"
-"The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
-"";
-
-static const char *stop_app = "StopMixMonitor";
-static const char *stop_synopsis = "Stop recording a call through MixMonitor";
-static const char *stop_desc = ""
-" StopMixMonitor():\n"
-"Stops the audio recording that was started with a call to MixMonitor()\n"
-"on the current channel.\n"
-"";
-
-struct module_symbols *me;
-
-static const char *mixmonitor_spy_type = "MixMonitor";
-
-struct mixmonitor {
- struct ast_audiohook audiohook;
- char *filename;
- char *post_process;
- char *name;
- unsigned int flags;
- struct ast_channel *chan;
-};
-
-enum {
- MUXFLAG_APPEND = (1 << 1),
- MUXFLAG_BRIDGED = (1 << 2),
- MUXFLAG_VOLUME = (1 << 3),
- MUXFLAG_READVOLUME = (1 << 4),
- MUXFLAG_WRITEVOLUME = (1 << 5),
-} mixmonitor_flags;
-
-enum {
- OPT_ARG_READVOLUME = 0,
- OPT_ARG_WRITEVOLUME,
- OPT_ARG_VOLUME,
- OPT_ARG_ARRAY_SIZE,
-} mixmonitor_args;
-
-AST_APP_OPTIONS(mixmonitor_opts, {
- AST_APP_OPTION('a', MUXFLAG_APPEND),
- AST_APP_OPTION('b', MUXFLAG_BRIDGED),
- AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
- AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
- AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
-});
-
-static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
-{
- struct ast_channel *peer = NULL;
- int res = 0;
-
- if (!chan)
- return -1;
-
- ast_audiohook_attach(chan, audiohook);
-
- if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
- ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
-
- return res;
-}
-
-#define SAMPLES_PER_FRAME 160
-
-static void *mixmonitor_thread(void *obj)
-{
- struct mixmonitor *mixmonitor = obj;
- struct ast_filestream *fs = NULL;
- unsigned int oflags;
- char *ext;
- int errflag = 0;
-
- ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
-
- ast_audiohook_lock(&mixmonitor->audiohook);
-
- while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
- struct ast_frame *fr = NULL;
-
- ast_audiohook_trigger_wait(&mixmonitor->audiohook);
-
- if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
- break;
-
- if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR)))
- continue;
-
- if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || ast_bridged_channel(mixmonitor->chan)) {
- /* Initialize the file if not already done so */
- if (!fs && !errflag) {
- oflags = O_CREAT | O_WRONLY;
- oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
-
- if ((ext = strrchr(mixmonitor->filename, '.')))
- *(ext++) = '\0';
- else
- ext = "raw";
-
- if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) {
- ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
- errflag = 1;
- }
- }
-
- /* Write out frame */
- if (fs)
- ast_writestream(fs, fr);
- }
-
- /* All done! free it. */
- ast_frame_free(fr, 0);
-
- }
-
- ast_audiohook_detach(&mixmonitor->audiohook);
- ast_audiohook_unlock(&mixmonitor->audiohook);
- ast_audiohook_destroy(&mixmonitor->audiohook);
-
- ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
-
- if (fs)
- ast_closestream(fs);
-
- if (mixmonitor->post_process) {
- ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
- ast_safe_system(mixmonitor->post_process);
- }
-
- ast_free(mixmonitor);
-
-
- return NULL;
-}
-
-static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
- int readvol, int writevol, const char *post_process)
-{
- pthread_t thread;
- struct mixmonitor *mixmonitor;
- char postprocess2[1024] = "";
- size_t len;
-
- len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
-
- postprocess2[0] = 0;
- /* If a post process system command is given attach it to the structure */
- if (!ast_strlen_zero(post_process)) {
- char *p1, *p2;
-
- p1 = ast_strdupa(post_process);
- for (p2 = p1; *p2 ; p2++) {
- if (*p2 == '^' && *(p2+1) == '{') {
- *p2 = '$';
- }
- }
- pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
- if (!ast_strlen_zero(postprocess2))
- len += strlen(postprocess2) + 1;
- }
-
- /* Pre-allocate mixmonitor structure and spy */
- if (!(mixmonitor = ast_calloc(1, len))) {
- return;
- }
-
- /* Copy over flags and channel name */
- mixmonitor->flags = flags;
- mixmonitor->chan = chan;
- mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
- strcpy(mixmonitor->name, chan->name);
- if (!ast_strlen_zero(postprocess2)) {
- mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
- strcpy(mixmonitor->post_process, postprocess2);
- }
-
- mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
- strcpy(mixmonitor->filename, filename);
-
- /* Setup the actual spy before creating our thread */
- if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
- ast_free(mixmonitor);
- return;
- }
-
- ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_WRITE);
-
- if (readvol)
- mixmonitor->audiohook.options.read_volume = readvol;
- if (writevol)
- mixmonitor->audiohook.options.write_volume = writevol;
-
- if (startmon(chan, &mixmonitor->audiohook)) {
- ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
- mixmonitor_spy_type, chan->name);
- ast_audiohook_destroy(&mixmonitor->audiohook);
- ast_free(mixmonitor);
- return;
- }
-
- ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
-
-}
-
-static int mixmonitor_exec(struct ast_channel *chan, void *data)
-{
- int x, readvol = 0, writevol = 0;
- struct ast_flags flags = {0};
- char *parse, *tmp, *slash;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(filename);
- AST_APP_ARG(options);
- AST_APP_ARG(post_process);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (ast_strlen_zero(args.filename)) {
- ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
- return -1;
- }
-
- if (args.options) {
- char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
-
- ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
-
- if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
- if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
- ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
- } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
- ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
- } else {
- readvol = get_volfactor(x);
- }
- }
-
- if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
- if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
- ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
- } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
- ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
- } else {
- writevol = get_volfactor(x);
- }
- }
-
- if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
- if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
- ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
- } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
- ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
- } else {
- readvol = writevol = get_volfactor(x);
- }
- }
- }
-
- /* if not provided an absolute path, use the system-configured monitoring directory */
- if (args.filename[0] != '/') {
- char *build;
-
- build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
- sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
- args.filename = build;
- }
-
- tmp = ast_strdupa(args.filename);
- if ((slash = strrchr(tmp, '/')))
- *slash = '\0';
- ast_mkdir(tmp, 0777);
-
- pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
- launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
-
- return 0;
-}
-
-static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
-{
- ast_audiohook_detach_source(chan, mixmonitor_spy_type);
- return 0;
-}
-
-static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_channel *chan;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "mixmonitor [start|stop]";
- e->usage =
- "Usage: mixmonitor <start|stop> <chan_name> [args]\n"
- " The optional arguments are passed to the MixMonitor\n"
- " application when the 'start' command is used.\n";
- return NULL;
- case CLI_GENERATE:
- return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
- }
-
- if (a->argc < 3)
- return CLI_SHOWUSAGE;
-
- if (!(chan = ast_get_channel_by_name_prefix_locked(a->argv[2], strlen(a->argv[2])))) {
- ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
- /* Technically this is a failure, but we don't want 2 errors printing out */
- return CLI_SUCCESS;
- }
-
- if (!strcasecmp(a->argv[1], "start")) {
- mixmonitor_exec(chan, a->argv[3]);
- ast_channel_unlock(chan);
- } else {
- ast_channel_unlock(chan);
- ast_audiohook_detach_source(chan, mixmonitor_spy_type);
- }
-
- return CLI_SUCCESS;
-}
-
-static struct ast_cli_entry cli_mixmonitor[] = {
- AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
-};
-
-static int unload_module(void)
-{
- int res;
-
- ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
- res = ast_unregister_application(stop_app);
- res |= ast_unregister_application(app);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
-
- ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
- res = ast_register_application(app, mixmonitor_exec, synopsis, desc);
- res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");
diff --git a/trunk/apps/app_morsecode.c b/trunk/apps/app_morsecode.c
deleted file mode 100644
index 6c88ed32c..000000000
--- a/trunk/apps/app_morsecode.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (c) 2006, Tilghman Lesher. All rights reserved.
- *
- * Tilghman Lesher <app_morsecode__v001@the-tilghman.com>
- *
- * This code is released by the author with no restrictions on usage.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- */
-
-/*! \file
- *
- * \brief Morsecode application
- *
- * \author Tilghman Lesher <app_morsecode__v001@the-tilghman.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/indications.h"
-
-static char *app_morsecode = "Morsecode";
-
-static char *morsecode_synopsis = "Plays morse code";
-
-static char *morsecode_descrip =
-" Morsecode(<string>):\n"
-"Plays the Morse code equivalent of the passed string. If the variable\n"
-"MORSEDITLEN is set, it will use that value for the length (in ms) of the dit\n"
-"(defaults to 80). Additionally, if MORSETONE is set, it will use that tone\n"
-"(in Hz). The tone default is 800.\n";
-
-
-static char *morsecode[] = {
- "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 0-15 */
- "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 16-31 */
- " ", /* 32 - <space> */
- ".-.-.-", /* 33 - ! */
- ".-..-.", /* 34 - " */
- "", /* 35 - # */
- "", /* 36 - $ */
- "", /* 37 - % */
- "", /* 38 - & */
- ".----.", /* 39 - ' */
- "-.--.-", /* 40 - ( */
- "-.--.-", /* 41 - ) */
- "", /* 42 - * */
- "", /* 43 - + */
- "--..--", /* 44 - , */
- "-....-", /* 45 - - */
- ".-.-.-", /* 46 - . */
- "-..-.", /* 47 - / */
- "-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----.", /* 48-57 - 0-9 */
- "---...", /* 58 - : */
- "-.-.-.", /* 59 - ; */
- "", /* 60 - < */
- "-...-", /* 61 - = */
- "", /* 62 - > */
- "..--..", /* 63 - ? */
- ".--.-.", /* 64 - @ */
- ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--",
- "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..",
- "-.--.-", /* 91 - [ (really '(') */
- "-..-.", /* 92 - \ (really '/') */
- "-.--.-", /* 93 - ] (really ')') */
- "", /* 94 - ^ */
- "..--.-", /* 95 - _ */
- ".----.", /* 96 - ` */
- ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--",
- "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..",
- "-.--.-", /* 123 - { (really '(') */
- "", /* 124 - | */
- "-.--.-", /* 125 - } (really ')') */
- "-..-.", /* 126 - ~ (really bar) */
- ". . .", /* 127 - <del> (error) */
-};
-
-static void playtone(struct ast_channel *chan, int tone, int len)
-{
- char dtmf[20];
- snprintf(dtmf, sizeof(dtmf), "%d/%d", tone, len);
- ast_playtones_start(chan, 0, dtmf, 0);
- ast_safe_sleep(chan, len);
- ast_playtones_stop(chan);
-}
-
-static int morsecode_exec(struct ast_channel *chan, void *data)
-{
- int res=0, ditlen, tone;
- char *digit;
- const char *ditlenc, *tonec;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Syntax: Morsecode(<string>) - no argument found\n");
- return 0;
- }
-
- /* Use variable MORESEDITLEN, if set (else 80) */
- ditlenc = pbx_builtin_getvar_helper(chan, "MORSEDITLEN");
- if (ast_strlen_zero(ditlenc) || (sscanf(ditlenc, "%d", &ditlen) != 1)) {
- ditlen = 80;
- }
-
- /* Use variable MORSETONE, if set (else 800) */
- tonec = pbx_builtin_getvar_helper(chan, "MORSETONE");
- if (ast_strlen_zero(tonec) || (sscanf(tonec, "%d", &tone) != 1)) {
- tone = 800;
- }
-
- for (digit = data; *digit; digit++) {
- int digit2 = *digit;
- char *dahdit;
- if (digit2 < 0) {
- continue;
- }
- for (dahdit = morsecode[digit2]; *dahdit; dahdit++) {
- if (*dahdit == '-') {
- playtone(chan, tone, 3 * ditlen);
- } else if (*dahdit == '.') {
- playtone(chan, tone, 1 * ditlen);
- } else {
- /* Account for ditlen of silence immediately following */
- playtone(chan, 0, 2 * ditlen);
- }
-
- /* Pause slightly between each dit and dah */
- playtone(chan, 0, 1 * ditlen);
- }
- /* Pause between characters */
- playtone(chan, 0, 2 * ditlen);
- }
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app_morsecode);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app_morsecode, morsecode_exec, morsecode_synopsis, morsecode_descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Morse code");
diff --git a/trunk/apps/app_mp3.c b/trunk/apps/app_mp3.c
deleted file mode 100644
index d2f2f5c0e..000000000
--- a/trunk/apps/app_mp3.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Silly application to play an MP3 file -- uses mpg123
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <sys/time.h>
-#include <signal.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/frame.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-
-#define LOCAL_MPG_123 "/usr/local/bin/mpg123"
-#define MPG_123 "/usr/bin/mpg123"
-
-static char *app = "MP3Player";
-
-static char *synopsis = "Play an MP3 file or stream";
-
-static char *descrip =
-" MP3Player(location): Executes mpg123 to play the given location,\n"
-"which typically would be a filename or a URL. User can exit by pressing\n"
-"any key on the dialpad, or by hanging up.";
-
-
-static int mp3play(char *filename, int fd)
-{
- int res;
- int x;
- sigset_t fullset, oldset;
-
- sigfillset(&fullset);
- pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
- res = fork();
- if (res < 0)
- ast_log(LOG_WARNING, "Fork failed\n");
- if (res) {
- pthread_sigmask(SIG_SETMASK, &oldset, NULL);
- return res;
- }
- if (ast_opt_high_priority)
- ast_set_priority(0);
- signal(SIGPIPE, SIG_DFL);
- pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
-
- dup2(fd, STDOUT_FILENO);
- for (x=STDERR_FILENO + 1;x<256;x++) {
- if (x != STDOUT_FILENO)
- close(x);
- }
- /* Execute mpg123, but buffer if it's a net connection */
- if (!strncasecmp(filename, "http://", 7)) {
- /* Most commonly installed in /usr/local/bin */
- execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
- /* But many places has it in /usr/bin */
- execl(MPG_123, "mpg123", "-q", "-s", "-b", "1024","-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
- /* As a last-ditch effort, try to use PATH */
- execlp("mpg123", "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
- }
- else {
- /* Most commonly installed in /usr/local/bin */
- execl(MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
- /* But many places has it in /usr/bin */
- execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
- /* As a last-ditch effort, try to use PATH */
- execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
- }
- ast_log(LOG_WARNING, "Execute of mpg123 failed\n");
- _exit(0);
-}
-
-static int timed_read(int fd, void *data, int datalen, int timeout)
-{
- int res;
- struct pollfd fds[1];
- fds[0].fd = fd;
- fds[0].events = POLLIN;
- res = poll(fds, 1, timeout);
- if (res < 1) {
- ast_log(LOG_NOTICE, "Poll timed out/errored out with %d\n", res);
- return -1;
- }
- return read(fd, data, datalen);
-
-}
-
-static int mp3_exec(struct ast_channel *chan, void *data)
-{
- int res=0;
- int fds[2];
- int ms = -1;
- int pid = -1;
- int owriteformat;
- int timeout = 2000;
- struct timeval next;
- struct ast_frame *f;
- struct myframe {
- struct ast_frame f;
- char offset[AST_FRIENDLY_OFFSET];
- short frdata[160];
- } myf;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "MP3 Playback requires an argument (filename)\n");
- return -1;
- }
-
- if (pipe(fds)) {
- ast_log(LOG_WARNING, "Unable to create pipe\n");
- return -1;
- }
-
- ast_stopstream(chan);
-
- owriteformat = chan->writeformat;
- res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
- if (res < 0) {
- ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
- return -1;
- }
-
- res = mp3play((char *)data, fds[1]);
- if (!strncasecmp((char *)data, "http://", 7)) {
- timeout = 10000;
- }
- /* Wait 1000 ms first */
- next = ast_tvnow();
- next.tv_sec += 1;
- if (res >= 0) {
- pid = res;
- /* Order is important -- there's almost always going to be mp3... we want to prioritize the
- user */
- for (;;) {
- ms = ast_tvdiff_ms(next, ast_tvnow());
- if (ms <= 0) {
- res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata), timeout);
- if (res > 0) {
- myf.f.frametype = AST_FRAME_VOICE;
- myf.f.subclass = AST_FORMAT_SLINEAR;
- myf.f.datalen = res;
- myf.f.samples = res / 2;
- myf.f.mallocd = 0;
- myf.f.offset = AST_FRIENDLY_OFFSET;
- myf.f.src = __PRETTY_FUNCTION__;
- myf.f.delivery.tv_sec = 0;
- myf.f.delivery.tv_usec = 0;
- myf.f.data = myf.frdata;
- if (ast_write(chan, &myf.f) < 0) {
- res = -1;
- break;
- }
- } else {
- ast_debug(1, "No more mp3\n");
- res = 0;
- break;
- }
- next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
- } else {
- ms = ast_waitfor(chan, ms);
- if (ms < 0) {
- ast_debug(1, "Hangup detected\n");
- res = -1;
- break;
- }
- if (ms) {
- f = ast_read(chan);
- if (!f) {
- ast_debug(1, "Null frame == hangup() detected\n");
- res = -1;
- break;
- }
- if (f->frametype == AST_FRAME_DTMF) {
- ast_debug(1, "User pressed a key\n");
- ast_frfree(f);
- res = 0;
- break;
- }
- ast_frfree(f);
- }
- }
- }
- }
- close(fds[0]);
- close(fds[1]);
-
- if (pid > -1)
- kill(pid, SIGKILL);
- if (!res && owriteformat)
- ast_set_write_format(chan, owriteformat);
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, mp3_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly MP3 Application");
diff --git a/trunk/apps/app_nbscat.c b/trunk/apps/app_nbscat.c
deleted file mode 100644
index 4e7203319..000000000
--- a/trunk/apps/app_nbscat.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Silly application to play an NBScat file -- uses nbscat8k
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <fcntl.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <signal.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/frame.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-
-#define LOCAL_NBSCAT "/usr/local/bin/nbscat8k"
-#define NBSCAT "/usr/bin/nbscat8k"
-
-#ifndef AF_LOCAL
-#define AF_LOCAL AF_UNIX
-#endif
-
-static char *app = "NBScat";
-
-static char *synopsis = "Play an NBS local stream";
-
-static char *descrip =
-" NBScat(): Executes nbscat to listen to the local NBS stream.\n"
-"User can exit by pressing any key.\n";
-
-
-static int NBScatplay(int fd)
-{
- int res;
- int x;
- sigset_t fullset, oldset;
-
- sigfillset(&fullset);
- pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
- res = fork();
- if (res < 0)
- ast_log(LOG_WARNING, "Fork failed\n");
- if (res) {
- pthread_sigmask(SIG_SETMASK, &oldset, NULL);
- return res;
- }
- signal(SIGPIPE, SIG_DFL);
- pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
-
- if (ast_opt_high_priority)
- ast_set_priority(0);
-
- dup2(fd, STDOUT_FILENO);
- for (x = STDERR_FILENO + 1; x < 1024; x++) {
- if (x != STDOUT_FILENO)
- close(x);
- }
- /* Most commonly installed in /usr/local/bin */
- execl(NBSCAT, "nbscat8k", "-d", (char *)NULL);
- execl(LOCAL_NBSCAT, "nbscat8k", "-d", (char *)NULL);
- ast_log(LOG_WARNING, "Execute of nbscat8k failed\n");
- _exit(0);
-}
-
-static int timed_read(int fd, void *data, int datalen)
-{
- int res;
- struct pollfd fds[1];
- fds[0].fd = fd;
- fds[0].events = POLLIN;
- res = poll(fds, 1, 2000);
- if (res < 1) {
- ast_log(LOG_NOTICE, "Selected timed out/errored out with %d\n", res);
- return -1;
- }
- return read(fd, data, datalen);
-
-}
-
-static int NBScat_exec(struct ast_channel *chan, void *data)
-{
- int res=0;
- int fds[2];
- int ms = -1;
- int pid = -1;
- int owriteformat;
- struct timeval next;
- struct ast_frame *f;
- struct myframe {
- struct ast_frame f;
- char offset[AST_FRIENDLY_OFFSET];
- short frdata[160];
- } myf;
-
- if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds)) {
- ast_log(LOG_WARNING, "Unable to create socketpair\n");
- return -1;
- }
-
- ast_stopstream(chan);
-
- owriteformat = chan->writeformat;
- res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
- if (res < 0) {
- ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
- return -1;
- }
-
- res = NBScatplay(fds[1]);
- /* Wait 1000 ms first */
- next = ast_tvnow();
- next.tv_sec += 1;
- if (res >= 0) {
- pid = res;
- /* Order is important -- there's almost always going to be mp3... we want to prioritize the
- user */
- for (;;) {
- ms = ast_tvdiff_ms(next, ast_tvnow());
- if (ms <= 0) {
- res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata));
- if (res > 0) {
- myf.f.frametype = AST_FRAME_VOICE;
- myf.f.subclass = AST_FORMAT_SLINEAR;
- myf.f.datalen = res;
- myf.f.samples = res / 2;
- myf.f.mallocd = 0;
- myf.f.offset = AST_FRIENDLY_OFFSET;
- myf.f.src = __PRETTY_FUNCTION__;
- myf.f.delivery.tv_sec = 0;
- myf.f.delivery.tv_usec = 0;
- myf.f.data = myf.frdata;
- if (ast_write(chan, &myf.f) < 0) {
- res = -1;
- break;
- }
- } else {
- ast_debug(1, "No more mp3\n");
- res = 0;
- break;
- }
- next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
- } else {
- ms = ast_waitfor(chan, ms);
- if (ms < 0) {
- ast_debug(1, "Hangup detected\n");
- res = -1;
- break;
- }
- if (ms) {
- f = ast_read(chan);
- if (!f) {
- ast_debug(1, "Null frame == hangup() detected\n");
- res = -1;
- break;
- }
- if (f->frametype == AST_FRAME_DTMF) {
- ast_debug(1, "User pressed a key\n");
- ast_frfree(f);
- res = 0;
- break;
- }
- ast_frfree(f);
- }
- }
- }
- }
- close(fds[0]);
- close(fds[1]);
-
- if (pid > -1)
- kill(pid, SIGKILL);
- if (!res && owriteformat)
- ast_set_write_format(chan, owriteformat);
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, NBScat_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly NBS Stream Application");
diff --git a/trunk/apps/app_osplookup.c b/trunk/apps/app_osplookup.c
deleted file mode 100644
index bee5dae1d..000000000
--- a/trunk/apps/app_osplookup.c
+++ /dev/null
@@ -1,2041 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2006, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*!
- * \file
- * \brief Open Settlement Protocol (OSP) Applications
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \extref The OSP Toolkit: http://www.transnexus.com
- * \extref OpenSSL http://www.openssl.org
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>osptk</depend>
- <depend>ssl</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <osp/osp.h>
-#include <osp/osputils.h>
-
-#include "asterisk/paths.h"
-#include "asterisk/lock.h"
-#include "asterisk/config.h"
-#include "asterisk/utils.h"
-#include "asterisk/causes.h"
-#include "asterisk/channel.h"
-#include "asterisk/app.h"
-#include "asterisk/module.h"
-#include "asterisk/pbx.h"
-#include "asterisk/cli.h"
-#include "asterisk/astosp.h"
-
-/* OSP Buffer Sizes */
-#define OSP_INTSTR_SIZE ((unsigned int)16) /* OSP signed/unsigned int string buffer size */
-#define OSP_NORSTR_SIZE ((unsigned int)256) /* OSP normal string buffer size */
-#define OSP_TOKSTR_SIZE ((unsigned int)4096) /* OSP token string buffer size */
-#define OSP_TECHSTR_SIZE ((unsigned int)32) /* OSP signed/unsigned int string buffer size */
-#define OSP_UUID_SIZE ((unsigned int)16) /* UUID size */
-#define OSP_UUIDSTR_SIZE ((unsigned int)36) /* UUID string size */
-
-/* OSP Authentication Policy */
-enum osp_authpolicy {
- OSP_AUTH_NO, /* Accept any call */
- OSP_AUTH_YES, /* Accept call with valid OSP token or without OSP token */
- OSP_AUTH_EXCLUSIVE /* Only accept call with valid OSP token */
-};
-
-/* Call ID type*/
-#define OSP_CALLID_UNDEFINED ((unsigned int)0) /* UNDEFINED */
-#define OSP_CALLID_H323 ((unsigned int)(1 << 0)) /* H.323 */
-#define OSP_CALLID_SIP ((unsigned int)(1 << 1)) /* SIP */
-#define OSP_CALLID_IAX ((unsigned int)(1 << 2)) /* IAX2 */
-#define OSP_CALLID_MAXNUM ((unsigned int)3) /* Max number of call ID type */
-
-/* OSP Supported Destination Protocols */
-#define OSP_PROT_H323 ((char*)"H323") /* H323 Q931 protocol name*/
-#define OSP_PROT_SIP ((char*)"SIP") /* SIP protocol name */
-#define OSP_PROT_IAX ((char*)"IAX") /* IAX protocol name */
-#define OSP_PROT_OTHER ((char*)"OTHER") /* Other protocol name */
-
-/* OSP supported Destination Tech */
-#if 0
-#define OSP_TECH_H323 ((char*)"OOH323") /* OOH323 tech name */
-#endif
-#define OSP_TECH_H323 ((char*)"H323") /* OH323 tech name */
-#define OSP_TECH_SIP ((char*)"SIP") /* SIP tech name */
-#define OSP_TECH_IAX ((char*)"IAX2") /* IAX2 tech name */
-
-/* SIP OSP header field name */
-#define OSP_SIP_HEADER ((char*)"P-OSP-Auth-Token: ")
-
-/* OSP Constants */
-#define OSP_INVALID_HANDLE ((int)-1) /* Invalid OSP handle, provider, transaction etc. */
-#define OSP_CONFIG_FILE ((const char*)"osp.conf") /* OSP configuration file name */
-#define OSP_GENERAL_CAT ((const char*)"general") /* OSP global configuration context name */
-#define OSP_DEF_PROVIDER ((const char*)"default") /* OSP default provider context name */
-#define OSP_MAX_CERTS ((unsigned int)10) /* OSP max number of cacerts */
-#define OSP_MAX_SRVS ((unsigned int)10) /* OSP max number of service points */
-#define OSP_DEF_MAXCONNECTIONS ((unsigned int)20) /* OSP default max_connections */
-#define OSP_MIN_MAXCONNECTIONS ((unsigned int)1) /* OSP min max_connections */
-#define OSP_MAX_MAXCONNECTIONS ((unsigned int)1000) /* OSP max max_connections */
-#define OSP_DEF_RETRYDELAY ((unsigned int)0) /* OSP default retry delay */
-#define OSP_MIN_RETRYDELAY ((unsigned int)0) /* OSP min retry delay */
-#define OSP_MAX_RETRYDELAY ((unsigned int)10) /* OSP max retry delay */
-#define OSP_DEF_RETRYLIMIT ((unsigned int)2) /* OSP default retry times */
-#define OSP_MIN_RETRYLIMIT ((unsigned int)0) /* OSP min retry times */
-#define OSP_MAX_RETRYLIMIT ((unsigned int)100) /* OSP max retry times */
-#define OSP_DEF_TIMEOUT ((unsigned int)500) /* OSP default timeout in ms */
-#define OSP_MIN_TIMEOUT ((unsigned int)200) /* OSP min timeout in ms */
-#define OSP_MAX_TIMEOUT ((unsigned int)10000) /* OSP max timeout in ms */
-#define OSP_DEF_AUTHPOLICY ((enum osp_authpolicy)OSP_AUTH_YES)
-#define OSP_AUDIT_URL ((const char*)"localhost") /* OSP default Audit URL */
-#define OSP_LOCAL_VALIDATION ((int)1) /* Validate OSP token locally */
-#define OSP_SSL_LIFETIME ((unsigned int)300) /* SSL life time, in seconds */
-#define OSP_HTTP_PERSISTENCE ((int)1) /* In seconds */
-#define OSP_CUSTOMER_ID ((const char*)"") /* OSP customer ID */
-#define OSP_DEVICE_ID ((const char*)"") /* OSP device ID */
-#define OSP_DEF_DESTINATIONS ((unsigned int)5) /* OSP default max number of destinations */
-#define OSP_DEF_TIMELIMIT ((unsigned int)0) /* OSP default duration limit, no limit */
-#define OSP_DEF_PROTOCOL OSP_PROT_SIP /* OSP default destination protocol, SIP */
-
-/* OSP Provider */
-struct osp_provider {
- char name[OSP_NORSTR_SIZE]; /* OSP provider context name */
- char privatekey[OSP_NORSTR_SIZE]; /* OSP private key file name */
- char localcert[OSP_NORSTR_SIZE]; /* OSP local cert file name */
- unsigned int cacount; /* Number of cacerts */
- char cacerts[OSP_MAX_CERTS][OSP_NORSTR_SIZE]; /* Cacert file names */
- unsigned int spcount; /* Number of service points */
- char srvpoints[OSP_MAX_SRVS][OSP_NORSTR_SIZE]; /* Service point URLs */
- int maxconnections; /* Max number of connections */
- int retrydelay; /* Retry delay */
- int retrylimit; /* Retry limit */
- int timeout; /* Timeout in ms */
- char source[OSP_NORSTR_SIZE]; /* IP of self */
- enum osp_authpolicy authpolicy; /* OSP authentication policy */
- char* defaultprotocol; /* OSP default destination protocol */
- OSPTPROVHANDLE handle; /* OSP provider handle */
- struct osp_provider* next; /* Pointer to next OSP provider */
-};
-
-/* Call ID */
-struct osp_callid {
- unsigned char buf[OSPC_CALLID_MAXSIZE]; /* Call ID string */
- unsigned int len; /* Call ID length */
-};
-
-/* OSP Application In/Output Results */
-struct osp_result {
- int inhandle; /* Inbound transaction handle */
- int outhandle; /* Outbound transaction handle */
- unsigned int intimelimit; /* Inbound duration limit */
- unsigned int outtimelimit; /* Outbound duration limit */
- char tech[OSP_TECHSTR_SIZE]; /* Outbound Asterisk TECH string */
- char dest[OSP_NORSTR_SIZE]; /* Outbound destination IP address */
- char called[OSP_NORSTR_SIZE]; /* Outbound called number, may be translated */
- char calling[OSP_NORSTR_SIZE]; /* Outbound calling number, may be translated */
- char token[OSP_TOKSTR_SIZE]; /* Outbound OSP token */
- char networkid[OSP_NORSTR_SIZE]; /* Outbound network ID */
- unsigned int numresults; /* Number of remain outbound destinations */
- struct osp_callid outcallid; /* Outbound call ID */
-};
-
-/* OSP Module Global Variables */
-AST_MUTEX_DEFINE_STATIC(osplock); /* Lock of OSP provider list */
-static int osp_initialized = 0; /* Init flag */
-static int osp_hardware = 0; /* Hardware accelleration flag */
-static struct osp_provider* ospproviders = NULL; /* OSP provider list */
-static unsigned int osp_tokenformat = TOKEN_ALGO_SIGNED; /* Token format supported */
-
-/* OSP Client Wrapper APIs */
-
-/*!
- * \brief Create OSP provider handle according to configuration
- * \param cfg OSP configuration
- * \param provider OSP provider context name
- * \return 1 Success, 0 Failed, -1 Error
- */
-static int osp_create_provider(
- struct ast_config* cfg,
- const char* provider)
-{
- int res;
- unsigned int t, i, j;
- struct osp_provider* p;
- struct ast_variable* v;
- OSPTPRIVATEKEY privatekey;
- OSPTCERT localcert;
- const char* psrvpoints[OSP_MAX_SRVS];
- OSPTCERT cacerts[OSP_MAX_CERTS];
- const OSPTCERT* pcacerts[OSP_MAX_CERTS];
- int error = OSPC_ERR_NO_ERROR;
-
- if (!(p = ast_calloc(1, sizeof(*p)))) {
- ast_log(LOG_ERROR, "Out of memory\n");
- return -1;
- }
-
- ast_copy_string(p->name, provider, sizeof(p->name));
- snprintf(p->privatekey, sizeof(p->privatekey), "%s/%s-privatekey.pem", ast_config_AST_KEY_DIR, provider);
- snprintf(p->localcert, sizeof(p->localcert), "%s/%s-localcert.pem", ast_config_AST_KEY_DIR, provider);
- p->maxconnections = OSP_DEF_MAXCONNECTIONS;
- p->retrydelay = OSP_DEF_RETRYDELAY;
- p->retrylimit = OSP_DEF_RETRYLIMIT;
- p->timeout = OSP_DEF_TIMEOUT;
- p->authpolicy = OSP_DEF_AUTHPOLICY;
- p->defaultprotocol = OSP_DEF_PROTOCOL;
- p->handle = OSP_INVALID_HANDLE;
-
- v = ast_variable_browse(cfg, provider);
- while(v) {
- if (!strcasecmp(v->name, "privatekey")) {
- if (v->value[0] == '/') {
- ast_copy_string(p->privatekey, v->value, sizeof(p->privatekey));
- } else {
- snprintf(p->privatekey, sizeof(p->privatekey), "%s/%s", ast_config_AST_KEY_DIR, v->value);
- }
- ast_debug(1, "OSP: privatekey '%s'\n", p->privatekey);
- } else if (!strcasecmp(v->name, "localcert")) {
- if (v->value[0] == '/') {
- ast_copy_string(p->localcert, v->value, sizeof(p->localcert));
- } else {
- snprintf(p->localcert, sizeof(p->localcert), "%s/%s", ast_config_AST_KEY_DIR, v->value);
- }
- ast_debug(1, "OSP: localcert '%s'\n", p->localcert);
- } else if (!strcasecmp(v->name, "cacert")) {
- if (p->cacount < OSP_MAX_CERTS) {
- if (v->value[0] == '/') {
- ast_copy_string(p->cacerts[p->cacount], v->value, sizeof(p->cacerts[0]));
- } else {
- snprintf(p->cacerts[p->cacount], sizeof(p->cacerts[0]), "%s/%s", ast_config_AST_KEY_DIR, v->value);
- }
- ast_debug(1, "OSP: cacert[%d]: '%s'\n", p->cacount, p->cacerts[p->cacount]);
- p->cacount++;
- } else {
- ast_log(LOG_WARNING, "OSP: Too many CA Certificates at line %d\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "servicepoint")) {
- if (p->spcount < OSP_MAX_SRVS) {
- ast_copy_string(p->srvpoints[p->spcount], v->value, sizeof(p->srvpoints[0]));
- ast_debug(1, "OSP: servicepoint[%d]: '%s'\n", p->spcount, p->srvpoints[p->spcount]);
- p->spcount++;
- } else {
- ast_log(LOG_WARNING, "OSP: Too many Service Points at line %d\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "maxconnections")) {
- if ((sscanf(v->value, "%d", &t) == 1) && (t >= OSP_MIN_MAXCONNECTIONS) && (t <= OSP_MAX_MAXCONNECTIONS)) {
- p->maxconnections = t;
- ast_debug(1, "OSP: maxconnections '%d'\n", t);
- } else {
- ast_log(LOG_WARNING, "OSP: maxconnections should be an integer from %d to %d, not '%s' at line %d\n",
- OSP_MIN_MAXCONNECTIONS, OSP_MAX_MAXCONNECTIONS, v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "retrydelay")) {
- if ((sscanf(v->value, "%d", &t) == 1) && (t >= OSP_MIN_RETRYDELAY) && (t <= OSP_MAX_RETRYDELAY)) {
- p->retrydelay = t;
- ast_debug(1, "OSP: retrydelay '%d'\n", t);
- } else {
- ast_log(LOG_WARNING, "OSP: retrydelay should be an integer from %d to %d, not '%s' at line %d\n",
- OSP_MIN_RETRYDELAY, OSP_MAX_RETRYDELAY, v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "retrylimit")) {
- if ((sscanf(v->value, "%d", &t) == 1) && (t >= OSP_MIN_RETRYLIMIT) && (t <= OSP_MAX_RETRYLIMIT)) {
- p->retrylimit = t;
- ast_debug(1, "OSP: retrylimit '%d'\n", t);
- } else {
- ast_log(LOG_WARNING, "OSP: retrylimit should be an integer from %d to %d, not '%s' at line %d\n",
- OSP_MIN_RETRYLIMIT, OSP_MAX_RETRYLIMIT, v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "timeout")) {
- if ((sscanf(v->value, "%d", &t) == 1) && (t >= OSP_MIN_TIMEOUT) && (t <= OSP_MAX_TIMEOUT)) {
- p->timeout = t;
- ast_debug(1, "OSP: timeout '%d'\n", t);
- } else {
- ast_log(LOG_WARNING, "OSP: timeout should be an integer from %d to %d, not '%s' at line %d\n",
- OSP_MIN_TIMEOUT, OSP_MAX_TIMEOUT, v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "source")) {
- ast_copy_string(p->source, v->value, sizeof(p->source));
- ast_debug(1, "OSP: source '%s'\n", p->source);
- } else if (!strcasecmp(v->name, "authpolicy")) {
- if ((sscanf(v->value, "%d", &t) == 1) && ((t == OSP_AUTH_NO) || (t == OSP_AUTH_YES) || (t == OSP_AUTH_EXCLUSIVE))) {
- p->authpolicy = t;
- ast_debug(1, "OSP: authpolicy '%d'\n", t);
- } else {
- ast_log(LOG_WARNING, "OSP: authpolicy should be %d, %d or %d, not '%s' at line %d\n",
- OSP_AUTH_NO, OSP_AUTH_YES, OSP_AUTH_EXCLUSIVE, v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "defaultprotocol")) {
- if (!strcasecmp(v->value, OSP_PROT_SIP)) {
- p->defaultprotocol = OSP_PROT_SIP;
- ast_debug(1, "OSP: default protocol '%s'\n", p->defaultprotocol);
- } else if (!strcasecmp(v->value, OSP_PROT_H323)) {
- p->defaultprotocol = OSP_PROT_H323;
- ast_debug(1, "OSP: default protocol '%s'\n", p->defaultprotocol);
- } else if (!strcasecmp(v->value, OSP_PROT_IAX)) {
- p->defaultprotocol = OSP_PROT_IAX;
- ast_debug(1, "OSP: default protocol '%s'\n", p->defaultprotocol);
- } else {
- ast_log(LOG_WARNING, "OSP: default protocol should be %s, %s, %s, or %s not '%s' at line %d\n",
- OSP_PROT_SIP, OSP_PROT_H323, OSP_PROT_IAX, OSP_PROT_OTHER, v->value, v->lineno);
- }
- }
- v = v->next;
- }
-
- error = OSPPUtilLoadPEMPrivateKey((unsigned char*)p->privatekey, &privatekey);
- if (error != OSPC_ERR_NO_ERROR) {
- ast_log(LOG_WARNING, "OSP: Unable to load privatekey '%s', error '%d'\n", p->privatekey, error);
- ast_free(p);
- return 0;
- }
-
- error = OSPPUtilLoadPEMCert((unsigned char*)p->localcert, &localcert);
- if (error != OSPC_ERR_NO_ERROR) {
- ast_log(LOG_WARNING, "OSP: Unable to load localcert '%s', error '%d'\n", p->localcert, error);
- if (privatekey.PrivateKeyData) {
- ast_free(privatekey.PrivateKeyData);
- }
- ast_free(p);
- return 0;
- }
-
- if (p->cacount < 1) {
- snprintf(p->cacerts[p->cacount], sizeof(p->cacerts[0]), "%s/%s-cacert.pem", ast_config_AST_KEY_DIR, provider);
- ast_debug(1, "OSP: cacert[%d]: '%s'\n", p->cacount, p->cacerts[p->cacount]);
- p->cacount++;
- }
- for (i = 0; i < p->cacount; i++) {
- error = OSPPUtilLoadPEMCert((unsigned char*)p->cacerts[i], &cacerts[i]);
- if (error != OSPC_ERR_NO_ERROR) {
- ast_log(LOG_WARNING, "OSP: Unable to load cacert '%s', error '%d'\n", p->cacerts[i], error);
- for (j = 0; j < i; j++) {
- if (cacerts[j].CertData) {
- ast_free(cacerts[j].CertData);
- }
- }
- if (localcert.CertData) {
- ast_free(localcert.CertData);
- }
- if (privatekey.PrivateKeyData) {
- ast_free(privatekey.PrivateKeyData);
- }
- ast_free(p);
- return 0;
- }
- pcacerts[i] = &cacerts[i];
- }
-
- for (i = 0; i < p->spcount; i++) {
- psrvpoints[i] = p->srvpoints[i];
- }
-
- error = OSPPProviderNew(
- p->spcount,
- psrvpoints,
- NULL,
- OSP_AUDIT_URL,
- &privatekey,
- &localcert,
- p->cacount,
- pcacerts,
- OSP_LOCAL_VALIDATION,
- OSP_SSL_LIFETIME,
- p->maxconnections,
- OSP_HTTP_PERSISTENCE,
- p->retrydelay,
- p->retrylimit,
- p->timeout,
- OSP_CUSTOMER_ID,
- OSP_DEVICE_ID,
- &p->handle);
- if (error != OSPC_ERR_NO_ERROR) {
- ast_log(LOG_WARNING, "OSP: Unable to create provider '%s', error '%d'\n", provider, error);
- ast_free(p);
- res = -1;
- } else {
- ast_debug(1, "OSP: provider '%s'\n", provider);
- ast_mutex_lock(&osplock);
- p->next = ospproviders;
- ospproviders = p;
- ast_mutex_unlock(&osplock);
- res = 1;
- }
-
- for (i = 0; i < p->cacount; i++) {
- if (cacerts[i].CertData) {
- ast_free(cacerts[i].CertData);
- }
- }
- if (localcert.CertData) {
- ast_free(localcert.CertData);
- }
- if (privatekey.PrivateKeyData) {
- ast_free(privatekey.PrivateKeyData);
- }
-
- return res;
-}
-
-/*!
- * \brief Get OSP provider by name
- * \param name OSP provider context name
- * \param provider OSP provider structure
- * \return 1 Success, 0 Failed, -1 Error
- */
-static int osp_get_provider(
- const char* name,
- struct osp_provider** provider)
-{
- int res = 0;
- struct osp_provider* p;
-
- ast_mutex_lock(&osplock);
- p = ospproviders;
- while(p) {
- if (!strcasecmp(p->name, name)) {
- *provider = p;
- ast_debug(1, "OSP: find provider '%s'\n", name);
- res = 1;
- break;
- }
- p = p->next;
- }
- ast_mutex_unlock(&osplock);
-
- return res;
-}
-
-/*!
- * \brief Create OSP transaction handle
- * \param provider OSP provider context name
- * \param transaction OSP transaction handle, output
- * \param sourcesize Size of source buffer, in/output
- * \param source Source of provider, output
- * \return 1 Success, 0 Failed, -1 Error
- */
-static int osp_create_transaction(
- const char* provider,
- int* transaction,
- unsigned int sourcesize,
- char* source)
-{
- int res = 0;
- struct osp_provider* p;
- int error;
-
- ast_mutex_lock(&osplock);
- p = ospproviders;
- while(p) {
- if (!strcasecmp(p->name, provider)) {
- error = OSPPTransactionNew(p->handle, transaction);
- if (error == OSPC_ERR_NO_ERROR) {
- ast_debug(1, "OSP: transaction '%d'\n", *transaction);
- ast_copy_string(source, p->source, sourcesize);
- ast_debug(1, "OSP: source '%s'\n", source);
- res = 1;
- } else {
- *transaction = OSP_INVALID_HANDLE;
- ast_debug(1, "OSP: Unable to create transaction handle, error '%d'\n", error);
- res = -1;
- }
- break;
- }
- p = p->next;
- }
- ast_mutex_unlock(&osplock);
-
- return res;
-}
-
-/*!
- * \brief Convert address to "[x.x.x.x]" or "host.domain" format
- * \param src Source address string
- * \param dst Destination address string
- * \param buffersize Size of dst buffer
- */
-static void osp_convert_address(
- const char* src,
- char* dst,
- int buffersize)
-{
- struct in_addr inp;
-
- if (inet_aton(src, &inp) != 0) {
- snprintf(dst, buffersize, "[%s]", src);
- } else {
- snprintf(dst, buffersize, "%s", src);
- }
-}
-
-/*!
- * \brief Validate OSP token of inbound call
- * \param transaction OSP transaction handle
- * \param source Source of inbound call
- * \param destination Destination of inbound call
- * \param calling Calling number
- * \param called Called number
- * \param token OSP token, may be empty
- * \param timelimit Call duration limit, output
- * \return 1 Success, 0 Failed, -1 Error
- */
-static int osp_validate_token(
- int transaction,
- const char* source,
- const char* destination,
- const char* calling,
- const char* called,
- const char* token,
- unsigned int* timelimit)
-{
- int res;
- int tokenlen;
- unsigned char tokenstr[OSP_TOKSTR_SIZE];
- char src[OSP_NORSTR_SIZE];
- char dst[OSP_NORSTR_SIZE];
- unsigned int authorised;
- unsigned int dummy = 0;
- int error;
-
- tokenlen = ast_base64decode(tokenstr, token, strlen(token));
- osp_convert_address(source, src, sizeof(src));
- osp_convert_address(destination, dst, sizeof(dst));
- error = OSPPTransactionValidateAuthorisation(
- transaction,
- src,
- dst,
- NULL,
- NULL,
- calling ? calling : "",
- OSPC_E164,
- called,
- OSPC_E164,
- 0,
- NULL,
- tokenlen,
- (char*)tokenstr,
- &authorised,
- timelimit,
- &dummy,
- NULL,
- osp_tokenformat);
- if (error != OSPC_ERR_NO_ERROR) {
- ast_debug(1, "OSP: Unable to validate inbound token\n");
- res = -1;
- } else if (authorised) {
- ast_debug(1, "OSP: Authorised\n");
- res = 1;
- } else {
- ast_debug(1, "OSP: Unauthorised\n");
- res = 0;
- }
-
- return res;
-}
-
-/*!
- * \brief Choose min duration limit
- * \param in Inbound duration limit
- * \param out Outbound duration limit
- * \return min duration limit
- */
-static unsigned int osp_choose_timelimit(
- unsigned int in,
- unsigned int out)
-{
- if (in == OSP_DEF_TIMELIMIT) {
- return out;
- } else if (out == OSP_DEF_TIMELIMIT) {
- return in;
- } else {
- return in < out ? in : out;
- }
-}
-
-/*!
- * \brief Choose min duration limit
- * \param provider OSP provider
- * \param called Called number
- * \param calling Calling number
- * \param destination Destination IP in '[x.x.x.x]' format
- * \param tokenlen OSP token length
- * \param token OSP token
- * \param reason Failure reason, output
- * \param result OSP lookup results, in/output
- * \return 1 Success, 0 Failed, -1 Error
- */
-static int osp_check_destination(
- struct osp_provider* provider,
- const char* called,
- const char* calling,
- char* destination,
- unsigned int tokenlen,
- const char* token,
- enum OSPEFAILREASON* reason,
- struct osp_result* result)
-{
- int res;
- OSPE_DEST_OSP_ENABLED enabled;
- OSPE_DEST_PROT protocol;
- int error;
-
- if (strlen(destination) <= 2) {
- ast_debug(1, "OSP: Wrong destination format '%s'\n", destination);
- *reason = OSPC_FAIL_NORMAL_UNSPECIFIED;
- return -1;
- }
-
- if ((error = OSPPTransactionIsDestOSPEnabled(result->outhandle, &enabled)) != OSPC_ERR_NO_ERROR) {
- ast_debug(1, "OSP: Unable to get destination OSP version, error '%d'\n", error);
- *reason = OSPC_FAIL_NORMAL_UNSPECIFIED;
- return -1;
- }
-
- if (enabled == OSPE_OSP_FALSE) {
- result->token[0] = '\0';
- } else {
- ast_base64encode(result->token, (const unsigned char*)token, tokenlen, sizeof(result->token) - 1);
- }
-
- if ((error = OSPPTransactionGetDestNetworkId(result->outhandle, result->networkid)) != OSPC_ERR_NO_ERROR) {
- ast_debug(1, "OSP: Unable to get destination network ID, error '%d'\n", error);
- result->networkid[0] = '\0';
- }
-
- if ((error = OSPPTransactionGetDestProtocol(result->outhandle, &protocol)) != OSPC_ERR_NO_ERROR) {
- ast_debug(1, "OSP: Unable to get destination protocol, error '%d'\n", error);
- *reason = OSPC_FAIL_NORMAL_UNSPECIFIED;
- result->token[0] = '\0';
- result->networkid[0] = '\0';
- return -1;
- }
-
- res = 1;
- /* Strip leading and trailing brackets */
- destination[strlen(destination) - 1] = '\0';
- switch(protocol) {
- case OSPE_DEST_PROT_H323_SETUP:
- ast_debug(1, "OSP: protocol '%s'\n", OSP_PROT_H323);
- ast_copy_string(result->tech, OSP_TECH_H323, sizeof(result->tech));
- ast_copy_string(result->dest, destination + 1, sizeof(result->dest));
- ast_copy_string(result->called, called, sizeof(result->called));
- ast_copy_string(result->calling, calling, sizeof(result->calling));
- break;
- case OSPE_DEST_PROT_SIP:
- ast_debug(1, "OSP: protocol '%s'\n", OSP_PROT_SIP);
- ast_copy_string(result->tech, OSP_TECH_SIP, sizeof(result->tech));
- ast_copy_string(result->dest, destination + 1, sizeof(result->dest));
- ast_copy_string(result->called, called, sizeof(result->called));
- ast_copy_string(result->calling, calling, sizeof(result->calling));
- break;
- case OSPE_DEST_PROT_IAX:
- ast_debug(1, "OSP: protocol '%s'\n", OSP_PROT_IAX);
- ast_copy_string(result->tech, OSP_TECH_IAX, sizeof(result->tech));
- ast_copy_string(result->dest, destination + 1, sizeof(result->dest));
- ast_copy_string(result->called, called, sizeof(result->called));
- ast_copy_string(result->calling, calling, sizeof(result->calling));
- break;
- case OSPE_DEST_PROT_UNDEFINED:
- case OSPE_DEST_PROT_UNKNOWN:
- ast_debug(1, "OSP: unknown/undefined protocol '%d'\n", protocol);
- ast_debug(1, "OSP: use default protocol '%s'\n", provider->defaultprotocol);
-
- ast_copy_string(result->tech, provider->defaultprotocol, sizeof(result->tech));
- ast_copy_string(result->dest, destination + 1, sizeof(result->dest));
- ast_copy_string(result->called, called, sizeof(result->called));
- ast_copy_string(result->calling, calling, sizeof(result->calling));
- break;
- case OSPE_DEST_PROT_H323_LRQ:
- default:
- ast_log(LOG_WARNING, "OSP: unsupported protocol '%d'\n", protocol);
- *reason = OSPC_FAIL_PROTOCOL_ERROR;
- result->token[0] = '\0';
- result->networkid[0] = '\0';
- res = 0;
- break;
- }
-
- return res;
-}
-
-/*!
- * \brief Convert Asterisk status to TC code
- * \param cause Asterisk hangup cause
- * \return OSP TC code
- */
-static enum OSPEFAILREASON asterisk2osp(
- int cause)
-{
- return (enum OSPEFAILREASON)cause;
-}
-
-/*!
- * \brief OSP Authentication function
- * \param provider OSP provider context name
- * \param transaction OSP transaction handle, output
- * \param source Source of inbound call
- * \param calling Calling number
- * \param called Called number
- * \param token OSP token, may be empty
- * \param timelimit Call duration limit, output
- * \return 1 Authenricated, 0 Unauthenticated, -1 Error
- */
-static int osp_auth(
- const char* provider,
- int* transaction,
- const char* source,
- const char* calling,
- const char* called,
- const char* token,
- unsigned int* timelimit)
-{
- int res;
- struct osp_provider* p;
- char dest[OSP_NORSTR_SIZE];
-
- *transaction = OSP_INVALID_HANDLE;
- *timelimit = OSP_DEF_TIMELIMIT;
-
- if ((res = osp_get_provider(provider, &p)) <= 0) {
- ast_debug(1, "OSP: Unabe to find OSP provider '%s'\n", provider);
- return res;
- }
-
- switch (p->authpolicy) {
- case OSP_AUTH_NO:
- res = 1;
- break;
- case OSP_AUTH_EXCLUSIVE:
- if (ast_strlen_zero(token)) {
- res = 0;
- } else if ((res = osp_create_transaction(provider, transaction, sizeof(dest), dest)) <= 0) {
- ast_debug(1, "OSP: Unable to generate transaction handle\n");
- *transaction = OSP_INVALID_HANDLE;
- res = 0;
- } else if((res = osp_validate_token(*transaction, source, dest, calling, called, token, timelimit)) <= 0) {
- OSPPTransactionRecordFailure(*transaction, OSPC_FAIL_CALL_REJECTED);
- }
- break;
- case OSP_AUTH_YES:
- default:
- if (ast_strlen_zero(token)) {
- res = 1;
- } else if ((res = osp_create_transaction(provider, transaction, sizeof(dest), dest)) <= 0) {
- ast_debug(1, "OSP: Unable to generate transaction handle\n");
- *transaction = OSP_INVALID_HANDLE;
- res = 0;
- } else if((res = osp_validate_token(*transaction, source, dest, calling, called, token, timelimit)) <= 0) {
- OSPPTransactionRecordFailure(*transaction, OSPC_FAIL_CALL_REJECTED);
- }
- break;
- }
-
- return res;
-}
-
-/*!
- * \brief Create a UUID
- * \param uuid UUID buffer
- * \param buffersize UUID buffer size
- * \return 1 Created, -1 Error
- */
-static int osp_create_uuid(
- unsigned char* uuid,
- unsigned int* buffersize)
-{
- int i, res;
- long int* tmp;
-
- if (*buffersize >= OSP_UUID_SIZE) {
- tmp = (long int*)uuid;
- for (i = 0; i < OSP_UUID_SIZE / sizeof(long int); i++) {
- tmp[i] = ast_random();
- }
- *buffersize = OSP_UUID_SIZE;
- res = 1;
- } else {
- res = -1;
- }
-
- return res;
-}
-
-/*!
- * \brief UUID to string
- * \param uuid UUID
- * \param buffer String buffer
- * \param buffersize String buffer size
- * \return 1 Successed, -1 Error
- */
-static int osp_uuid2str(
- unsigned char* uuid,
- char* buffer,
- unsigned int buffersize)
-{
- int res;
-
- if (buffersize > OSP_UUIDSTR_SIZE) {
- snprintf(buffer, buffersize, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
- uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
- uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
- res = 1;
- } else {
- res = -1;
- }
-
- return res;
-}
-
-/*!
- * \brief Create a call ID according to the type
- * \param type Call ID type
- * \param callid Call ID buffer
- * \return 1 Created, 0 Not create, -1 Error
- */
-static int osp_create_callid(
- unsigned int type,
- struct osp_callid* callid)
-{
- int res;
-
- callid->len = sizeof(callid->buf);
- switch (type) {
- case OSP_CALLID_H323:
- res = osp_create_uuid(callid->buf, &callid->len);
- break;
- case OSP_CALLID_SIP:
- case OSP_CALLID_IAX:
- res = 0;
- default:
- res = -1;
- break;
- }
-
- if ((res != 1) && (callid->len != 0)) {
- callid->buf[0] = '\0';
- callid->len = 0;
- }
-
- return res;
-}
-
-/*!
- * \brief OSP Lookup function
- * \param provider OSP provider context name
- * \param srcdev Source device of outbound call
- * \param calling Calling number
- * \param called Called number
- * \param callidtypes Call ID types
- * \param result Lookup results
- * \return 1 Found , 0 No route, -1 Error
- */
-static int osp_lookup(
- const char* provider,
- const char* srcdev,
- const char* calling,
- const char* called,
- unsigned int callidtypes,
- struct osp_result* result)
-{
- int res;
- struct osp_provider* p;
- char source[OSP_NORSTR_SIZE];
- char callingnum[OSP_NORSTR_SIZE];
- char callednum[OSP_NORSTR_SIZE];
- char destination[OSP_NORSTR_SIZE];
- unsigned int tokenlen;
- char token[OSP_TOKSTR_SIZE];
- char src[OSP_NORSTR_SIZE];
- char dev[OSP_NORSTR_SIZE];
- unsigned int i, type;
- struct osp_callid callid;
- unsigned int callidnum;
- OSPTCALLID* callids[OSP_CALLID_MAXNUM];
- unsigned int dummy = 0;
- enum OSPEFAILREASON reason;
- int error;
-
- result->outhandle = OSP_INVALID_HANDLE;
- result->tech[0] = '\0';
- result->dest[0] = '\0';
- result->called[0] = '\0';
- result->calling[0] = '\0';
- result->token[0] = '\0';
- result->networkid[0] = '\0';
- result->numresults = 0;
- result->outtimelimit = OSP_DEF_TIMELIMIT;
-
- if ((res = osp_get_provider(provider, &p)) <= 0) {
- ast_debug(1, "OSP: Unabe to find OSP provider '%s'\n", provider);
- return res;
- }
-
- if ((res = osp_create_transaction(provider, &result->outhandle, sizeof(source), source)) <= 0) {
- ast_debug(1, "OSP: Unable to generate transaction handle\n");
- result->outhandle = OSP_INVALID_HANDLE;
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NORMAL_UNSPECIFIED);
- }
- return -1;
- }
-
- callidnum = 0;
- callids[0] = NULL;
- for (i = 0; i < OSP_CALLID_MAXNUM; i++) {
- type = 1 << i;
- if (callidtypes & type) {
- error = osp_create_callid(type, &callid);
- if (error == 1) {
- callids[callidnum] = OSPPCallIdNew(callid.len, callid.buf);
- callidnum++;
- }
- }
- }
-
- osp_convert_address(source, src, sizeof(src));
- osp_convert_address(srcdev, dev, sizeof(dev));
- result->numresults = OSP_DEF_DESTINATIONS;
- error = OSPPTransactionRequestAuthorisation(
- result->outhandle,
- src,
- dev,
- calling ? calling : "",
- OSPC_E164,
- called,
- OSPC_E164,
- NULL,
- callidnum,
- callids,
- NULL,
- &result->numresults,
- &dummy,
- NULL);
-
- for (i = 0; i < callidnum; i++) {
- OSPPCallIdDelete(&callids[i]);
- }
-
- if (error != OSPC_ERR_NO_ERROR) {
- ast_debug(1, "OSP: Unable to request authorization\n");
- result->numresults = 0;
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NORMAL_UNSPECIFIED);
- }
- return -1;
- }
-
- if (!result->numresults) {
- ast_debug(1, "OSP: No more destination\n");
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
- }
- return 0;
- }
-
- result->outcallid.len = sizeof(result->outcallid.buf);
- tokenlen = sizeof(token);
- error = OSPPTransactionGetFirstDestination(
- result->outhandle,
- 0,
- NULL,
- NULL,
- &result->outtimelimit,
- &result->outcallid.len,
- result->outcallid.buf,
- sizeof(callednum),
- callednum,
- sizeof(callingnum),
- callingnum,
- sizeof(destination),
- destination,
- 0,
- NULL,
- &tokenlen,
- token);
- if (error != OSPC_ERR_NO_ERROR) {
- ast_debug(1, "OSP: Unable to get first route\n");
- result->numresults = 0;
- result->outtimelimit = OSP_DEF_TIMELIMIT;
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
- }
- return -1;
- }
-
- result->numresults--;
- result->outtimelimit = osp_choose_timelimit(result->intimelimit, result->outtimelimit);
- ast_debug(1, "OSP: outtimelimit '%d'\n", result->outtimelimit);
- ast_debug(1, "OSP: called '%s'\n", callednum);
- ast_debug(1, "OSP: calling '%s'\n", callingnum);
- ast_debug(1, "OSP: destination '%s'\n", destination);
- ast_debug(1, "OSP: token size '%d'\n", tokenlen);
-
- if ((res = osp_check_destination(p, callednum, callingnum, destination, tokenlen, token, &reason, result)) > 0) {
- return 1;
- }
-
- if (!result->numresults) {
- ast_debug(1, "OSP: No more destination\n");
- result->outtimelimit = OSP_DEF_TIMELIMIT;
- OSPPTransactionRecordFailure(result->outhandle, reason);
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
- }
- return 0;
- }
-
- while(result->numresults) {
- result->outcallid.len = sizeof(result->outcallid.buf);
- tokenlen = sizeof(token);
- error = OSPPTransactionGetNextDestination(
- result->outhandle,
- reason,
- 0,
- NULL,
- NULL,
- &result->outtimelimit,
- &result->outcallid.len,
- result->outcallid.buf,
- sizeof(callednum),
- callednum,
- sizeof(callingnum),
- callingnum,
- sizeof(destination),
- destination,
- 0,
- NULL,
- &tokenlen,
- token);
- if (error == OSPC_ERR_NO_ERROR) {
- result->numresults--;
- result->outtimelimit = osp_choose_timelimit(result->intimelimit, result->outtimelimit);
- ast_debug(1, "OSP: outtimelimit '%d'\n", result->outtimelimit);
- ast_debug(1, "OSP: called '%s'\n", callednum);
- ast_debug(1, "OSP: calling '%s'\n", callingnum);
- ast_debug(1, "OSP: destination '%s'\n", destination);
- ast_debug(1, "OSP: token size '%d'\n", tokenlen);
-
- if ((res = osp_check_destination(p, callednum, callingnum, destination, tokenlen, token, &reason, result)) > 0) {
- break;
- } else if (!result->numresults) {
- ast_debug(1, "OSP: No more destination\n");
- OSPPTransactionRecordFailure(result->outhandle, reason);
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
- }
- res = 0;
- break;
- }
- } else {
- ast_debug(1, "OSP: Unable to get route, error '%d'\n", error);
- result->numresults = 0;
- result->outtimelimit = OSP_DEF_TIMELIMIT;
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NORMAL_UNSPECIFIED);
- }
- res = -1;
- break;
- }
- }
- return res;
-}
-
-/*!
- * \brief OSP Lookup Next function
- * \param provider OSP provider name
- * \param cause Asterisk hangup cuase
- * \param result Lookup results, in/output
- * \return 1 Found , 0 No route, -1 Error
- */
-static int osp_next(
- const char* provider,
- int cause,
- struct osp_result* result)
-{
- int res;
- struct osp_provider* p;
- char callingnum[OSP_NORSTR_SIZE];
- char callednum[OSP_NORSTR_SIZE];
- char destination[OSP_NORSTR_SIZE];
- unsigned int tokenlen;
- char token[OSP_TOKSTR_SIZE];
- enum OSPEFAILREASON reason;
- int error;
-
- result->tech[0] = '\0';
- result->dest[0] = '\0';
- result->called[0] = '\0';
- result->calling[0] = '\0';
- result->token[0] = '\0';
- result->networkid[0] = '\0';
- result->outtimelimit = OSP_DEF_TIMELIMIT;
-
- if ((res = osp_get_provider(provider, &p)) <= 0) {
- ast_debug(1, "OSP: Unabe to find OSP provider '%s'\n", provider);
- return res;
- }
-
- if (result->outhandle == OSP_INVALID_HANDLE) {
- ast_debug(1, "OSP: Transaction handle undefined\n");
- result->numresults = 0;
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NORMAL_UNSPECIFIED);
- }
- return -1;
- }
-
- reason = asterisk2osp(cause);
-
- if (!result->numresults) {
- ast_debug(1, "OSP: No more destination\n");
- OSPPTransactionRecordFailure(result->outhandle, reason);
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
- }
- return 0;
- }
-
- while(result->numresults) {
- result->outcallid.len = sizeof(result->outcallid.buf);
- tokenlen = sizeof(token);
- error = OSPPTransactionGetNextDestination(
- result->outhandle,
- reason,
- 0,
- NULL,
- NULL,
- &result->outtimelimit,
- &result->outcallid.len,
- result->outcallid.buf,
- sizeof(callednum),
- callednum,
- sizeof(callingnum),
- callingnum,
- sizeof(destination),
- destination,
- 0,
- NULL,
- &tokenlen,
- token);
- if (error == OSPC_ERR_NO_ERROR) {
- result->numresults--;
- result->outtimelimit = osp_choose_timelimit(result->intimelimit, result->outtimelimit);
- ast_debug(1, "OSP: outtimelimit '%d'\n", result->outtimelimit);
- ast_debug(1, "OSP: called '%s'\n", callednum);
- ast_debug(1, "OSP: calling '%s'\n", callingnum);
- ast_debug(1, "OSP: destination '%s'\n", destination);
- ast_debug(1, "OSP: token size '%d'\n", tokenlen);
-
- if ((res = osp_check_destination(p, callednum, callingnum, destination, tokenlen, token, &reason, result)) > 0) {
- res = 1;
- break;
- } else if (!result->numresults) {
- ast_debug(1, "OSP: No more destination\n");
- OSPPTransactionRecordFailure(result->outhandle, reason);
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
- }
- res = 0;
- break;
- }
- } else {
- ast_debug(1, "OSP: Unable to get route, error '%d'\n", error);
- result->token[0] = '\0';
- result->numresults = 0;
- result->outtimelimit = OSP_DEF_TIMELIMIT;
- if (result->inhandle != OSP_INVALID_HANDLE) {
- OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NORMAL_UNSPECIFIED);
- }
- res = -1;
- break;
- }
- }
-
- return res;
-}
-
-/*!
- * \brief OSP Finish function
- * \param handle OSP in/outbound transaction handle
- * \param recorded If failure reason has been recorded
- * \param cause Asterisk hangup cause
- * \param start Call start time
- * \param connect Call connect time
- * \param end Call end time
- * \param release Who release first, 0 source, 1 destination
- * \return 1 Success, 0 Failed, -1 Error
- */
-static int osp_finish(
- int handle,
- int recorded,
- int cause,
- time_t start,
- time_t connect,
- time_t end,
- unsigned int release)
-{
- int res;
- enum OSPEFAILREASON reason;
- time_t alert = 0;
- unsigned isPddInfoPresent = 0;
- unsigned pdd = 0;
- unsigned int dummy = 0;
- int error;
-
- if (handle == OSP_INVALID_HANDLE) {
- return 0;
- }
-
- if (!recorded) {
- reason = asterisk2osp(cause);
- OSPPTransactionRecordFailure(handle, reason);
- }
-
- error = OSPPTransactionReportUsage(
- handle,
- difftime(end, connect),
- start,
- end,
- alert,
- connect,
- isPddInfoPresent,
- pdd,
- release,
- (unsigned char*)"",
- 0,
- 0,
- 0,
- 0,
- &dummy,
- NULL);
- if (error == OSPC_ERR_NO_ERROR) {
- ast_debug(1, "OSP: Usage reported\n");
- res = 1;
- } else {
- ast_debug(1, "OSP: Unable to report usage, error '%d'\n", error);
- res = -1;
- }
- OSPPTransactionDelete(handle);
-
- return res;
-}
-
-/* OSP Application APIs */
-
-/*!
- * \brief OSP Application OSPAuth
- * \param chan Channel
- * \param data Parameter
- * \return 0 Success, -1 Failed
- */
-static int ospauth_exec(
- struct ast_channel* chan,
- void* data)
-{
- int res;
- const char* provider = OSP_DEF_PROVIDER;
- struct varshead* headp;
- struct ast_var_t* current;
- const char* source = "";
- const char* token = "";
- int handle;
- unsigned int timelimit;
- char buffer[OSP_INTSTR_SIZE];
- const char* status;
- char* tmp;
-
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(provider);
- AST_APP_ARG(options);
- );
-
- if (!(tmp = ast_strdupa(data))) {
- ast_log(LOG_ERROR, "Out of memory\n");
- return -1;
- }
-
- AST_STANDARD_APP_ARGS(args, tmp);
-
- if (!ast_strlen_zero(args.provider)) {
- provider = args.provider;
- }
- ast_debug(1, "OSPAuth: provider '%s'\n", provider);
-
- headp = &chan->varshead;
- AST_LIST_TRAVERSE(headp, current, entries) {
- if (!strcasecmp(ast_var_name(current), "OSPPEERIP")) {
- source = ast_var_value(current);
- } else if (!strcasecmp(ast_var_name(current), "OSPINTOKEN")) {
- token = ast_var_value(current);
- }
- }
-
- ast_debug(1, "OSPAuth: source '%s'\n", source);
- ast_debug(1, "OSPAuth: token size '%zd'\n", strlen(token));
-
- if ((res = osp_auth(provider, &handle, source, chan->cid.cid_num, chan->exten, token, &timelimit)) > 0) {
- status = AST_OSP_SUCCESS;
- } else {
- timelimit = OSP_DEF_TIMELIMIT;
- if (!res) {
- status = AST_OSP_FAILED;
- } else {
- status = AST_OSP_ERROR;
- }
- }
-
- snprintf(buffer, sizeof(buffer), "%d", handle);
- pbx_builtin_setvar_helper(chan, "OSPINHANDLE", buffer);
- ast_debug(1, "OSPAuth: OSPINHANDLE '%s'\n", buffer);
- snprintf(buffer, sizeof(buffer), "%d", timelimit);
- pbx_builtin_setvar_helper(chan, "OSPINTIMELIMIT", buffer);
- ast_debug(1, "OSPAuth: OSPINTIMELIMIT '%s'\n", buffer);
- pbx_builtin_setvar_helper(chan, "OSPAUTHSTATUS", status);
- ast_debug(1, "OSPAuth: %s\n", status);
-
- if(res <= 0) {
- res = -1;
- } else {
- res = 0;
- }
-
- return res;
-}
-
-/*!
- * \brief OSP Application OSPLookup
- * \param chan Channel
- * \param data Parameter
- * \return 0 Success, -1 Failed
- */
-static int osplookup_exec(
- struct ast_channel* chan,
- void* data)
-{
- int res, cres;
- const char* provider = OSP_DEF_PROVIDER;
- struct varshead* headp;
- struct ast_var_t* current;
- const char* srcdev = "";
- const char* netid = "";
- char buffer[OSP_TOKSTR_SIZE];
- unsigned int callidtypes = OSP_CALLID_UNDEFINED;
- struct osp_result result;
- const char* status;
- char* tmp;
-
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(exten);
- AST_APP_ARG(provider);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "OSPLookup: Arg required, OSPLookup(exten[|provider[|options]])\n");
- return -1;
- }
-
- if (!(tmp = ast_strdupa(data))) {
- ast_log(LOG_ERROR, "Out of memory\n");
- return -1;
- }
-
- AST_STANDARD_APP_ARGS(args, tmp);
-
- ast_debug(1, "OSPLookup: exten '%s'\n", args.exten);
-
- if (!ast_strlen_zero(args.provider)) {
- provider = args.provider;
- }
- ast_debug(1, "OSPlookup: provider '%s'\n", provider);
-
- if (args.options) {
- if (strchr(args.options, 'h')) {
- callidtypes |= OSP_CALLID_H323;
- }
- if (strchr(args.options, 's')) {
- callidtypes |= OSP_CALLID_SIP;
- }
- if (strchr(args.options, 'i')) {
- callidtypes |= OSP_CALLID_IAX;
- }
- }
- ast_debug(1, "OSPLookup: call id types '%d'\n", callidtypes);
-
- result.inhandle = OSP_INVALID_HANDLE;
- result.intimelimit = OSP_DEF_TIMELIMIT;
-
- headp = &chan->varshead;
- AST_LIST_TRAVERSE(headp, current, entries) {
- if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
- if (sscanf(ast_var_value(current), "%d", &result.inhandle) != 1) {
- result.inhandle = OSP_INVALID_HANDLE;
- }
- } else if (!strcasecmp(ast_var_name(current), "OSPINTIMELIMIT")) {
- if (sscanf(ast_var_value(current), "%d", &result.intimelimit) != 1) {
- result.intimelimit = OSP_DEF_TIMELIMIT;
- }
- } else if (!strcasecmp(ast_var_name(current), "OSPINNETWORKID")) {
- netid = ast_var_value(current);
- } else if (!strcasecmp(ast_var_name(current), "OSPPEERIP")) {
- srcdev = ast_var_value(current);
- }
- }
- ast_debug(1, "OSPLookup: OSPINHANDLE '%d'\n", result.inhandle);
- ast_debug(1, "OSPLookup: OSPINTIMELIMIT '%d'\n", result.intimelimit);
- ast_debug(1, "OSPLookup: OSPINNETWORKID '%s'\n", netid);
- ast_debug(1, "OSPLookup: source device '%s'\n", srcdev);
-
- if ((cres = ast_autoservice_start(chan)) < 0) {
- return -1;
- }
-
- if ((res = osp_lookup(provider, srcdev, chan->cid.cid_num, args.exten, callidtypes, &result)) > 0) {
- status = AST_OSP_SUCCESS;
- } else {
- result.tech[0] = '\0';
- result.dest[0] = '\0';
- result.called[0] = '\0';
- result.calling[0] = '\0';
- result.token[0] = '\0';
- result.networkid[0] = '\0';
- result.numresults = 0;
- result.outtimelimit = OSP_DEF_TIMELIMIT;
- result.outcallid.buf[0] = '\0';
- result.outcallid.len = 0;
- if (!res) {
- status = AST_OSP_FAILED;
- } else {
- status = AST_OSP_ERROR;
- }
- }
-
- snprintf(buffer, sizeof(buffer), "%d", result.outhandle);
- pbx_builtin_setvar_helper(chan, "OSPOUTHANDLE", buffer);
- ast_debug(1, "OSPLookup: OSPOUTHANDLE '%s'\n", buffer);
- pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech);
- ast_debug(1, "OSPLookup: OSPTECH '%s'\n", result.tech);
- pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest);
- ast_debug(1, "OSPLookup: OSPDEST '%s'\n", result.dest);
- pbx_builtin_setvar_helper(chan, "OSPCALLED", result.called);
- ast_debug(1, "OSPLookup: OSPCALLED '%s'\n", result.called);
- pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling);
- ast_debug(1, "OSPLookup: OSPCALLING '%s'\n", result.calling);
- pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token);
- ast_debug(1, "OSPLookup: OSPOUTTOKEN size '%zd'\n", strlen(result.token));
- snprintf(buffer, sizeof(buffer), "%d", result.numresults);
- pbx_builtin_setvar_helper(chan, "OSPRESULTS", buffer);
- ast_debug(1, "OSPLookup: OSPRESULTS '%s'\n", buffer);
- snprintf(buffer, sizeof(buffer), "%d", result.outtimelimit);
- pbx_builtin_setvar_helper(chan, "OSPOUTTIMELIMIT", buffer);
- ast_debug(1, "OSPLookup: OSPOUTTIMELIMIT '%s'\n", buffer);
- snprintf(buffer, sizeof(buffer), "%d", callidtypes);
- pbx_builtin_setvar_helper(chan, "OSPOUTCALLIDTYPES", buffer);
- ast_debug(1, "OSPLookup: OSPOUTCALLIDTYPES '%s'\n", buffer);
- pbx_builtin_setvar_helper(chan, "OSPLOOKUPSTATUS", status);
- ast_debug(1, "OSPLookup: %s\n", status);
-
- if (!strcasecmp(result.tech, OSP_TECH_H323)) {
- if ((callidtypes & OSP_CALLID_H323) && (result.outcallid.len != 0)) {
- osp_uuid2str(result.outcallid.buf, buffer, sizeof(buffer));
- } else {
- buffer[0] = '\0';
- }
- pbx_builtin_setvar_helper(chan, "OSPOUTCALLID", buffer);
- snprintf(buffer, sizeof(buffer), "%s/%s@%s", result.tech, result.called, result.dest);
- pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
- } else if (!strcasecmp(result.tech, OSP_TECH_SIP)) {
- snprintf(buffer, sizeof(buffer), "%s/%s@%s", result.tech, result.called, result.dest);
- pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
- if (!ast_strlen_zero(result.token)) {
- snprintf(buffer, sizeof(buffer), "%s%s", OSP_SIP_HEADER, result.token);
- pbx_builtin_setvar_helper(chan, "_SIPADDHEADER", buffer);
- ast_debug(1, "OSPLookup: SIPADDHEADER size '%zd'\n", strlen(buffer));
- }
- } else if (!strcasecmp(result.tech, OSP_TECH_IAX)) {
- snprintf(buffer, sizeof(buffer), "%s/%s/%s", result.tech, result.dest, result.called);
- pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
- }
-
- if ((cres = ast_autoservice_stop(chan)) < 0) {
- return -1;
- }
-
- if(res <= 0) {
- res = -1;
- } else {
- res = 0;
- }
-
- return res;
-}
-
-/*!
- * \brief OSP Application OSPNext
- * \param chan Channel
- * \param data Parameter
- * \return 0 Success, -1 Failed
- */
-static int ospnext_exec(
- struct ast_channel* chan,
- void* data)
-{
- int res;
- const char* provider = OSP_DEF_PROVIDER;
- int cause = 0;
- struct varshead* headp;
- struct ast_var_t* current;
- struct osp_result result;
- char buffer[OSP_TOKSTR_SIZE];
- unsigned int callidtypes = OSP_CALLID_UNDEFINED;
- const char* status;
- char* tmp;
-
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(cause);
- AST_APP_ARG(provider);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "OSPNext: Arg required, OSPNext(cause[|provider[|options]])\n");
- return -1;
- }
-
- if (!(tmp = ast_strdupa(data))) {
- ast_log(LOG_ERROR, "Out of memory\n");
- return -1;
- }
-
- AST_STANDARD_APP_ARGS(args, tmp);
-
- if (!ast_strlen_zero(args.cause) && sscanf(args.cause, "%d", &cause) != 1) {
- cause = 0;
- }
- ast_debug(1, "OSPNext: cause '%d'\n", cause);
-
- if (!ast_strlen_zero(args.provider)) {
- provider = args.provider;
- }
- ast_debug(1, "OSPlookup: provider '%s'\n", provider);
-
- result.inhandle = OSP_INVALID_HANDLE;
- result.outhandle = OSP_INVALID_HANDLE;
- result.intimelimit = OSP_DEF_TIMELIMIT;
- result.numresults = 0;
-
- headp = &chan->varshead;
- AST_LIST_TRAVERSE(headp, current, entries) {
- if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
- if (sscanf(ast_var_value(current), "%d", &result.inhandle) != 1) {
- result.inhandle = OSP_INVALID_HANDLE;
- }
- } else if (!strcasecmp(ast_var_name(current), "OSPOUTHANDLE")) {
- if (sscanf(ast_var_value(current), "%d", &result.outhandle) != 1) {
- result.outhandle = OSP_INVALID_HANDLE;
- }
- } else if (!strcasecmp(ast_var_name(current), "OSPINTIMELIMIT")) {
- if (sscanf(ast_var_value(current), "%d", &result.intimelimit) != 1) {
- result.intimelimit = OSP_DEF_TIMELIMIT;
- }
- } else if (!strcasecmp(ast_var_name(current), "OSPOUTCALLIDTYPES")) {
- if (sscanf(ast_var_value(current), "%d", &callidtypes) != 1) {
- callidtypes = OSP_CALLID_UNDEFINED;
- }
- } else if (!strcasecmp(ast_var_name(current), "OSPRESULTS")) {
- if (sscanf(ast_var_value(current), "%d", &result.numresults) != 1) {
- result.numresults = 0;
- }
- }
- }
- ast_debug(1, "OSPNext: OSPINHANDLE '%d'\n", result.inhandle);
- ast_debug(1, "OSPNext: OSPOUTHANDLE '%d'\n", result.outhandle);
- ast_debug(1, "OSPNext: OSPINTIMELIMIT '%d'\n", result.intimelimit);
- ast_debug(1, "OSPNext: OSPOUTCALLIDTYPES '%d'\n", callidtypes);
- ast_debug(1, "OSPNext: OSPRESULTS '%d'\n", result.numresults);
-
- if ((res = osp_next(provider, cause, &result)) > 0) {
- status = AST_OSP_SUCCESS;
- } else {
- result.tech[0] = '\0';
- result.dest[0] = '\0';
- result.called[0] = '\0';
- result.calling[0] = '\0';
- result.token[0] = '\0';
- result.networkid[0] = '\0';
- result.numresults = 0;
- result.outtimelimit = OSP_DEF_TIMELIMIT;
- result.outcallid.buf[0] = '\0';
- result.outcallid.len = 0;
- if (!res) {
- status = AST_OSP_FAILED;
- } else {
- status = AST_OSP_ERROR;
- }
- }
-
- pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech);
- ast_debug(1, "OSPNext: OSPTECH '%s'\n", result.tech);
- pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest);
- ast_debug(1, "OSPNext: OSPDEST '%s'\n", result.dest);
- pbx_builtin_setvar_helper(chan, "OSPCALLED", result.called);
- ast_debug(1, "OSPNext: OSPCALLED'%s'\n", result.called);
- pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling);
- ast_debug(1, "OSPNext: OSPCALLING '%s'\n", result.calling);
- pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token);
- ast_debug(1, "OSPNext: OSPOUTTOKEN size '%zd'\n", strlen(result.token));
- snprintf(buffer, sizeof(buffer), "%d", result.numresults);
- pbx_builtin_setvar_helper(chan, "OSPRESULTS", buffer);
- ast_debug(1, "OSPNext: OSPRESULTS '%s'\n", buffer);
- snprintf(buffer, sizeof(buffer), "%d", result.outtimelimit);
- pbx_builtin_setvar_helper(chan, "OSPOUTTIMELIMIT", buffer);
- ast_debug(1, "OSPNext: OSPOUTTIMELIMIT '%s'\n", buffer);
- pbx_builtin_setvar_helper(chan, "OSPNEXTSTATUS", status);
- ast_debug(1, "OSPNext: %s\n", status);
-
- if (!strcasecmp(result.tech, OSP_TECH_H323)) {
- if ((callidtypes & OSP_CALLID_H323) && (result.outcallid.len != 0)) {
- osp_uuid2str(result.outcallid.buf, buffer, sizeof(buffer));
- } else {
- buffer[0] = '\0';
- }
- pbx_builtin_setvar_helper(chan, "OSPOUTCALLID", buffer);
- snprintf(buffer, sizeof(buffer), "%s/%s@%s", result.tech, result.called, result.dest);
- pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
- } else if (!strcasecmp(result.tech, OSP_TECH_SIP)) {
- snprintf(buffer, sizeof(buffer), "%s/%s@%s", result.tech, result.called, result.dest);
- pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
- if (!ast_strlen_zero(result.token)) {
- snprintf(buffer, sizeof(buffer), "%s%s", OSP_SIP_HEADER, result.token);
- pbx_builtin_setvar_helper(chan, "_SIPADDHEADER", buffer);
- ast_debug(1, "OSPLookup: SIPADDHEADER size '%zd'\n", strlen(buffer));
- }
- } else if (!strcasecmp(result.tech, OSP_TECH_IAX)) {
- snprintf(buffer, sizeof(buffer), "%s/%s/%s", result.tech, result.dest, result.called);
- pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
- }
-
- if(res <= 0) {
- res = -1;
- } else {
- res = 0;
- }
-
- return res;
-}
-
-/*!
- * \brief OSP Application OSPFinish
- * \param chan Channel
- * \param data Parameter
- * \return 0 Success, -1 Failed
- */
-static int ospfinished_exec(
- struct ast_channel* chan,
- void* data)
-{
- int res = 1;
- int cause = 0;
- struct varshead* headp;
- struct ast_var_t* current;
- int inhandle = OSP_INVALID_HANDLE;
- int outhandle = OSP_INVALID_HANDLE;
- int recorded = 0;
- time_t start, connect, end;
- unsigned int release;
- char buffer[OSP_INTSTR_SIZE];
- const char* status;
- char* tmp;
-
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(cause);
- AST_APP_ARG(options);
- );
-
- if (!(tmp = ast_strdupa(data))) {
- ast_log(LOG_ERROR, "Out of memory\n");
- return -1;
- }
-
- AST_STANDARD_APP_ARGS(args, tmp);
-
- headp = &chan->varshead;
- AST_LIST_TRAVERSE(headp, current, entries) {
- if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
- if (sscanf(ast_var_value(current), "%d", &inhandle) != 1) {
- inhandle = OSP_INVALID_HANDLE;
- }
- } else if (!strcasecmp(ast_var_name(current), "OSPOUTHANDLE")) {
- if (sscanf(ast_var_value(current), "%d", &outhandle) != 1) {
- outhandle = OSP_INVALID_HANDLE;
- }
- } else if (!recorded &&
- (!strcasecmp(ast_var_name(current), "OSPAUTHSTATUS") ||
- !strcasecmp(ast_var_name(current), "OSPLOOKUPSTATUS") ||
- !strcasecmp(ast_var_name(current), "OSPNEXTSTATUS")))
- {
- if (strcasecmp(ast_var_value(current), AST_OSP_SUCCESS)) {
- recorded = 1;
- }
- }
- }
- ast_debug(1, "OSPFinish: OSPINHANDLE '%d'\n", inhandle);
- ast_debug(1, "OSPFinish: OSPOUTHANDLE '%d'\n", outhandle);
- ast_debug(1, "OSPFinish: recorded '%d'\n", recorded);
-
- if (!ast_strlen_zero(args.cause) && sscanf(args.cause, "%d", &cause) != 1) {
- cause = 0;
- }
- ast_debug(1, "OSPFinish: cause '%d'\n", cause);
-
- if (chan->cdr) {
- start = chan->cdr->start.tv_sec;
- connect = chan->cdr->answer.tv_sec;
- if (connect) {
- end = time(NULL);
- } else {
- end = connect;
- }
- } else {
- start = 0;
- connect = 0;
- end = 0;
- }
- ast_debug(1, "OSPFinish: start '%ld'\n", start);
- ast_debug(1, "OSPFinish: connect '%ld'\n", connect);
- ast_debug(1, "OSPFinish: end '%ld'\n", end);
-
- release = ast_check_hangup(chan) ? 0 : 1;
-
- if (osp_finish(outhandle, recorded, cause, start, connect, end, release) <= 0) {
- ast_debug(1, "OSPFinish: Unable to report usage for outbound call\n");
- }
- switch (cause) {
- case AST_CAUSE_NORMAL_CLEARING:
- break;
- default:
- cause = AST_CAUSE_NO_ROUTE_DESTINATION;
- break;
- }
- if (osp_finish(inhandle, recorded, cause, start, connect, end, release) <= 0) {
- ast_debug(1, "OSPFinish: Unable to report usage for inbound call\n");
- }
- snprintf(buffer, sizeof(buffer), "%d", OSP_INVALID_HANDLE);
- pbx_builtin_setvar_helper(chan, "OSPOUTHANDLE", buffer);
- pbx_builtin_setvar_helper(chan, "OSPINHANDLE", buffer);
-
- if (res > 0) {
- status = AST_OSP_SUCCESS;
- } else if (!res) {
- status = AST_OSP_FAILED;
- } else {
- status = AST_OSP_ERROR;
- }
- pbx_builtin_setvar_helper(chan, "OSPFINISHSTATUS", status);
-
- if(!res) {
- res = -1;
- } else {
- res = 0;
- }
-
- return res;
-}
-
-/* OSP Module APIs */
-
-static int osp_unload(void);
-static int osp_load(int reload)
-{
- const char* t;
- unsigned int v;
- struct ast_config* cfg;
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
- int error = OSPC_ERR_NO_ERROR;
-
- if ((cfg = ast_config_load(OSP_CONFIG_FILE, config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
-
- if (cfg) {
- if (reload)
- osp_unload();
-
- t = ast_variable_retrieve(cfg, OSP_GENERAL_CAT, "accelerate");
- if (t && ast_true(t)) {
- if ((error = OSPPInit(1)) != OSPC_ERR_NO_ERROR) {
- ast_log(LOG_WARNING, "OSP: Unable to enable hardware accelleration\n");
- OSPPInit(0);
- } else {
- osp_hardware = 1;
- }
- } else {
- OSPPInit(0);
- }
- ast_debug(1, "OSP: osp_hardware '%d'\n", osp_hardware);
-
- t = ast_variable_retrieve(cfg, OSP_GENERAL_CAT, "tokenformat");
- if (t) {
- if ((sscanf(t, "%d", &v) == 1) &&
- ((v == TOKEN_ALGO_SIGNED) || (v == TOKEN_ALGO_UNSIGNED) || (v == TOKEN_ALGO_BOTH)))
- {
- osp_tokenformat = v;
- } else {
- ast_log(LOG_WARNING, "tokenformat should be an integer from %d, %d or %d, not '%s'\n",
- TOKEN_ALGO_SIGNED, TOKEN_ALGO_UNSIGNED, TOKEN_ALGO_BOTH, t);
- }
- }
- ast_debug(1, "OSP: osp_tokenformat '%d'\n", osp_tokenformat);
-
- t = ast_category_browse(cfg, NULL);
- while(t) {
- if (strcasecmp(t, OSP_GENERAL_CAT)) {
- osp_create_provider(cfg, t);
- }
- t = ast_category_browse(cfg, t);
- }
-
- osp_initialized = 1;
-
- ast_config_destroy(cfg);
- } else {
- ast_log(LOG_WARNING, "OSP: Unable to find configuration. OSP support disabled\n");
- return 0;
- }
- ast_debug(1, "OSP: osp_initialized '%d'\n", osp_initialized);
-
- return 1;
-}
-
-static int osp_unload(void)
-{
- struct osp_provider* p;
- struct osp_provider* next;
-
- if (osp_initialized) {
- ast_mutex_lock(&osplock);
- p = ospproviders;
- while(p) {
- next = p->next;
- OSPPProviderDelete(p->handle, 0);
- ast_free(p);
- p = next;
- }
- ospproviders = NULL;
- ast_mutex_unlock(&osplock);
-
- OSPPCleanup();
-
- osp_tokenformat = TOKEN_ALGO_SIGNED;
- osp_hardware = 0;
- osp_initialized = 0;
- }
- return 0;
-}
-
-static char *handle_cli_osp_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- int i;
- int found = 0;
- struct osp_provider* p;
- const char* provider = NULL;
- const char* tokenalgo;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "osp show";
- e->usage =
- "Usage: osp show\n"
- " Displays information on Open Settlement Protocol support\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if ((a->argc < 2) || (a->argc > 3))
- return CLI_SHOWUSAGE;
- if (a->argc > 2)
- provider = a->argv[2];
- if (!provider) {
- switch (osp_tokenformat) {
- case TOKEN_ALGO_BOTH:
- tokenalgo = "Both";
- break;
- case TOKEN_ALGO_UNSIGNED:
- tokenalgo = "Unsigned";
- break;
- case TOKEN_ALGO_SIGNED:
- default:
- tokenalgo = "Signed";
- break;
- }
- ast_cli(a->fd, "OSP: %s %s %s\n",
- osp_initialized ? "Initialized" : "Uninitialized", osp_hardware ? "Accelerated" : "Normal", tokenalgo);
- }
-
- ast_mutex_lock(&osplock);
- p = ospproviders;
- while(p) {
- if (!provider || !strcasecmp(p->name, provider)) {
- if (found) {
- ast_cli(a->fd, "\n");
- }
- ast_cli(a->fd, " == OSP Provider '%s' == \n", p->name);
- ast_cli(a->fd, "Local Private Key: %s\n", p->privatekey);
- ast_cli(a->fd, "Local Certificate: %s\n", p->localcert);
- for (i = 0; i < p->cacount; i++) {
- ast_cli(a->fd, "CA Certificate %d: %s\n", i + 1, p->cacerts[i]);
- }
- for (i = 0; i < p->spcount; i++) {
- ast_cli(a->fd, "Service Point %d: %s\n", i + 1, p->srvpoints[i]);
- }
- ast_cli(a->fd, "Max Connections: %d\n", p->maxconnections);
- ast_cli(a->fd, "Retry Delay: %d seconds\n", p->retrydelay);
- ast_cli(a->fd, "Retry Limit: %d\n", p->retrylimit);
- ast_cli(a->fd, "Timeout: %d milliseconds\n", p->timeout);
- ast_cli(a->fd, "Source: %s\n", strlen(p->source) ? p->source : "<unspecified>");
- ast_cli(a->fd, "Auth Policy %d\n", p->authpolicy);
- ast_cli(a->fd, "Default protocol %s\n", p->defaultprotocol);
- ast_cli(a->fd, "OSP Handle: %d\n", p->handle);
- found++;
- }
- p = p->next;
- }
- ast_mutex_unlock(&osplock);
-
- if (!found) {
- if (provider) {
- ast_cli(a->fd, "Unable to find OSP provider '%s'\n", provider);
- } else {
- ast_cli(a->fd, "No OSP providers configured\n");
- }
- }
- return CLI_SUCCESS;
-}
-
-static const char* app1= "OSPAuth";
-static const char* synopsis1 = "OSP authentication";
-static const char* descrip1 =
-" OSPAuth([provider[,options]]): Authenticate a SIP INVITE by OSP and sets\n"
-"the variables:\n"
-" ${OSPINHANDLE}: The inbound call transaction handle\n"
-" ${OSPINTIMELIMIT}: The inbound call duration limit in seconds\n"
-"\n"
-"This application sets the following channel variable upon completion:\n"
-" OSPAUTHSTATUS The status of the OSP Auth attempt as a text string, one of\n"
-" SUCCESS | FAILED | ERROR\n";
-
-static const char* app2= "OSPLookup";
-static const char* synopsis2 = "Lookup destination by OSP";
-static const char* descrip2 =
-" OSPLookup(exten[,provider[,options]]): Looks up an extension via OSP and sets\n"
-"the variables, where 'n' is the number of the result beginning with 1:\n"
-" ${OSPOUTHANDLE}: The OSP Handle for anything remaining\n"
-" ${OSPTECH}: The technology to use for the call\n"
-" ${OSPDEST}: The destination to use for the call\n"
-" ${OSPCALLED}: The called number to use for the call\n"
-" ${OSPCALLING}: The calling number to use for the call\n"
-" ${OSPDIALSTR}: The dial command string\n"
-" ${OSPOUTTOKEN}: The actual OSP token as a string\n"
-" ${OSPOUTTIMELIMIT}: The outbound call duration limit in seconds\n"
-" ${OSPOUTCALLIDTYPES}: The outbound call id types\n"
-" ${OSPOUTCALLID}: The outbound call id\n"
-" ${OSPRESULTS}: The number of OSP results total remaining\n"
-"\n"
-"The option string may contain the following character:\n"
-" 'h' -- generate H323 call id for the outbound call\n"
-" 's' -- generate SIP call id for the outbound call. Have not been implemented\n"
-" 'i' -- generate IAX call id for the outbound call. Have not been implemented\n"
-"This application sets the following channel variable upon completion:\n"
-" OSPLOOKUPSTATUS The status of the OSP Lookup attempt as a text string, one of\n"
-" SUCCESS | FAILED | ERROR\n";
-
-static const char* app3 = "OSPNext";
-static const char* synopsis3 = "Lookup next destination by OSP";
-static const char* descrip3 =
-" OSPNext(cause[,provider[,options]]): Looks up the next OSP Destination for ${OSPOUTHANDLE}\n"
-"See OSPLookup for more information\n"
-"\n"
-"This application sets the following channel variable upon completion:\n"
-" OSPNEXTSTATUS The status of the OSP Next attempt as a text string, one of\n"
-" SUCCESS | FAILED | ERROR\n";
-
-static const char* app4 = "OSPFinish";
-static const char* synopsis4 = "Record OSP entry";
-static const char* descrip4 =
-" OSPFinish([status[,options]]): Records call state for ${OSPINHANDLE}, according to\n"
-"status, which should be one of BUSY, CONGESTION, ANSWER, NOANSWER, or CHANUNAVAIL\n"
-"or coincidentally, just what the Dial application stores in its ${DIALSTATUS}.\n"
-"\n"
-"This application sets the following channel variable upon completion:\n"
-" OSPFINISHSTATUS The status of the OSP Finish attempt as a text string, one of\n"
-" SUCCESS | FAILED | ERROR \n";
-
-static struct ast_cli_entry cli_osp[] = {
- AST_CLI_DEFINE(handle_cli_osp_show, "Displays OSF information")
-};
-
-static int load_module(void)
-{
- int res;
-
- if (!osp_load(0))
- return AST_MODULE_LOAD_DECLINE;
-
- ast_cli_register_multiple(cli_osp, sizeof(cli_osp) / sizeof(struct ast_cli_entry));
- res = ast_register_application(app1, ospauth_exec, synopsis1, descrip1);
- res |= ast_register_application(app2, osplookup_exec, synopsis2, descrip2);
- res |= ast_register_application(app3, ospnext_exec, synopsis3, descrip3);
- res |= ast_register_application(app4, ospfinished_exec, synopsis4, descrip4);
-
- return res;
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(app4);
- res |= ast_unregister_application(app3);
- res |= ast_unregister_application(app2);
- res |= ast_unregister_application(app1);
- ast_cli_unregister_multiple(cli_osp, sizeof(cli_osp) / sizeof(struct ast_cli_entry));
- osp_unload();
-
- return res;
-}
-
-static int reload(void)
-{
- osp_load(1);
-
- return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Open Settlement Protocol Applications",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
diff --git a/trunk/apps/app_page.c b/trunk/apps/app_page.c
deleted file mode 100644
index 2ea367edd..000000000
--- a/trunk/apps/app_page.c
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (c) 2004 - 2006 Digium, Inc. All rights reserved.
- *
- * Mark Spencer <markster@digium.com>
- *
- * This code is released under the GNU General Public License
- * version 2.0. See LICENSE for more information.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- */
-
-/*! \file
- *
- * \brief page() - Paging application
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>zaptel</depend>
- <depend>app_meetme</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/file.h"
-#include "asterisk/app.h"
-#include "asterisk/chanvars.h"
-#include "asterisk/utils.h"
-#include "asterisk/devicestate.h"
-#include "asterisk/dial.h"
-
-static const char *app_page= "Page";
-
-static const char *page_synopsis = "Pages phones";
-
-static const char *page_descrip =
-"Page(Technology/Resource&Technology2/Resource2[,options])\n"
-" Places outbound calls to the given technology / resource and dumps\n"
-"them into a conference bridge as muted participants. The original\n"
-"caller is dumped into the conference as a speaker and the room is\n"
-"destroyed when the original caller leaves. Valid options are:\n"
-" d - full duplex audio\n"
-" q - quiet, do not play beep to caller\n"
-" r - record the page into a file (see 'r' for app_meetme)\n"
-" s - only dial channel if devicestate says it is not in use\n";
-
-enum {
- PAGE_DUPLEX = (1 << 0),
- PAGE_QUIET = (1 << 1),
- PAGE_RECORD = (1 << 2),
- PAGE_SKIP = (1 << 3),
-} page_opt_flags;
-
-AST_APP_OPTIONS(page_opts, {
- AST_APP_OPTION('d', PAGE_DUPLEX),
- AST_APP_OPTION('q', PAGE_QUIET),
- AST_APP_OPTION('r', PAGE_RECORD),
- AST_APP_OPTION('s', PAGE_SKIP),
-});
-
-#define MAX_DIALS 128
-
-static int page_exec(struct ast_channel *chan, void *data)
-{
- char *options, *tech, *resource, *tmp;
- char meetmeopts[88], originator[AST_CHANNEL_NAME], *opts[0];
- struct ast_flags flags = { 0 };
- unsigned int confid = ast_random();
- struct ast_app *app;
- int res = 0, pos = 0, i = 0;
- struct ast_dial *dials[MAX_DIALS];
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "This application requires at least one argument (destination(s) to page)\n");
- return -1;
- }
-
- if (!(app = pbx_findapp("MeetMe"))) {
- ast_log(LOG_WARNING, "There is no MeetMe application available!\n");
- return -1;
- };
-
- options = ast_strdupa(data);
-
- ast_copy_string(originator, chan->name, sizeof(originator));
- if ((tmp = strchr(originator, '-')))
- *tmp = '\0';
-
- tmp = strsep(&options, ",");
- if (options)
- ast_app_parse_options(page_opts, &flags, opts, options);
-
- snprintf(meetmeopts, sizeof(meetmeopts), "MeetMe,%ud,%s%sqxdw(5)", confid, (ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "m"),
- (ast_test_flag(&flags, PAGE_RECORD) ? "r" : "") );
-
- /* Go through parsing/calling each device */
- while ((tech = strsep(&tmp, "&"))) {
- int state = 0;
- struct ast_dial *dial = NULL;
-
- /* don't call the originating device */
- if (!strcasecmp(tech, originator))
- continue;
-
- /* If no resource is available, continue on */
- if (!(resource = strchr(tech, '/'))) {
- ast_log(LOG_WARNING, "Incomplete destination '%s' supplied.\n", tech);
- continue;
- }
-
- /* Ensure device is not in use if skip option is enabled */
- if (ast_test_flag(&flags, PAGE_SKIP) && (state = ast_device_state(tech)) != AST_DEVICE_NOT_INUSE) {
- ast_log(LOG_WARNING, "Destination '%s' has device state '%s'.\n", tech, devstate2str(state));
- continue;
- }
-
- *resource++ = '\0';
-
- /* Create a dialing structure */
- if (!(dial = ast_dial_create())) {
- ast_log(LOG_WARNING, "Failed to create dialing structure.\n");
- continue;
- }
-
- /* Append technology and resource */
- ast_dial_append(dial, tech, resource);
-
- /* Set ANSWER_EXEC as global option */
- ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, meetmeopts);
-
- /* Run this dial in async mode */
- ast_dial_run(dial, chan, 1);
-
- /* Put in our dialing array */
- dials[pos++] = dial;
- }
-
- if (!ast_test_flag(&flags, PAGE_QUIET)) {
- res = ast_streamfile(chan, "beep", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
- }
-
- if (!res) {
- snprintf(meetmeopts, sizeof(meetmeopts), "%ud,A%s%sqxd", confid, (ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "t"),
- (ast_test_flag(&flags, PAGE_RECORD) ? "r" : "") );
- pbx_exec(chan, app, meetmeopts);
- }
-
- /* Go through each dial attempt cancelling, joining, and destroying */
- for (i = 0; i < pos; i++) {
- struct ast_dial *dial = dials[i];
-
- /* We have to wait for the async thread to exit as it's possible Meetme won't throw them out immediately */
- ast_dial_join(dial);
-
- /* Hangup all channels */
- ast_dial_hangup(dial);
-
- /* Destroy dialing structure */
- ast_dial_destroy(dial);
- }
-
- return -1;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app_page);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app_page, page_exec, page_synopsis, page_descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Page Multiple Phones");
-
diff --git a/trunk/apps/app_parkandannounce.c b/trunk/apps/app_parkandannounce.c
deleted file mode 100644
index 7ad7a6416..000000000
--- a/trunk/apps/app_parkandannounce.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2006, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * Author: Ben Miller <bgmiller@dccinc.com>
- * With TONS of help from Mark!
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief ParkAndAnnounce application for Asterisk
- *
- * \author Ben Miller <bgmiller@dccinc.com>
- * \arg With TONS of help from Mark!
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/features.h"
-#include "asterisk/say.h"
-#include "asterisk/lock.h"
-#include "asterisk/utils.h"
-#include "asterisk/app.h"
-
-static char *app = "ParkAndAnnounce";
-
-static char *synopsis = "Park and Announce";
-
-static char *descrip =
-" ParkAndAnnounce(announce:template,timeout,dial[,return_context]):\n"
-"Park a call into the parkinglot and announce the call to another channel.\n"
-"\n"
-"announce template: Colon-separated list of files to announce. The word PARKED\n"
-" will be replaced by a say_digits of the extension in which\n"
-" the call is parked.\n"
-"timeout: Time in seconds before the call returns into the return\n"
-" context.\n"
-"dial: The app_dial style resource to call to make the\n"
-" announcement. Console/dsp calls the console.\n"
-"return_context: The goto-style label to jump the call back into after\n"
-" timeout. Default <priority+1>.\n"
-"\n"
-"The variable ${PARKEDAT} will contain the parking extension into which the\n"
-"call was placed. Use with the Local channel to allow the dialplan to make\n"
-"use of this information.\n";
-
-
-static int parkandannounce_exec(struct ast_channel *chan, void *data)
-{
- int res = -1;
- int lot, timeout = 0, dres;
- char *dialtech, *tmp[100], buf[13];
- int looptemp, i;
- char *s;
-
- struct ast_channel *dchan;
- struct outgoing_helper oh = { 0, };
- int outstate;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(template);
- AST_APP_ARG(timeout);
- AST_APP_ARG(dial);
- AST_APP_ARG(return_context);
- );
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce:template|timeout|dial|[return_context])\n");
- return -1;
- }
-
- s = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, s);
-
- if (args.timeout)
- timeout = atoi(args.timeout) * 1000;
-
- if (ast_strlen_zero(args.dial)) {
- ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or Zap/g1/5551212\n");
- return -1;
- }
-
- dialtech = strsep(&args.dial, "/");
- ast_verb(3, "Dial Tech,String: (%s,%s)\n", dialtech, args.dial);
-
- if (!ast_strlen_zero(args.return_context))
- ast_parseable_goto(chan, args.return_context);
-
- ast_verb(3, "Return Context: (%s,%s,%d) ID: %s\n", chan->context, chan->exten, chan->priority, chan->cid.cid_num);
- if (!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
- ast_verb(3, "Warning: Return Context Invalid, call will return to default|s\n");
- }
-
- /* we are using masq_park here to protect * from touching the channel once we park it. If the channel comes out of timeout
- before we are done announcing and the channel is messed with, Kablooeee. So we use Masq to prevent this. */
-
- ast_masq_park_call(chan, NULL, timeout, &lot);
-
- ast_verb(3, "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, args.return_context);
-
- /* Now place the call to the extension */
-
- snprintf(buf, sizeof(buf), "%d", lot);
- oh.parent_channel = chan;
- oh.vars = ast_variable_new("_PARKEDAT", buf, "");
- dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, args.dial, 30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh);
-
- if (dchan) {
- if (dchan->_state == AST_STATE_UP) {
- ast_verb(4, "Channel %s was answered.\n", dchan->name);
- } else {
- ast_verb(4, "Channel %s was never answered.\n", dchan->name);
- ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", dchan->name);
- ast_hangup(dchan);
- return -1;
- }
- } else {
- ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
- return -1;
- }
-
- ast_stopstream(dchan);
-
- /* now we have the call placed and are ready to play stuff to it */
-
- ast_verb(4, "Announce Template:%s\n", args.template);
-
- for (looptemp = 0, tmp[looptemp++] = strsep(&args.template, ":");
- looptemp < sizeof(tmp) / sizeof(tmp[0]);
- tmp[looptemp++] = strsep(&args.template, ":"));
-
- for (i = 0; i < looptemp; i++) {
- ast_verb(4, "Announce:%s\n", tmp[i]);
- if (!strcmp(tmp[i], "PARKED")) {
- ast_say_digits(dchan, lot, "", dchan->language);
- } else {
- dres = ast_streamfile(dchan, tmp[i], dchan->language);
- if (!dres) {
- dres = ast_waitstream(dchan, "");
- } else {
- ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], dchan->name);
- dres = 0;
- }
- }
- }
-
- ast_stopstream(dchan);
- ast_hangup(dchan);
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- /* return ast_register_application(app, park_exec); */
- return ast_register_application(app, parkandannounce_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Parking and Announce Application");
diff --git a/trunk/apps/app_pickupchan.c b/trunk/apps/app_pickupchan.c
deleted file mode 100644
index 6c3c4e3ef..000000000
--- a/trunk/apps/app_pickupchan.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2008, Gary Cook
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Pickup a ringing channel
- *
- * \author Gary Cook
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "asterisk/file.h"
-#include "asterisk/logger.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-#include "asterisk/app.h"
-#include "asterisk/options.h"
-
-static const char *app = "PickupChan";
-static const char *synopsis = "Pickup a ringing channel";
-static const char *descrip =
-" PickupChan(channel[&channel...]): This application can pickup any ringing channel\n";
-
-/*! \todo This application should return a result code, like PICKUPRESULT */
-
-/*! \brief Helper function that determines whether a channel is capable of being picked up */
-static int can_pickup(struct ast_channel *chan)
-{
- ast_debug(3, "Checking Pickup '%s' state '%s ( %d )'\n", chan->name, ast_state2str(chan->_state), chan->_state);
-
- if (!chan->pbx && (chan->_state == AST_STATE_RINGING || chan->_state == AST_STATE_RING)) {
- return 1;
- } else {
- return 0;
- }
-}
-
-/*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
-static struct ast_channel *my_ast_get_channel_by_name_locked(char *channame)
-{
- struct ast_channel *chan;
- char *chkchan = alloca(strlen(channame) + 2);
-
- /* need to append a '-' for the comparison so we check full channel name,
- * i.e SIP/hgc- , use a temporary variable so original stays the same for
- * debugging.
- */
- strcpy(chkchan, channame);
- strcat(chkchan, "-");
-
- for (chan = ast_walk_channel_by_name_prefix_locked(NULL, channame, strlen(channame));
- chan;
- chan = ast_walk_channel_by_name_prefix_locked(chan, channame, strlen(channame))) {
- if (!strncasecmp(chan->name, chkchan, strlen(chkchan)) && can_pickup(chan))
- return chan;
- ast_channel_unlock(chan);
- }
- return NULL;
-}
-
-/*! \brief Perform actual pickup between two channels */
-static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
-{
- int res = 0;
-
- ast_debug(3, "Call pickup on '%s' by '%s'\n", target->name, chan->name);
-
- if ((res = ast_answer(chan))) {
- ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
- return -1;
- }
-
- if ((res = ast_queue_control(chan, AST_CONTROL_ANSWER))) {
- ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
- return -1;
- }
-
- if ((res = ast_channel_masquerade(target, chan))) {
- ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
- return -1;
- }
-
- return res;
-}
-
-/*! \brief Attempt to pick up specified channel named , does not use context */
-static int pickup_by_channel(struct ast_channel *chan, char *pickup)
-{
- int res = 0;
- struct ast_channel *target;
-
- if (!(target = my_ast_get_channel_by_name_locked(pickup)))
- return -1;
-
- /* Just check that we are not picking up the SAME as target */
- if (chan->name != target->name && chan != target) {
- res = pickup_do(chan, target);
- ast_channel_unlock(target);
- }
-
- return res;
-}
-
-/*! \brief Main application entry point */
-static int pickupchan_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_module_user *u = NULL;
- char *tmp = ast_strdupa(data);
- char *pickup = NULL, *context = NULL;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Pickup requires an argument (channel)!\n");
- return -1;
- }
-
- u = ast_module_user_add(chan);
-
- /* Parse channel (and ignore context if there) */
- while (!ast_strlen_zero(tmp) && (pickup = strsep(&tmp, "&"))) {
- if ((context = strchr(pickup , '@'))) {
- *context++ = '\0';
- }
- if (!strncasecmp(chan->name, pickup , strlen(pickup))) {
- ast_log(LOG_NOTICE, "Cannot pickup your own channel %s.\n", pickup);
- } else {
- if (!pickup_by_channel(chan, pickup)) {
- break;
- }
- ast_log(LOG_NOTICE, "No target channel found for %s.\n", pickup);
- }
- }
-
- ast_module_user_remove(u);
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, pickupchan_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel Pickup Application");
diff --git a/trunk/apps/app_playback.c b/trunk/apps/app_playback.c
deleted file mode 100644
index 21b3ab3eb..000000000
--- a/trunk/apps/app_playback.c
+++ /dev/null
@@ -1,524 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Trivial application to playback a sound file
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-/* This file provides config-file based 'say' functions, and implenents
- * some CLI commands.
- */
-#include "asterisk/say.h" /* provides config-file based 'say' functions */
-#include "asterisk/cli.h"
-
-static char *app = "Playback";
-
-static char *synopsis = "Play a file";
-
-static char *descrip =
-" Playback(filename[&filename2...][,option]): Plays back given filenames (do not put\n"
-"extension). Options may also be included following a comma.\n"
-"The 'skip' option causes the playback of the message to be skipped if the channel\n"
-"is not in the 'up' state (i.e. it hasn't been answered yet). If 'skip' is \n"
-"specified, the application will return immediately should the channel not be\n"
-"off hook. Otherwise, unless 'noanswer' is specified, the channel will\n"
-"be answered before the sound is played. Not all channels support playing\n"
-"messages while still on hook.\n"
-"This application sets the following channel variable upon completion:\n"
-" PLAYBACKSTATUS The status of the playback attempt as a text string, one of\n"
-" SUCCESS | FAILED\n"
-;
-
-
-static struct ast_config *say_cfg = NULL;
-/* save the say' api calls.
- * The first entry is NULL if we have the standard source,
- * otherwise we are sourcing from here.
- * 'say load [new|old]' will enable the new or old method, or report status
- */
-static const void *say_api_buf[40];
-static const char *say_old = "old";
-static const char *say_new = "new";
-
-static void save_say_mode(const void *arg)
-{
- int i = 0;
- say_api_buf[i++] = arg;
-
- say_api_buf[i++] = ast_say_number_full;
- say_api_buf[i++] = ast_say_enumeration_full;
- say_api_buf[i++] = ast_say_digit_str_full;
- say_api_buf[i++] = ast_say_character_str_full;
- say_api_buf[i++] = ast_say_phonetic_str_full;
- say_api_buf[i++] = ast_say_datetime;
- say_api_buf[i++] = ast_say_time;
- say_api_buf[i++] = ast_say_date;
- say_api_buf[i++] = ast_say_datetime_from_now;
- say_api_buf[i++] = ast_say_date_with_format;
-}
-
-static void restore_say_mode(void *arg)
-{
- int i = 0;
- say_api_buf[i++] = arg;
-
- ast_say_number_full = say_api_buf[i++];
- ast_say_enumeration_full = say_api_buf[i++];
- ast_say_digit_str_full = say_api_buf[i++];
- ast_say_character_str_full = say_api_buf[i++];
- ast_say_phonetic_str_full = say_api_buf[i++];
- ast_say_datetime = say_api_buf[i++];
- ast_say_time = say_api_buf[i++];
- ast_say_date = say_api_buf[i++];
- ast_say_datetime_from_now = say_api_buf[i++];
- ast_say_date_with_format = say_api_buf[i++];
-}
-
-/*
- * Typical 'say' arguments in addition to the date or number or string
- * to say. We do not include 'options' because they may be different
- * in recursive calls, and so they are better left as an external
- * parameter.
- */
-typedef struct {
- struct ast_channel *chan;
- const char *ints;
- const char *language;
- int audiofd;
- int ctrlfd;
-} say_args_t;
-
-static int s_streamwait3(const say_args_t *a, const char *fn)
-{
- int res = ast_streamfile(a->chan, fn, a->language);
- if (res) {
- ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
- return res;
- }
- res = (a->audiofd > -1 && a->ctrlfd > -1) ?
- ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) :
- ast_waitstream(a->chan, a->ints);
- ast_stopstream(a->chan);
- return res;
-}
-
-/*
- * the string is 'prefix:data' or prefix:fmt:data'
- * with ':' being invalid in strings.
- */
-static int do_say(say_args_t *a, const char *s, const char *options, int depth)
-{
- struct ast_variable *v;
- char *lang, *x, *rule = NULL;
- int ret = 0;
- struct varshead head = { .first = NULL, .last = NULL };
- struct ast_var_t *n;
-
- ast_debug(2, "string <%s> depth <%d>\n", s, depth);
- if (depth++ > 10) {
- ast_log(LOG_WARNING, "recursion too deep, exiting\n");
- return -1;
- } else if (!say_cfg) {
- ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
- return -1;
- }
-
- /* scan languages same as in file.c */
- if (a->language == NULL)
- a->language = "en"; /* default */
- ast_debug(2, "try <%s> in <%s>\n", s, a->language);
- lang = ast_strdupa(a->language);
- for (;;) {
- for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) {
- if (ast_extension_match(v->name, s)) {
- rule = ast_strdupa(v->value);
- break;
- }
- }
- if (rule)
- break;
- if ( (x = strchr(lang, '_')) )
- *x = '\0'; /* try without suffix */
- else if (strcmp(lang, "en"))
- lang = "en"; /* last resort, try 'en' if not done yet */
- else
- break;
- }
- if (!rule)
- return 0;
-
- /* skip up to two prefixes to get the value */
- if ( (x = strchr(s, ':')) )
- s = x + 1;
- if ( (x = strchr(s, ':')) )
- s = x + 1;
- ast_debug(2, "value is <%s>\n", s);
- n = ast_var_assign("SAY", s);
- AST_LIST_INSERT_HEAD(&head, n, entries);
-
- /* scan the body, one piece at a time */
- while ( !ret && (x = strsep(&rule, ",")) ) { /* exit on key */
- char fn[128];
- const char *p, *fmt, *data; /* format and data pointers */
-
- /* prepare a decent file name */
- x = ast_skip_blanks(x);
- ast_trim_blanks(x);
-
- /* replace variables */
- pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
- ast_debug(2, "doing [%s]\n", fn);
-
- /* locate prefix and data, if any */
- fmt = index(fn, ':');
- if (!fmt || fmt == fn) { /* regular filename */
- ret = s_streamwait3(a, fn);
- continue;
- }
- fmt++;
- data = index(fmt, ':'); /* colon before data */
- if (!data || data == fmt) { /* simple prefix-fmt */
- ret = do_say(a, fn, options, depth);
- continue;
- }
- /* prefix:fmt:data */
- for (p = fmt; p < data && ret <= 0; p++) {
- char fn2[sizeof(fn)];
- if (*p == ' ' || *p == '\t') /* skip blanks */
- continue;
- if (*p == '\'') {/* file name - we trim them */
- char *y;
- strcpy(fn2, ast_skip_blanks(p+1)); /* make a full copy */
- y = index(fn2, '\'');
- if (!y) {
- p = data; /* invalid. prepare to end */
- break;
- }
- *y = '\0';
- ast_trim_blanks(fn2);
- p = index(p+1, '\'');
- ret = s_streamwait3(a, fn2);
- } else {
- int l = fmt-fn;
- strcpy(fn2, fn); /* copy everything */
- /* after prefix, append the format */
- fn2[l++] = *p;
- strcpy(fn2 + l, data);
- ret = do_say(a, fn2, options, depth);
- }
-
- if (ret) {
- break;
- }
- }
- }
- ast_var_delete(n);
- return ret;
-}
-
-static int say_full(struct ast_channel *chan, const char *string,
- const char *ints, const char *lang, const char *options,
- int audiofd, int ctrlfd)
-{
- say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
- return do_say(&a, string, options, 0);
-}
-
-static int say_number_full(struct ast_channel *chan, int num,
- const char *ints, const char *lang, const char *options,
- int audiofd, int ctrlfd)
-{
- char buf[64];
- say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
- snprintf(buf, sizeof(buf), "num:%d", num);
- return do_say(&a, buf, options, 0);
-}
-
-static int say_enumeration_full(struct ast_channel *chan, int num,
- const char *ints, const char *lang, const char *options,
- int audiofd, int ctrlfd)
-{
- char buf[64];
- say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
- snprintf(buf, sizeof(buf), "enum:%d", num);
- return do_say(&a, buf, options, 0);
-}
-
-static int say_date_generic(struct ast_channel *chan, time_t t,
- const char *ints, const char *lang, const char *format, const char *timezone, const char *prefix)
-{
- char buf[128];
- struct ast_tm tm;
- struct timeval tv = { t, 0 };
- say_args_t a = { chan, ints, lang, -1, -1 };
- if (format == NULL)
- format = "";
-
- ast_localtime(&tv, &tm, NULL);
- snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
- prefix,
- format,
- tm.tm_year+1900,
- tm.tm_mon+1,
- tm.tm_mday,
- tm.tm_hour,
- tm.tm_min,
- tm.tm_sec,
- tm.tm_wday,
- tm.tm_yday);
- return do_say(&a, buf, NULL, 0);
-}
-
-static int say_date_with_format(struct ast_channel *chan, time_t t,
- const char *ints, const char *lang, const char *format, const char *timezone)
-{
- return say_date_generic(chan, t, ints, lang, format, timezone, "datetime");
-}
-
-static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
-{
- return say_date_generic(chan, t, ints, lang, "", NULL, "date");
-}
-
-static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
-{
- return say_date_generic(chan, t, ints, lang, "", NULL, "time");
-}
-
-static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
-{
- return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
-}
-
-/*
- * remap the 'say' functions to use those in this file
- */
-static int say_init_mode(const char *mode) {
- if (!strcmp(mode, say_new)) {
- if (say_cfg == NULL) {
- ast_log(LOG_ERROR, "There is no say.conf file to use new mode\n");
- return -1;
- }
- save_say_mode(say_new);
- ast_say_number_full = say_number_full;
-
- ast_say_enumeration_full = say_enumeration_full;
-#if 0
- ast_say_digits_full = say_digits_full;
- ast_say_digit_str_full = say_digit_str_full;
- ast_say_character_str_full = say_character_str_full;
- ast_say_phonetic_str_full = say_phonetic_str_full;
- ast_say_datetime_from_now = say_datetime_from_now;
-#endif
- ast_say_datetime = say_datetime;
- ast_say_time = say_time;
- ast_say_date = say_date;
- ast_say_date_with_format = say_date_with_format;
- } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
- restore_say_mode(NULL);
- } else if (strcmp(mode, say_old)) {
- ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
- return -1;
- }
-
- return 0;
-}
-
-static char *__say_cli_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- const char *old_mode = say_api_buf[0] ? say_new : say_old;
- char *mode;
- switch (cmd) {
- case CLI_INIT:
- e->command = "say load [new|old]";
- e->usage =
- "Usage: say load [new|old]\n"
- " say load\n"
- " Report status of current say mode\n"
- " say load new\n"
- " Set say method, configured in say.conf\n"
- " say load old\n"
- " Set old say method, coded in asterisk core\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc == 2) {
- ast_cli(a->fd, "say mode is [%s]\n", old_mode);
- return CLI_SUCCESS;
- } else if (a->argc != 3)
- return CLI_SHOWUSAGE;
- mode = a->argv[2];
- if (!strcmp(mode, old_mode))
- ast_cli(a->fd, "say mode is %s already\n", mode);
- else
- if (say_init_mode(mode) == 0)
- ast_cli(a->fd, "setting say mode from %s to %s\n", old_mode, mode);
-
- return CLI_SUCCESS;
-}
-
-static struct ast_cli_entry cli_playback[] = {
- AST_CLI_DEFINE(__say_cli_init, "Set or show the say mode"),
-};
-
-static int playback_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- int mres = 0;
- char *tmp;
- int option_skip=0;
- int option_say=0;
- int option_noanswer = 0;
-
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(filenames);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
- return -1;
- }
-
- tmp = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, tmp);
-
- if (args.options) {
- if (strcasestr(args.options, "skip"))
- option_skip = 1;
- if (strcasestr(args.options, "say"))
- option_say = 1;
- if (strcasestr(args.options, "noanswer"))
- option_noanswer = 1;
- }
- if (chan->_state != AST_STATE_UP) {
- if (option_skip) {
- /* At the user's option, skip if the line is not up */
- goto done;
- } else if (!option_noanswer)
- /* Otherwise answer unless we're supposed to send this while on-hook */
- res = ast_answer(chan);
- }
- if (!res) {
- char *back = args.filenames;
- char *front;
-
- ast_stopstream(chan);
- while (!res && (front = strsep(&back, "&"))) {
- if (option_say)
- res = say_full(chan, front, "", chan->language, NULL, -1, -1);
- else
- res = ast_streamfile(chan, front, chan->language);
- if (!res) {
- res = ast_waitstream(chan, "");
- ast_stopstream(chan);
- } else {
- ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
- res = 0;
- mres = 1;
- }
- }
- }
-done:
- pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
- return res;
-}
-
-static int reload(void)
-{
- struct ast_variable *v;
- struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
- struct ast_config *newcfg;
-
- if ((newcfg = ast_config_load("say.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
-
- if (say_cfg) {
- ast_config_destroy(say_cfg);
- ast_log(LOG_NOTICE, "Reloading say.conf\n");
- say_cfg = newcfg;
- }
-
- if (say_cfg) {
- for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
- if (ast_extension_match(v->name, "mode")) {
- say_init_mode(v->value);
- break;
- }
- }
- }
-
- /*
- * XXX here we should sort rules according to the same order
- * we have in pbx.c so we have the same matching behaviour.
- */
- return 0;
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(app);
-
- ast_cli_unregister_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
-
- if (say_cfg)
- ast_config_destroy(say_cfg);
-
- return res;
-}
-
-static int load_module(void)
-{
- struct ast_variable *v;
- struct ast_flags config_flags = { 0 };
-
- say_cfg = ast_config_load("say.conf", config_flags);
- if (say_cfg) {
- for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
- if (ast_extension_match(v->name, "mode")) {
- say_init_mode(v->value);
- break;
- }
- }
- }
-
- ast_cli_register_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
- return ast_register_application(app, playback_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
diff --git a/trunk/apps/app_privacy.c b/trunk/apps/app_privacy.c
deleted file mode 100644
index dacfd704c..000000000
--- a/trunk/apps/app_privacy.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Block all calls without Caller*ID, require phone # to be entered
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/utils.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/image.h"
-#include "asterisk/callerid.h"
-#include "asterisk/app.h"
-#include "asterisk/config.h"
-
-static char *app = "PrivacyManager";
-
-static char *synopsis = "Require phone number to be entered, if no CallerID sent";
-
-static char *descrip =
- " PrivacyManager([maxretries][,minlength]): If no Caller*ID \n"
- "is sent, PrivacyManager answers the channel and asks the caller to\n"
- "enter their phone number. The caller is given 'maxretries' attempts to do so.\n"
- "The application does nothing if Caller*ID was received on the channel.\n"
- " maxretries default 3 -maximum number of attempts the caller is allowed \n"
- " to input a callerid.\n"
- " minlength default 10 -minimum allowable digits in the input callerid number.\n"
- "The application sets the following channel variable upon completion: \n"
- "PRIVACYMGRSTATUS The status of the privacy manager's attempt to collect \n"
- " a phone number from the user. A text string that is either:\n"
- " SUCCESS | FAILED \n"
-;
-
-
-static int privacy_exec (struct ast_channel *chan, void *data)
-{
- int res=0;
- int retries;
- int maxretries = 3;
- int minlength = 10;
- int x = 0;
- char phone[30];
- char *parse = NULL;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(maxretries);
- AST_APP_ARG(minlength);
- AST_APP_ARG(options);
- );
-
- if (!ast_strlen_zero(chan->cid.cid_num)) {
- if (option_verbose > 2)
- ast_verbose (VERBOSE_PREFIX_3 "CallerID Present: Skipping\n");
- } else {
- /*Answer the channel if it is not already*/
- if (chan->_state != AST_STATE_UP) {
- if ((res = ast_answer(chan)))
- return -1;
- }
-
- if (!ast_strlen_zero(data)) {
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (args.maxretries) {
- if (sscanf(args.maxretries, "%d", &x) == 1)
- maxretries = x;
- else
- ast_log(LOG_WARNING, "Invalid max retries argument\n");
- }
- if (args.minlength) {
- if (sscanf(args.minlength, "%d", &x) == 1)
- minlength = x;
- else
- ast_log(LOG_WARNING, "Invalid min length argument\n");
- }
-
- }
-
- /* Play unidentified call */
- res = ast_safe_sleep(chan, 1000);
- if (!res)
- res = ast_streamfile(chan, "privacy-unident", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
-
- /* Ask for 10 digit number, give 3 attempts */
- for (retries = 0; retries < maxretries; retries++) {
- if (!res)
- res = ast_streamfile(chan, "privacy-prompt", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
-
- if (!res )
- res = ast_readstring(chan, phone, sizeof(phone) - 1, /* digit timeout ms */ 3200, /* first digit timeout */ 5000, "#");
-
- if (res < 0)
- break;
-
- /* Make sure we get at least digits */
- if (strlen(phone) >= minlength )
- break;
- else {
- res = ast_streamfile(chan, "privacy-incorrect", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
- }
- }
-
- /* Got a number, play sounds and send them on their way */
- if ((retries < maxretries) && res >= 0 ) {
- res = ast_streamfile(chan, "privacy-thankyou", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
-
- ast_set_callerid (chan, phone, "Privacy Manager", NULL);
-
- /* Clear the unavailable presence bit so if it came in on PRI
- * the caller id will now be passed out to other channels
- */
- chan->cid.cid_pres &= (AST_PRES_UNAVAILABLE ^ 0xFF);
-
- if (option_verbose > 2) {
- ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID to %s, callerpres to %d\n",phone,chan->cid.cid_pres);
- }
- pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "SUCCESS");
- } else {
- pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "FAILED");
- }
- }
-
- return 0;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application (app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, privacy_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Require phone number to be entered, if no CallerID sent");
diff --git a/trunk/apps/app_queue.c b/trunk/apps/app_queue.c
deleted file mode 100644
index 547132b6a..000000000
--- a/trunk/apps/app_queue.c
+++ /dev/null
@@ -1,6153 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2006, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief True call queues with optional send URL on answer
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \arg Config in \ref Config_qu queues.conf
- *
- * \par Development notes
- * \note 2004-11-25: Persistent Dynamic Members added by:
- * NetNation Communications (www.netnation.com)
- * Kevin Lindsay <kevinl@netnation.com>
- *
- * Each dynamic agent in each queue is now stored in the astdb.
- * When asterisk is restarted, each agent will be automatically
- * readded into their recorded queues. This feature can be
- * configured with the 'persistent_members=<1|0>' setting in the
- * '[general]' category in queues.conf. The default is on.
- *
- * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
- *
- * \note These features added by David C. Troy <dave@toad.net>:
- * - Per-queue holdtime calculation
- * - Estimated holdtime announcement
- * - Position announcement
- * - Abandoned/completed call counters
- * - Failout timer passed as optional app parameter
- * - Optional monitoring of calls, started when call is answered
- *
- * Patch Version 1.07 2003-12-24 01
- *
- * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
- * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
- *
- * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
- * by Matthew Enger <m.enger@xi.com.au>
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>res_monitor</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <sys/time.h>
-#include <sys/signal.h>
-#include <netinet/in.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/app.h"
-#include "asterisk/linkedlists.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/say.h"
-#include "asterisk/features.h"
-#include "asterisk/musiconhold.h"
-#include "asterisk/cli.h"
-#include "asterisk/manager.h"
-#include "asterisk/config.h"
-#include "asterisk/monitor.h"
-#include "asterisk/utils.h"
-#include "asterisk/causes.h"
-#include "asterisk/astdb.h"
-#include "asterisk/devicestate.h"
-#include "asterisk/stringfields.h"
-#include "asterisk/event.h"
-#include "asterisk/astobj2.h"
-#include "asterisk/strings.h"
-#include "asterisk/global_datastores.h"
-
-enum {
- QUEUE_STRATEGY_RINGALL = 0,
- QUEUE_STRATEGY_LEASTRECENT,
- QUEUE_STRATEGY_FEWESTCALLS,
- QUEUE_STRATEGY_RANDOM,
- QUEUE_STRATEGY_RRMEMORY,
- QUEUE_STRATEGY_LINEAR,
- QUEUE_STRATEGY_WRANDOM
-};
-
-static struct strategy {
- int strategy;
- char *name;
-} strategies[] = {
- { QUEUE_STRATEGY_RINGALL, "ringall" },
- { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
- { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
- { QUEUE_STRATEGY_RANDOM, "random" },
- { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
- { QUEUE_STRATEGY_LINEAR, "linear" },
- { QUEUE_STRATEGY_WRANDOM, "wrandom"},
-};
-
-#define DEFAULT_RETRY 5
-#define DEFAULT_TIMEOUT 15
-#define RECHECK 1 /* Recheck every second to see we we're at the top yet */
-#define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
-#define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 /* The minimum number of seconds between position announcements
- The default value of 15 provides backwards compatibility */
-#define MAX_QUEUE_BUCKETS 53
-
-#define RES_OKAY 0 /* Action completed */
-#define RES_EXISTS (-1) /* Entry already exists */
-#define RES_OUTOFMEMORY (-2) /* Out of memory */
-#define RES_NOSUCHQUEUE (-3) /* No such queue */
-#define RES_NOT_DYNAMIC (-4) /* Member is not dynamic */
-
-static char *app = "Queue";
-
-static char *synopsis = "Queue a call for a call queue";
-
-static char *descrip =
-" Queue(queuename[,options[,URL][,announceoverride][,timeout][,AGI][,macro][,gosub][,rule]):\n"
-"Queues an incoming call in a particular call queue as defined in queues.conf.\n"
-"This application will return to the dialplan if the queue does not exist, or\n"
-"any of the join options cause the caller to not enter the queue.\n"
-"The option string may contain zero or more of the following characters:\n"
-" 'c' -- continue in the dialplan if the callee hangs up.\n"
-" 'd' -- data-quality (modem) call (minimum delay).\n"
-" 'h' -- allow callee to hang up by pressing *.\n"
-" 'H' -- allow caller to hang up by pressing *.\n"
-" 'n' -- no retries on the timeout; will exit this application and \n"
-" go to the next step.\n"
-" 'i' -- ignore call forward requests from queue members and do nothing\n"
-" when they are requested.\n"
-" 'r' -- ring instead of playing MOH. Periodic Announcements are still made, if applicable.\n"
-" 't' -- allow the called user to transfer the calling user.\n"
-" 'T' -- allow the calling user to transfer the call.\n"
-" 'w' -- allow the called user to write the conversation to disk via Monitor.\n"
-" 'W' -- allow the calling user to write the conversation to disk via Monitor.\n"
-" 'k' -- Allow the called party to enable parking of the call by sending\n"
-" the DTMF sequence defined for call parking in features.conf.\n"
-" 'K' -- Allow the calling party to enable parking of the call by sending\n"
-" the DTMF sequence defined for call parking in features.conf.\n"
-" 'x' -- allow the called user to write the conversation to disk via MixMonitor\n"
-" 'X' -- allow the calling user to write the conversation to disk via MixMonitor\n"
-
-" In addition to transferring the call, a call may be parked and then picked\n"
-"up by another user.\n"
-" The optional URL will be sent to the called party if the channel supports\n"
-"it.\n"
-" The optional AGI parameter will setup an AGI script to be executed on the \n"
-"calling party's channel once they are connected to a queue member.\n"
-" The optional macro parameter will run a macro on the \n"
-"calling party's channel once they are connected to a queue member.\n"
-" The optional gosub parameter will run a gosub on the \n"
-"calling party's channel once they are connected to a queue member.\n"
-" The optional rule parameter will cause the queue's defaultrule to be\n"
-"overridden by the rule specified.\n"
-" The timeout will cause the queue to fail out after a specified number of\n"
-"seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
-" This application sets the following channel variable upon completion:\n"
-" QUEUESTATUS The status of the call as a text string, one of\n"
-" TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL | CONTINUE\n";
-
-static char *app_aqm = "AddQueueMember" ;
-static char *app_aqm_synopsis = "Dynamically adds queue members" ;
-static char *app_aqm_descrip =
-" AddQueueMember(queuename[,interface[,penalty[,options[,membername]]]]):\n"
-"Dynamically adds interface to an existing queue.\n"
-"If the interface is already in the queue it will return an error.\n"
-" This application sets the following channel variable upon completion:\n"
-" AQMSTATUS The status of the attempt to add a queue member as a \n"
-" text string, one of\n"
-" ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
-"Example: AddQueueMember(techsupport,SIP/3000)\n"
-"";
-
-static char *app_rqm = "RemoveQueueMember" ;
-static char *app_rqm_synopsis = "Dynamically removes queue members" ;
-static char *app_rqm_descrip =
-" RemoveQueueMember(queuename[,interface[,options]]):\n"
-"Dynamically removes interface to an existing queue\n"
-"If the interface is NOT in the queue it will return an error.\n"
-" This application sets the following channel variable upon completion:\n"
-" RQMSTATUS The status of the attempt to remove a queue member as a\n"
-" text string, one of\n"
-" REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
-"Example: RemoveQueueMember(techsupport,SIP/3000)\n"
-"";
-
-static char *app_pqm = "PauseQueueMember" ;
-static char *app_pqm_synopsis = "Pauses a queue member" ;
-static char *app_pqm_descrip =
-" PauseQueueMember([queuename],interface[,options[,reason]]):\n"
-"Pauses (blocks calls for) a queue member.\n"
-"The given interface will be paused in the given queue. This prevents\n"
-"any calls from being sent from the queue to the interface until it is\n"
-"unpaused with UnpauseQueueMember or the manager interface. If no\n"
-"queuename is given, the interface is paused in every queue it is a\n"
-"member of. The application will fail if the interface is not found.\n"
-"The reason string is entirely optional and is used to add extra information\n"
-"to the appropriate queue_log entries and manager events.\n"
-" This application sets the following channel variable upon completion:\n"
-" PQMSTATUS The status of the attempt to pause a queue member as a\n"
-" text string, one of\n"
-" PAUSED | NOTFOUND\n"
-"Example: PauseQueueMember(,SIP/3000)\n";
-
-static char *app_upqm = "UnpauseQueueMember" ;
-static char *app_upqm_synopsis = "Unpauses a queue member" ;
-static char *app_upqm_descrip =
-" UnpauseQueueMember([queuename],interface[,options[,reason]]):\n"
-"Unpauses (resumes calls to) a queue member.\n"
-"This is the counterpart to PauseQueueMember and operates exactly the\n"
-"same way, except it unpauses instead of pausing the given interface.\n"
-"The reason string is entirely optional and is used to add extra information\n"
-"to the appropriate queue_log entries and manager events.\n"
-" This application sets the following channel variable upon completion:\n"
-" UPQMSTATUS The status of the attempt to unpause a queue \n"
-" member as a text string, one of\n"
-" UNPAUSED | NOTFOUND\n"
-"Example: UnpauseQueueMember(,SIP/3000)\n";
-
-static char *app_ql = "QueueLog" ;
-static char *app_ql_synopsis = "Writes to the queue_log" ;
-static char *app_ql_descrip =
-" QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n"
-"Allows you to write your own events into the queue log\n"
-"Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n";
-
-/*! \brief Persistent Members astdb family */
-static const char *pm_family = "Queue/PersistentMembers";
-/* The maximum length of each persistent member queue database entry */
-#define PM_MAX_LEN 8192
-
-/*! \brief queues.conf [general] option */
-static int queue_keep_stats = 0;
-
-/*! \brief queues.conf [general] option */
-static int queue_persistent_members = 0;
-
-/*! \brief queues.conf per-queue weight option */
-static int use_weight = 0;
-
-/*! \brief queues.conf [general] option */
-static int autofill_default = 0;
-
-/*! \brief queues.conf [general] option */
-static int montype_default = 0;
-
-/*! \brief queues.conf [general] option */
-static int shared_lastcall = 0;
-
-/*! \brief Subscription to device state change events */
-static struct ast_event_sub *device_state_sub;
-
-/*! \brief queues.conf [general] option */
-static int update_cdr = 0;
-
-enum queue_result {
- QUEUE_UNKNOWN = 0,
- QUEUE_TIMEOUT = 1,
- QUEUE_JOINEMPTY = 2,
- QUEUE_LEAVEEMPTY = 3,
- QUEUE_JOINUNAVAIL = 4,
- QUEUE_LEAVEUNAVAIL = 5,
- QUEUE_FULL = 6,
- QUEUE_CONTINUE = 7,
-};
-
-const struct {
- enum queue_result id;
- char *text;
-} queue_results[] = {
- { QUEUE_UNKNOWN, "UNKNOWN" },
- { QUEUE_TIMEOUT, "TIMEOUT" },
- { QUEUE_JOINEMPTY,"JOINEMPTY" },
- { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
- { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
- { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
- { QUEUE_FULL, "FULL" },
- { QUEUE_CONTINUE, "CONTINUE" },
-};
-
-/*! \brief We define a custom "local user" structure because we
- use it not only for keeping track of what is in use but
- also for keeping track of who we're dialing.
-
- There are two "links" defined in this structure, q_next and call_next.
- q_next links ALL defined callattempt structures into a linked list. call_next is
- a link which allows for a subset of the callattempts to be traversed. This subset
- is used in wait_for_answer so that irrelevant callattempts are not traversed. This
- also is helpful so that queue logs are always accurate in the case where a call to
- a member times out, especially if using the ringall strategy. */
-
-struct callattempt {
- struct callattempt *q_next;
- struct callattempt *call_next;
- struct ast_channel *chan;
- char interface[256];
- int stillgoing;
- int metric;
- int oldstatus;
- time_t lastcall;
- struct call_queue *lastqueue;
- struct member *member;
-};
-
-
-struct queue_ent {
- struct call_queue *parent; /*!< What queue is our parent */
- char moh[80]; /*!< Name of musiconhold to be used */
- char announce[80]; /*!< Announcement to play for member when call is answered */
- char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
- char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
- int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
- int pos; /*!< Where we are in the queue */
- int prio; /*!< Our priority */
- int last_pos_said; /*!< Last position we told the user */
- time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
- int last_periodic_announce_sound; /*!< The last periodic announcement we made */
- time_t last_pos; /*!< Last time we told the user their position */
- int opos; /*!< Where we started in the queue */
- int handled; /*!< Whether our call was handled */
- int pending; /*!< Non-zero if we are attempting to call a member */
- int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
- int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
- int linpos; /*!< If using linear strategy, what position are we at? */
- int linwrapped; /*!< Is the linpos wrapped? */
- time_t start; /*!< When we started holding */
- time_t expire; /*!< When this entry should expire (time out of queue) */
- struct ast_channel *chan; /*!< Our channel */
- AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
- struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
- struct queue_ent *next; /*!< The next queue entry */
-};
-
-struct member {
- char interface[80]; /*!< Technology/Location to dial to reach this member*/
- char state_interface[80]; /*!< Technology/Location from which to read devicestate changes */
- char membername[80]; /*!< Member name to use in queue logs */
- int penalty; /*!< Are we a last resort? */
- int calls; /*!< Number of calls serviced by this member */
- int dynamic; /*!< Are we dynamically added? */
- int realtime; /*!< Is this member realtime? */
- int status; /*!< Status of queue member */
- int paused; /*!< Are we paused (not accepting calls)? */
- time_t lastcall; /*!< When last successful call was hungup */
- struct call_queue *lastqueue; /*!< Last queue we received a call */
- unsigned int dead:1; /*!< Used to detect members deleted in realtime */
- unsigned int delme:1; /*!< Flag to delete entry on reload */
-};
-
-struct member_interface {
- char interface[80];
- AST_LIST_ENTRY(member_interface) list; /*!< Next call queue */
-};
-
-static AST_LIST_HEAD_STATIC(interfaces, member_interface);
-
-/* values used in multi-bit flags in call_queue */
-#define QUEUE_EMPTY_NORMAL 1
-#define QUEUE_EMPTY_STRICT 2
-#define QUEUE_EMPTY_LOOSE 3
-#define ANNOUNCEHOLDTIME_ALWAYS 1
-#define ANNOUNCEHOLDTIME_ONCE 2
-#define QUEUE_EVENT_VARIABLES 3
-
-struct penalty_rule {
- int time; /*!< Number of seconds that need to pass before applying this rule */
- int max_value; /*!< The amount specified in the penalty rule for max penalty */
- int min_value; /*!< The amount specified in the penalty rule for min penalty */
- int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
- int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
- AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
-};
-
-struct call_queue {
- AST_DECLARE_STRING_FIELDS(
- /*! Queue name */
- AST_STRING_FIELD(name);
- /*! Music on Hold class */
- AST_STRING_FIELD(moh);
- /*! Announcement to play when call is answered */
- AST_STRING_FIELD(announce);
- /*! Exit context */
- AST_STRING_FIELD(context);
- /*! Macro to run upon member connection */
- AST_STRING_FIELD(membermacro);
- /*! Gosub to run upon member connection */
- AST_STRING_FIELD(membergosub);
- /*! Default rule to use if none specified in call to Queue() */
- AST_STRING_FIELD(defaultrule);
- /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
- AST_STRING_FIELD(sound_next);
- /*! Sound file: "There are currently" (def. queue-thereare) */
- AST_STRING_FIELD(sound_thereare);
- /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
- AST_STRING_FIELD(sound_calls);
- /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
- AST_STRING_FIELD(sound_holdtime);
- /*! Sound file: "minutes." (def. queue-minutes) */
- AST_STRING_FIELD(sound_minutes);
- /*! Sound file: "less-than" (def. queue-lessthan) */
- AST_STRING_FIELD(sound_lessthan);
- /*! Sound file: "seconds." (def. queue-seconds) */
- AST_STRING_FIELD(sound_seconds);
- /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
- AST_STRING_FIELD(sound_thanks);
- /*! Sound file: Custom announce for caller, no default */
- AST_STRING_FIELD(sound_callerannounce);
- /*! Sound file: "Hold time" (def. queue-reporthold) */
- AST_STRING_FIELD(sound_reporthold);
- );
- /*! Sound files: Custom announce, no default */
- struct ast_str *sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS];
- unsigned int dead:1;
- unsigned int joinempty:2;
- unsigned int eventwhencalled:2;
- unsigned int leavewhenempty:2;
- unsigned int ringinuse:1;
- unsigned int setinterfacevar:1;
- unsigned int setqueuevar:1;
- unsigned int setqueueentryvar:1;
- unsigned int reportholdtime:1;
- unsigned int wrapped:1;
- unsigned int timeoutrestart:1;
- unsigned int announceholdtime:2;
- unsigned int announceposition:1;
- int strategy:4;
- unsigned int maskmemberstatus:1;
- unsigned int realtime:1;
- unsigned int found:1;
- int announcefrequency; /*!< How often to announce their position */
- int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
- int periodicannouncefrequency; /*!< How often to play periodic announcement */
- int roundingseconds; /*!< How many seconds do we round to? */
- int holdtime; /*!< Current avg holdtime, based on recursive boxcar filter */
- int callscompleted; /*!< Number of queue calls completed */
- int callsabandoned; /*!< Number of queue calls abandoned */
- int servicelevel; /*!< seconds setting for servicelevel*/
- int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
- char monfmt[8]; /*!< Format to use when recording calls */
- int montype; /*!< Monitor type Monitor vs. MixMonitor */
- int count; /*!< How many entries */
- int maxlen; /*!< Max number of entries */
- int wrapuptime; /*!< Wrapup Time */
-
- int retry; /*!< Retry calling everyone after this amount of time */
- int timeout; /*!< How long to wait for an answer */
- int weight; /*!< Respective weight */
- int autopause; /*!< Auto pause queue members if they fail to answer */
-
- /* Queue strategy things */
- int rrpos; /*!< Round Robin - position */
- int memberdelay; /*!< Seconds to delay connecting member to caller */
- int autofill; /*!< Ignore the head call status and ring an available agent */
-
- struct ao2_container *members; /*!< Head of the list of members */
- /*!
- * \brief Number of members _logged in_
- * \note There will be members in the members container that are not logged
- * in, so this can not simply be replaced with ao2_container_count().
- */
- int membercount;
- struct queue_ent *head; /*!< Head of the list of callers */
- AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
- AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
-};
-
-struct rule_list {
- char name[80];
- AST_LIST_HEAD_NOLOCK(,penalty_rule) rules;
- AST_LIST_ENTRY(rule_list) list;
-};
-
-AST_LIST_HEAD_STATIC(rule_lists, rule_list);
-
-static struct ao2_container *queues;
-
-static void update_realtime_members(struct call_queue *q);
-static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
-
-/*! \brief sets the QUEUESTATUS channel variable */
-static void set_queue_result(struct ast_channel *chan, enum queue_result res)
-{
- int i;
-
- for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
- if (queue_results[i].id == res) {
- pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
- return;
- }
- }
-}
-
-static char *int2strat(int strategy)
-{
- int x;
-
- for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
- if (strategy == strategies[x].strategy)
- return strategies[x].name;
- }
-
- return "<unknown>";
-}
-
-static int strat2int(const char *strategy)
-{
- int x;
-
- for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
- if (!strcasecmp(strategy, strategies[x].name))
- return strategies[x].strategy;
- }
-
- return -1;
-}
-
-static int queue_hash_cb(const void *obj, const int flags)
-{
- const struct call_queue *q = obj;
- return ast_str_hash(q->name);
-}
-
-static int queue_cmp_cb(void *obj, void *arg, int flags)
-{
- struct call_queue *q = obj, *q2 = arg;
- return !strcasecmp(q->name, q2->name) ? CMP_MATCH : 0;
-}
-
-static inline struct call_queue *queue_ref(struct call_queue *q)
-{
- ao2_ref(q, 1);
- return q;
-}
-
-static inline struct call_queue *queue_unref(struct call_queue *q)
-{
- ao2_ref(q, -1);
- return q;
-}
-
-static void set_queue_variables(struct queue_ent *qe)
-{
-
- char interfacevar[256]="";
- float sl = 0;
-
- if (qe->parent->setqueuevar) {
- sl = 0;
- if (qe->parent->callscompleted > 0)
- sl = 100 * ((float) qe->parent->callscompletedinsl / (float) qe->parent->callscompleted);
-
- snprintf(interfacevar,sizeof(interfacevar),
- "QUEUENAME=%s|QUEUEMAX=%d|QUEUESTRATEGY=%s|QUEUECALLS=%d|QUEUEHOLDTIME=%d|QUEUECOMPLETED=%d|QUEUEABANDONED=%d|QUEUESRVLEVEL=%d|QUEUESRVLEVELPERF=%2.1f",
- qe->parent->name, qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->callscompleted,
- qe->parent->callsabandoned, qe->parent->servicelevel, sl);
-
- pbx_builtin_setvar(qe->chan, interfacevar);
- }
-}
-
-/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
-static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
-{
- struct queue_ent *cur;
-
- if (!q || !new)
- return;
- if (prev) {
- cur = prev->next;
- prev->next = new;
- } else {
- cur = q->head;
- q->head = new;
- }
- new->next = cur;
- new->parent = q;
- new->pos = ++(*pos);
- new->opos = *pos;
-}
-
-enum queue_member_status {
- QUEUE_NO_MEMBERS,
- QUEUE_NO_REACHABLE_MEMBERS,
- QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS,
- QUEUE_NORMAL
-};
-
-/*! \brief Check if members are available
- *
- * This function checks to see if members are available to be called. If any member
- * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
- * the appropriate reason why is returned
- */
-static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty, int min_penalty)
-{
- struct member *member;
- struct ao2_iterator mem_iter;
- enum queue_member_status result = QUEUE_NO_MEMBERS;
-
- ao2_lock(q);
- mem_iter = ao2_iterator_init(q->members, 0);
- for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
- if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
- continue;
-
- switch (member->status) {
- case AST_DEVICE_INVALID:
- /* nothing to do */
- break;
- case AST_DEVICE_UNAVAILABLE:
- if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)
- result = QUEUE_NO_REACHABLE_MEMBERS;
- break;
- default:
- if (member->paused) {
- result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
- } else {
- ao2_unlock(q);
- ao2_ref(member, -1);
- return QUEUE_NORMAL;
- }
- break;
- }
- }
-
- ao2_unlock(q);
- return result;
-}
-
-struct statechange {
- AST_LIST_ENTRY(statechange) entry;
- int state;
- char dev[0];
-};
-/*! \brief set a member's status based on device state of that member's state_interface*/
-static void *handle_statechange(struct statechange *sc)
-{
- struct call_queue *q;
- struct member *cur;
- struct ao2_iterator mem_iter;
- struct member_interface *curint;
- struct ao2_iterator queue_iter;
- char *loc;
- char *technology;
-
- technology = ast_strdupa(sc->dev);
- loc = strchr(technology, '/');
- if (loc) {
- *loc++ = '\0';
- } else {
- return NULL;
- }
-
- AST_LIST_LOCK(&interfaces);
- AST_LIST_TRAVERSE(&interfaces, curint, list) {
- char *interface;
- char *slash_pos;
- interface = ast_strdupa(curint->interface);
- if ((slash_pos = strchr(interface, '/')))
- if ((slash_pos = strchr(slash_pos + 1, '/')))
- *slash_pos = '\0';
-
- if (!strcasecmp(interface, sc->dev))
- break;
- }
- AST_LIST_UNLOCK(&interfaces);
-
- if (!curint) {
- ast_debug(3, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state));
- return NULL;
- }
-
- ast_debug(1, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- ao2_lock(q);
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((cur = ao2_iterator_next(&mem_iter))) {
- char *interface;
- char *slash_pos;
- interface = ast_strdupa(cur->state_interface);
- if ((slash_pos = strchr(interface, '/')))
- if ((slash_pos = strchr(slash_pos + 1, '/')))
- *slash_pos = '\0';
-
- if (strcasecmp(sc->dev, interface)) {
- ao2_ref(cur, -1);
- continue;
- }
-
- if (cur->status != sc->state) {
- cur->status = sc->state;
- if (q->maskmemberstatus) {
- ao2_ref(cur, -1);
- continue;
- }
-
- manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "MemberName: %s\r\n"
- "Membership: %s\r\n"
- "Penalty: %d\r\n"
- "CallsTaken: %d\r\n"
- "LastCall: %d\r\n"
- "Status: %d\r\n"
- "Paused: %d\r\n",
- q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
- cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
- }
- ao2_ref(cur, -1);
- }
- queue_unref(q);
- ao2_unlock(q);
- }
-
- return NULL;
-}
-
-/*!
- * \brief Data used by the device state thread
- */
-static struct {
- /*! Set to 1 to stop the thread */
- unsigned int stop:1;
- /*! The device state monitoring thread */
- pthread_t thread;
- /*! Lock for the state change queue */
- ast_mutex_t lock;
- /*! Condition for the state change queue */
- ast_cond_t cond;
- /*! Queue of state changes */
- AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
-} device_state = {
- .thread = AST_PTHREADT_NULL,
-};
-
-/*! \brief Consumer of the statechange queue */
-static void *device_state_thread(void *data)
-{
- struct statechange *sc = NULL;
-
- while (!device_state.stop) {
- ast_mutex_lock(&device_state.lock);
- if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
- ast_cond_wait(&device_state.cond, &device_state.lock);
- sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
- }
- ast_mutex_unlock(&device_state.lock);
-
- /* Check to see if we were woken up to see the request to stop */
- if (device_state.stop)
- break;
-
- if (!sc)
- continue;
-
- handle_statechange(sc);
-
- ast_free(sc);
- sc = NULL;
- }
-
- if (sc)
- ast_free(sc);
-
- while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
- ast_free(sc);
-
- return NULL;
-}
-/*! \brief Producer of the statechange queue */
-static int statechange_queue(const char *dev, enum ast_device_state state)
-{
- struct statechange *sc;
-
- if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
- return 0;
-
- sc->state = state;
- strcpy(sc->dev, dev);
-
- ast_mutex_lock(&device_state.lock);
- AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
- ast_cond_signal(&device_state.cond);
- ast_mutex_unlock(&device_state.lock);
-
- return 0;
-}
-static void device_state_cb(const struct ast_event *event, void *unused)
-{
- enum ast_device_state state;
- const char *device;
-
- state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
- device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
-
- if (ast_strlen_zero(device)) {
- ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
- return;
- }
-
- statechange_queue(device, state);
-}
-
-/*! \brief allocate space for new queue member and set fields based on parameters passed */
-static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
-{
- struct member *cur;
-
- if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
- cur->penalty = penalty;
- cur->paused = paused;
- ast_copy_string(cur->interface, interface, sizeof(cur->interface));
- if (!ast_strlen_zero(state_interface))
- ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
- else
- ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
- if (!ast_strlen_zero(membername))
- ast_copy_string(cur->membername, membername, sizeof(cur->membername));
- else
- ast_copy_string(cur->membername, interface, sizeof(cur->membername));
- if (!strchr(cur->interface, '/'))
- ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
- cur->status = ast_device_state(cur->state_interface);
- }
-
- return cur;
-}
-
-
-static int compress_char(const char c)
-{
- if (c < 32)
- return 0;
- else if (c > 96)
- return c - 64;
- else
- return c - 32;
-}
-
-static int member_hash_fn(const void *obj, const int flags)
-{
- const struct member *mem = obj;
- const char *chname = strchr(mem->interface, '/');
- int ret = 0, i;
- if (!chname)
- chname = mem->interface;
- for (i = 0; i < 5 && chname[i]; i++)
- ret += compress_char(chname[i]) << (i * 6);
- return ret;
-}
-
-static int member_cmp_fn(void *obj1, void *obj2, int flags)
-{
- struct member *mem1 = obj1, *mem2 = obj2;
- return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
-}
-
-static void init_queue(struct call_queue *q)
-{
- int i;
- struct penalty_rule *pr_iter;
-
- q->dead = 0;
- q->retry = DEFAULT_RETRY;
- q->timeout = -1;
- q->maxlen = 0;
- q->announcefrequency = 0;
- q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
- q->announceholdtime = 0;
- q->announceholdtime = 1;
- q->roundingseconds = 0; /* Default - don't announce seconds */
- q->servicelevel = 0;
- q->ringinuse = 1;
- q->setinterfacevar = 0;
- q->setqueuevar = 0;
- q->setqueueentryvar = 0;
- q->autofill = autofill_default;
- q->montype = montype_default;
- q->monfmt[0] = '\0';
- q->reportholdtime = 0;
- q->wrapuptime = 0;
- q->autofill = 0;
- q->joinempty = 0;
- q->leavewhenempty = 0;
- q->memberdelay = 0;
- q->maskmemberstatus = 0;
- q->eventwhencalled = 0;
- q->weight = 0;
- q->timeoutrestart = 0;
- q->periodicannouncefrequency = 0;
- if (!q->members) {
- if (q->strategy == QUEUE_STRATEGY_LINEAR)
- /* linear strategy depends on order, so we have to place all members in a single bucket */
- q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
- else
- q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
- }
- q->membercount = 0;
- q->found = 1;
-
- ast_string_field_set(q, sound_next, "queue-youarenext");
- ast_string_field_set(q, sound_thereare, "queue-thereare");
- ast_string_field_set(q, sound_calls, "queue-callswaiting");
- ast_string_field_set(q, sound_holdtime, "queue-holdtime");
- ast_string_field_set(q, sound_minutes, "queue-minutes");
- ast_string_field_set(q, sound_seconds, "queue-seconds");
- ast_string_field_set(q, sound_thanks, "queue-thankyou");
- ast_string_field_set(q, sound_lessthan, "queue-less-than");
- ast_string_field_set(q, sound_reporthold, "queue-reporthold");
-
- if ((q->sound_periodicannounce[0] = ast_str_create(32)))
- ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
-
- for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
- if (q->sound_periodicannounce[i])
- ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
- }
-
- while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
- ast_free(pr_iter);
-}
-
-static void clear_queue(struct call_queue *q)
-{
- q->holdtime = 0;
- q->callscompleted = 0;
- q->callsabandoned = 0;
- q->callscompletedinsl = 0;
- q->wrapuptime = 0;
-}
-
-static int add_to_interfaces(const char *interface)
-{
- struct member_interface *curint;
-
- AST_LIST_LOCK(&interfaces);
- AST_LIST_TRAVERSE(&interfaces, curint, list) {
- if (!strcasecmp(curint->interface, interface))
- break;
- }
-
- if (curint) {
- AST_LIST_UNLOCK(&interfaces);
- return 0;
- }
-
- ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
-
- if ((curint = ast_calloc(1, sizeof(*curint)))) {
- ast_copy_string(curint->interface, interface, sizeof(curint->interface));
- AST_LIST_INSERT_HEAD(&interfaces, curint, list);
- }
- AST_LIST_UNLOCK(&interfaces);
-
- return 0;
-}
-
-static int interface_exists_global(const char *interface)
-{
- struct call_queue *q;
- struct member *mem, tmpmem;
- struct ao2_iterator queue_iter, mem_iter;
- int ret = 0;
-
- ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- ao2_lock(q);
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((mem = ao2_iterator_next(&mem_iter))) {
- if (!strcasecmp(mem->state_interface, interface)) {
- ao2_ref(mem, -1);
- ret = 1;
- break;
- }
- }
- ao2_unlock(q);
- queue_unref(q);
- }
-
- return ret;
-}
-
-static int remove_from_interfaces(const char *interface)
-{
- struct member_interface *curint;
-
- AST_LIST_LOCK(&interfaces);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
- if (!strcasecmp(curint->interface, interface)) {
- if (!interface_exists_global(interface)) {
- ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
- AST_LIST_REMOVE_CURRENT(list);
- ast_free(curint);
- }
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- AST_LIST_UNLOCK(&interfaces);
-
- return 0;
-}
-
-static void clear_and_free_interfaces(void)
-{
- struct member_interface *curint;
-
- AST_LIST_LOCK(&interfaces);
- while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
- ast_free(curint);
- AST_LIST_UNLOCK(&interfaces);
-}
-
-/*Note: call this with the rule_lists locked */
-static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
-{
- char *timestr, *maxstr, *minstr, *contentdup;
- struct penalty_rule *rule = NULL, *rule_iter;
- struct rule_list *rl_iter;
- int time, inserted = 0;
-
- if (!(rule = ast_calloc(1, sizeof(*rule)))) {
- ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum);
- return -1;
- }
-
- contentdup = ast_strdupa(content);
-
- if (!(maxstr = strchr(contentdup, ','))) {
- ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
- ast_free(rule);
- return -1;
- }
-
- *maxstr++ = '\0';
- timestr = contentdup;
-
- if ((time = atoi(timestr)) < 0) {
- ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
- ast_free(rule);
- return -1;
- }
-
- rule->time = time;
-
- if ((minstr = strchr(maxstr,',')))
- *minstr++ = '\0';
-
- /* The last check will evaluate true if either no penalty change is indicated for a given rule
- * OR if a min penalty change is indicated but no max penalty change is */
- if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
- rule->max_relative = 1;
- }
-
- rule->max_value = atoi(maxstr);
-
- if (!ast_strlen_zero(minstr)) {
- if (*minstr == '+' || *minstr == '-')
- rule->min_relative = 1;
- rule->min_value = atoi(minstr);
- } else /*there was no minimum specified, so assume this means no change*/
- rule->min_relative = 1;
-
- /*We have the rule made, now we need to insert it where it belongs*/
- AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
- if (strcasecmp(rl_iter->name, list_name))
- continue;
-
- AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
- if (rule->time < rule_iter->time) {
- AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
- inserted = 1;
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
-
- if (!inserted) {
- AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
- }
- }
-
- return 0;
-}
-
-/*! \brief Configure a queue parameter.
-\par
- For error reporting, line number is passed for .conf static configuration.
- For Realtime queues, linenum is -1.
- The failunknown flag is set for config files (and static realtime) to show
- errors for unknown parameters. It is cleared for dynamic realtime to allow
- extra fields in the tables. */
-static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
-{
- if (!strcasecmp(param, "musicclass") ||
- !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
- ast_string_field_set(q, moh, val);
- } else if (!strcasecmp(param, "announce")) {
- ast_string_field_set(q, announce, val);
- } else if (!strcasecmp(param, "context")) {
- ast_string_field_set(q, context, val);
- } else if (!strcasecmp(param, "timeout")) {
- q->timeout = atoi(val);
- if (q->timeout < 0)
- q->timeout = DEFAULT_TIMEOUT;
- } else if (!strcasecmp(param, "ringinuse")) {
- q->ringinuse = ast_true(val);
- } else if (!strcasecmp(param, "setinterfacevar")) {
- q->setinterfacevar = ast_true(val);
- } else if (!strcasecmp(param, "setqueuevar")) {
- q->setqueuevar = ast_true(val);
- } else if (!strcasecmp(param, "setqueueentryvar")) {
- q->setqueueentryvar = ast_true(val);
- } else if (!strcasecmp(param, "monitor-format")) {
- ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
- } else if (!strcasecmp(param, "membermacro")) {
- ast_string_field_set(q, membermacro, val);
- } else if (!strcasecmp(param, "membergosub")) {
- ast_string_field_set(q, membergosub, val);
- } else if (!strcasecmp(param, "queue-youarenext")) {
- ast_string_field_set(q, sound_next, val);
- } else if (!strcasecmp(param, "queue-thereare")) {
- ast_string_field_set(q, sound_thereare, val);
- } else if (!strcasecmp(param, "queue-callswaiting")) {
- ast_string_field_set(q, sound_calls, val);
- } else if (!strcasecmp(param, "queue-holdtime")) {
- ast_string_field_set(q, sound_holdtime, val);
- } else if (!strcasecmp(param, "queue-minutes")) {
- ast_string_field_set(q, sound_minutes, val);
- } else if (!strcasecmp(param, "queue-seconds")) {
- ast_string_field_set(q, sound_seconds, val);
- } else if (!strcasecmp(param, "queue-lessthan")) {
- ast_string_field_set(q, sound_lessthan, val);
- } else if (!strcasecmp(param, "queue-thankyou")) {
- ast_string_field_set(q, sound_thanks, val);
- } else if (!strcasecmp(param, "queue-callerannounce")) {
- ast_string_field_set(q, sound_callerannounce, val);
- } else if (!strcasecmp(param, "queue-reporthold")) {
- ast_string_field_set(q, sound_reporthold, val);
- } else if (!strcasecmp(param, "announce-frequency")) {
- q->announcefrequency = atoi(val);
- } else if (!strcasecmp(param, "min-announce-frequency")) {
- q->minannouncefrequency = atoi(val);
- ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
- } else if (!strcasecmp(param, "announce-round-seconds")) {
- q->roundingseconds = atoi(val);
- /* Rounding to any other values just doesn't make sense... */
- if (!(q->roundingseconds == 0 || q->roundingseconds == 1 || q->roundingseconds == 5 || q->roundingseconds == 10
- || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
- if (linenum >= 0) {
- ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
- "using 0 instead for queue '%s' at line %d of queues.conf\n",
- val, param, q->name, linenum);
- } else {
- ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
- "using 0 instead for queue '%s'\n", val, param, q->name);
- }
- q->roundingseconds=0;
- }
- } else if (!strcasecmp(param, "announce-holdtime")) {
- if (!strcasecmp(val, "once"))
- q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
- else if (ast_true(val))
- q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
- else
- q->announceholdtime = 0;
- } else if (!strcasecmp(param, "announce-position")) {
- q->announceposition = ast_true(val);
- } else if (!strcasecmp(param, "periodic-announce")) {
- if (strchr(val, ',')) {
- char *s, *buf = ast_strdupa(val);
- unsigned int i = 0;
-
- while ((s = strsep(&buf, ",|"))) {
- if (!q->sound_periodicannounce[i])
- q->sound_periodicannounce[i] = ast_str_create(16);
- ast_str_set(&q->sound_periodicannounce[i], 0, s);
- i++;
- if (i == MAX_PERIODIC_ANNOUNCEMENTS)
- break;
- }
- } else {
- ast_str_set(&q->sound_periodicannounce[0], 0, val);
- }
- } else if (!strcasecmp(param, "periodic-announce-frequency")) {
- q->periodicannouncefrequency = atoi(val);
- } else if (!strcasecmp(param, "retry")) {
- q->retry = atoi(val);
- if (q->retry <= 0)
- q->retry = DEFAULT_RETRY;
- } else if (!strcasecmp(param, "wrapuptime")) {
- q->wrapuptime = atoi(val);
- } else if (!strcasecmp(param, "autofill")) {
- q->autofill = ast_true(val);
- } else if (!strcasecmp(param, "monitor-type")) {
- if (!strcasecmp(val, "mixmonitor"))
- q->montype = 1;
- } else if (!strcasecmp(param, "autopause")) {
- q->autopause = ast_true(val);
- } else if (!strcasecmp(param, "maxlen")) {
- q->maxlen = atoi(val);
- if (q->maxlen < 0)
- q->maxlen = 0;
- } else if (!strcasecmp(param, "servicelevel")) {
- q->servicelevel= atoi(val);
- } else if (!strcasecmp(param, "strategy")) {
- /* We already have set this, no need to do it again */
- return;
- } else if (!strcasecmp(param, "joinempty")) {
- if (!strcasecmp(val, "loose"))
- q->joinempty = QUEUE_EMPTY_LOOSE;
- else if (!strcasecmp(val, "strict"))
- q->joinempty = QUEUE_EMPTY_STRICT;
- else if (ast_true(val))
- q->joinempty = QUEUE_EMPTY_NORMAL;
- else
- q->joinempty = 0;
- } else if (!strcasecmp(param, "leavewhenempty")) {
- if (!strcasecmp(val, "loose"))
- q->leavewhenempty = QUEUE_EMPTY_LOOSE;
- else if (!strcasecmp(val, "strict"))
- q->leavewhenempty = QUEUE_EMPTY_STRICT;
- else if (ast_true(val))
- q->leavewhenempty = QUEUE_EMPTY_NORMAL;
- else
- q->leavewhenempty = 0;
- } else if (!strcasecmp(param, "eventmemberstatus")) {
- q->maskmemberstatus = !ast_true(val);
- } else if (!strcasecmp(param, "eventwhencalled")) {
- if (!strcasecmp(val, "vars")) {
- q->eventwhencalled = QUEUE_EVENT_VARIABLES;
- } else {
- q->eventwhencalled = ast_true(val) ? 1 : 0;
- }
- } else if (!strcasecmp(param, "reportholdtime")) {
- q->reportholdtime = ast_true(val);
- } else if (!strcasecmp(param, "memberdelay")) {
- q->memberdelay = atoi(val);
- } else if (!strcasecmp(param, "weight")) {
- q->weight = atoi(val);
- if (q->weight)
- use_weight++;
- /* With Realtime queues, if the last queue using weights is deleted in realtime,
- we will not see any effect on use_weight until next reload. */
- } else if (!strcasecmp(param, "timeoutrestart")) {
- q->timeoutrestart = ast_true(val);
- } else if (!strcasecmp(param, "defaultrule")) {
- ast_string_field_set(q, defaultrule, val);
- } else if (failunknown) {
- if (linenum >= 0) {
- ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
- q->name, param, linenum);
- } else {
- ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
- }
- }
-}
-
-static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str, const char* state_interface)
-{
- struct member *m, tmpmem;
- int penalty = 0;
- int paused = 0;
-
- if (penalty_str) {
- penalty = atoi(penalty_str);
- if (penalty < 0)
- penalty = 0;
- }
-
- if (paused_str) {
- paused = atoi(paused_str);
- if (paused < 0)
- paused = 0;
- }
-
- /* Find the member, or the place to put a new one. */
- ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
- m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
-
- /* Create a new one if not found, else update penalty */
- if (!m) {
- if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
- m->dead = 0;
- m->realtime = 1;
- add_to_interfaces(m->state_interface);
- ao2_link(q->members, m);
- ao2_ref(m, -1);
- m = NULL;
- q->membercount++;
- }
- } else {
- m->dead = 0; /* Do not delete this one. */
- if (paused_str)
- m->paused = paused;
- if (strcasecmp(state_interface, m->state_interface)) {
- remove_from_interfaces(m->state_interface);
- ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
- add_to_interfaces(m->state_interface);
- }
- m->penalty = penalty;
- ao2_ref(m, -1);
- }
-}
-
-static void free_members(struct call_queue *q, int all)
-{
- /* Free non-dynamic members */
- struct member *cur;
- struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
-
- while ((cur = ao2_iterator_next(&mem_iter))) {
- if (all || !cur->dynamic) {
- ao2_unlink(q->members, cur);
- remove_from_interfaces(cur->state_interface);
- q->membercount--;
- }
- ao2_ref(cur, -1);
- }
-}
-
-static void destroy_queue(void *obj)
-{
- struct call_queue *q = obj;
- int i;
-
- ast_debug(0, "Queue destructor called for queue '%s'!\n", q->name);
-
- free_members(q, 1);
- ast_string_field_free_memory(q);
- for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
- if (q->sound_periodicannounce[i])
- free(q->sound_periodicannounce[i]);
- }
- ao2_ref(q->members, -1);
-}
-
-static struct call_queue *alloc_queue(const char *queuename)
-{
- struct call_queue *q;
-
- if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
- if (ast_string_field_init(q, 64)) {
- free(q);
- return NULL;
- }
- ast_string_field_set(q, name, queuename);
- }
- return q;
-}
-
-/*!\brief Reload a single queue via realtime.
- \return Return the queue, or NULL if it doesn't exist.
- \note Should be called with the global qlock locked. */
-static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
-{
- struct ast_variable *v;
- struct call_queue *q, tmpq = {
- .name = queuename,
- };
- struct member *m;
- struct ao2_iterator mem_iter;
- char *interface = NULL;
- const char *tmp_name;
- char *tmp;
- char tmpbuf[64]; /* Must be longer than the longest queue param name. */
-
- /* Static queues override realtime. */
- if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
- ao2_lock(q);
- if (!q->realtime) {
- if (q->dead) {
- ao2_unlock(q);
- queue_unref(q);
- return NULL;
- } else {
- ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
- ao2_unlock(q);
- return q;
- }
- }
- queue_unref(q);
- } else if (!member_config)
- /* Not found in the list, and it's not realtime ... */
- return NULL;
-
- /* Check if queue is defined in realtime. */
- if (!queue_vars) {
- /* Delete queue from in-core list if it has been deleted in realtime. */
- if (q) {
- /*! \note Hmm, can't seem to distinguish a DB failure from a not
- found condition... So we might delete an in-core queue
- in case of DB failure. */
- ast_debug(1, "Queue %s not found in realtime.\n", queuename);
-
- q->dead = 1;
- /* Delete if unused (else will be deleted when last caller leaves). */
- ao2_unlink(queues, q);
- ao2_unlock(q);
- queue_unref(q);
- }
- return NULL;
- }
-
- /* Create a new queue if an in-core entry does not exist yet. */
- if (!q) {
- if (!(q = alloc_queue(queuename)))
- return NULL;
- ao2_lock(q);
- clear_queue(q);
- q->realtime = 1;
- init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
- ao2_link(queues, q);
- }
-
- memset(tmpbuf, 0, sizeof(tmpbuf));
- for (v = queue_vars; v; v = v->next) {
- /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
- if ((tmp = strchr(v->name, '_'))) {
- ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
- tmp_name = tmpbuf;
- tmp = tmpbuf;
- while ((tmp = strchr(tmp, '_')))
- *tmp++ = '-';
- } else
- tmp_name = v->name;
- queue_set_param(q, tmp_name, v->value, -1, 0);
- }
-
- /* Temporarily set realtime members dead so we can detect deleted ones.
- * Also set the membercount correctly for realtime*/
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- q->membercount++;
- if (m->realtime)
- m->dead = 1;
- ao2_ref(m, -1);
- }
-
- while ((interface = ast_category_browse(member_config, interface))) {
- rt_handle_member_record(q, interface,
- ast_variable_retrieve(member_config, interface, "membername"),
- ast_variable_retrieve(member_config, interface, "penalty"),
- ast_variable_retrieve(member_config, interface, "paused"),
- ast_variable_retrieve(member_config, interface, "state_interface"));
- }
-
- /* Delete all realtime members that have been deleted in DB. */
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- if (m->dead) {
- ao2_unlink(q->members, m);
- remove_from_interfaces(m->state_interface);
- q->membercount--;
- }
- ao2_ref(m, -1);
- }
-
- ao2_unlock(q);
-
- return q;
-}
-
-static struct call_queue *load_realtime_queue(const char *queuename)
-{
- struct ast_variable *queue_vars;
- struct ast_config *member_config = NULL;
- struct call_queue *q = NULL, tmpq = {
- .name = queuename,
- };
-
- /* Find the queue in the in-core list first. */
- q = ao2_find(queues, &tmpq, OBJ_POINTER);
-
- if (!q || q->realtime) {
- /*! \note Load from realtime before taking the global qlock, to avoid blocking all
- queue operations while waiting for the DB.
-
- This will be two separate database transactions, so we might
- see queue parameters as they were before another process
- changed the queue and member list as it was after the change.
- Thus we might see an empty member list when a queue is
- deleted. In practise, this is unlikely to cause a problem. */
-
- queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
- if (queue_vars) {
- member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
- if (!member_config) {
- ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
- ast_variables_destroy(queue_vars);
- return NULL;
- }
- }
-
- ao2_lock(queues);
- q = find_queue_by_name_rt(queuename, queue_vars, member_config);
- if (member_config)
- ast_config_destroy(member_config);
- if (queue_vars)
- ast_variables_destroy(queue_vars);
- ao2_unlock(queues);
-
- } else {
- update_realtime_members(q);
- }
- return q;
-}
-
-static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
-{
- struct ast_variable *var;
- int ret = -1;
-
- if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL)))
- return ret;
- while (var) {
- if (!strcmp(var->name, "uniqueid"))
- break;
- var = var->next;
- }
- if (var && !ast_strlen_zero(var->value)) {
- if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
- ret = 0;
- }
- return ret;
-}
-
-static void update_realtime_members(struct call_queue *q)
-{
- struct ast_config *member_config = NULL;
- struct member *m;
- char *interface = NULL;
- struct ao2_iterator mem_iter;
-
- member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL);
- if (!member_config) {
- /*This queue doesn't have realtime members*/
- ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
- return;
- }
-
- ao2_lock(q);
-
- /* Temporarily set realtime members dead so we can detect deleted ones.*/
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- if (m->realtime)
- m->dead = 1;
- ao2_ref(m, -1);
- }
-
- while ((interface = ast_category_browse(member_config, interface))) {
- rt_handle_member_record(q, interface,
- S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
- ast_variable_retrieve(member_config, interface, "penalty"),
- ast_variable_retrieve(member_config, interface, "paused"),
- ast_variable_retrieve(member_config, interface, "state_interface"));
- }
-
- /* Delete all realtime members that have been deleted in DB. */
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- if (m->dead) {
- ao2_unlink(q->members, m);
- remove_from_interfaces(m->state_interface);
- q->membercount--;
- }
- ao2_ref(m, -1);
- }
- ao2_unlock(q);
-}
-
-static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
-{
- struct call_queue *q;
- struct queue_ent *cur, *prev = NULL;
- int res = -1;
- int pos = 0;
- int inserted = 0;
- enum queue_member_status stat;
-
- if (!(q = load_realtime_queue(queuename)))
- return res;
-
- ao2_lock(queues);
- ao2_lock(q);
-
- /* This is our one */
- stat = get_member_status(q, qe->max_penalty, qe->min_penalty);
- if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
- *reason = QUEUE_JOINEMPTY;
- else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS))
- *reason = QUEUE_JOINUNAVAIL;
- else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
- *reason = QUEUE_JOINUNAVAIL;
- else if (q->maxlen && (q->count >= q->maxlen))
- *reason = QUEUE_FULL;
- else {
- /* There's space for us, put us at the right position inside
- * the queue.
- * Take into account the priority of the calling user */
- inserted = 0;
- prev = NULL;
- cur = q->head;
- while (cur) {
- /* We have higher priority than the current user, enter
- * before him, after all the other users with priority
- * higher or equal to our priority. */
- if ((!inserted) && (qe->prio > cur->prio)) {
- insert_entry(q, prev, qe, &pos);
- inserted = 1;
- }
- cur->pos = ++pos;
- prev = cur;
- cur = cur->next;
- }
- /* No luck, join at the end of the queue */
- if (!inserted)
- insert_entry(q, prev, qe, &pos);
- ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
- ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
- ast_copy_string(qe->context, q->context, sizeof(qe->context));
- q->count++;
- res = 0;
- manager_event(EVENT_FLAG_CALL, "Join",
- "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
- qe->chan->name,
- S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
- S_OR(qe->chan->cid.cid_name, "unknown"),
- q->name, qe->pos, q->count, qe->chan->uniqueid );
- ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
- }
- ao2_unlock(q);
- ao2_unlock(queues);
-
- return res;
-}
-
-static int play_file(struct ast_channel *chan, const char *filename)
-{
- int res;
-
- ast_stopstream(chan);
-
- res = ast_streamfile(chan, filename, chan->language);
- if (!res)
- res = ast_waitstream(chan, AST_DIGIT_ANY);
-
- ast_stopstream(chan);
-
- return res;
-}
-
-static int valid_exit(struct queue_ent *qe, char digit)
-{
- int digitlen = strlen(qe->digits);
-
- /* Prevent possible buffer overflow */
- if (digitlen < sizeof(qe->digits) - 2) {
- qe->digits[digitlen] = digit;
- qe->digits[digitlen + 1] = '\0';
- } else {
- qe->digits[0] = '\0';
- return 0;
- }
-
- /* If there's no context to goto, short-circuit */
- if (ast_strlen_zero(qe->context))
- return 0;
-
- /* If the extension is bad, then reset the digits to blank */
- if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
- qe->digits[0] = '\0';
- return 0;
- }
-
- /* We have an exact match */
- if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
- qe->valid_digits = 1;
- /* Return 1 on a successful goto */
- return 1;
- }
-
- return 0;
-}
-
-static int say_position(struct queue_ent *qe, int ringing)
-{
- int res = 0, avgholdmins, avgholdsecs;
- time_t now;
-
- /* Let minannouncefrequency seconds pass between the start of each position announcement */
- time(&now);
- if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
- return 0;
-
- /* If either our position has changed, or we are over the freq timer, say position */
- if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
- return 0;
-
- if (ringing) {
- ast_indicate(qe->chan,-1);
- } else {
- ast_moh_stop(qe->chan);
- }
- if (qe->parent->announceposition) {
- /* Say we're next, if we are */
- if (qe->pos == 1) {
- res = play_file(qe->chan, qe->parent->sound_next);
- if (res)
- goto playout;
- else
- goto posout;
- } else {
- res = play_file(qe->chan, qe->parent->sound_thereare);
- if (res)
- goto playout;
- res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
- if (res)
- goto playout;
- res = play_file(qe->chan, qe->parent->sound_calls);
- if (res)
- goto playout;
- }
- }
- /* Round hold time to nearest minute */
- avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
-
- /* If they have specified a rounding then round the seconds as well */
- if (qe->parent->roundingseconds) {
- avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
- avgholdsecs *= qe->parent->roundingseconds;
- } else {
- avgholdsecs = 0;
- }
-
- ast_verb(3, "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
-
- /* If the hold time is >1 min, if it's enabled, and if it's not
- supposed to be only once and we have already said it, say it */
- if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
- (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
- res = play_file(qe->chan, qe->parent->sound_holdtime);
- if (res)
- goto playout;
-
- if (avgholdmins > 0) {
- if (avgholdmins < 2) {
- res = play_file(qe->chan, qe->parent->sound_lessthan);
- if (res)
- goto playout;
-
- res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
- if (res)
- goto playout;
- } else {
- res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
- if (res)
- goto playout;
- }
-
- res = play_file(qe->chan, qe->parent->sound_minutes);
- if (res)
- goto playout;
- }
- if (avgholdsecs>0) {
- res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
- if (res)
- goto playout;
-
- res = play_file(qe->chan, qe->parent->sound_seconds);
- if (res)
- goto playout;
- }
-
- }
-
-posout:
- if (qe->parent->announceposition) {
- ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
- qe->chan->name, qe->parent->name, qe->pos);
- }
- res = play_file(qe->chan, qe->parent->sound_thanks);
-
-playout:
- if ((res > 0 && !valid_exit(qe, res)) || res < 0)
- res = 0;
-
- /* Set our last_pos indicators */
- qe->last_pos = now;
- qe->last_pos_said = qe->pos;
-
- /* Don't restart music on hold if we're about to exit the caller from the queue */
- if (!res) {
- if (ringing)
- ast_indicate(qe->chan, AST_CONTROL_RINGING);
- else
- ast_moh_start(qe->chan, qe->moh, NULL);
- }
- return res;
-}
-
-static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
-{
- int oldvalue;
-
- /* Calculate holdtime using a recursive boxcar filter */
- /* Thanks to SRT for this contribution */
- /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
-
- ao2_lock(qe->parent);
- oldvalue = qe->parent->holdtime;
- qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
- ao2_unlock(qe->parent);
-}
-
-
-static void leave_queue(struct queue_ent *qe)
-{
- struct call_queue *q;
- struct queue_ent *cur, *prev = NULL;
- struct penalty_rule *pr_iter;
- int pos = 0;
-
- if (!(q = qe->parent))
- return;
- queue_ref(q);
- ao2_lock(q);
-
- prev = NULL;
- for (cur = q->head; cur; cur = cur->next) {
- if (cur == qe) {
- q->count--;
-
- /* Take us out of the queue */
- manager_event(EVENT_FLAG_CALL, "Leave",
- "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
- qe->chan->name, q->name, q->count, qe->chan->uniqueid);
- ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
- /* Take us out of the queue */
- if (prev)
- prev->next = cur->next;
- else
- q->head = cur->next;
- /* Free penalty rules */
- while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
- ast_free(pr_iter);
- } else {
- /* Renumber the people after us in the queue based on a new count */
- cur->pos = ++pos;
- prev = cur;
- }
- }
- ao2_unlock(q);
-
- /*If the queue is a realtime queue, check to see if it's still defined in real time*/
- if (q->realtime) {
- if (!ast_load_realtime("queues", "name", q->name, NULL))
- q->dead = 1;
- }
-
- if (q->dead) {
- /* It's dead and nobody is in it, so kill it */
- ao2_unlink(queues, q);
- queue_unref(q);
- }
- queue_unref(q);
-}
-
-/* Hang up a list of outgoing calls */
-static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
-{
- struct callattempt *oo;
-
- while (outgoing) {
- /* Hangup any existing lines we have open */
- if (outgoing->chan && (outgoing->chan != exception))
- ast_hangup(outgoing->chan);
- oo = outgoing;
- outgoing = outgoing->q_next;
- if (oo->member)
- ao2_ref(oo->member, -1);
- ast_free(oo);
- }
-}
-
-static int update_status(struct call_queue *q, struct member *member, int status)
-{
- struct member *cur;
- struct ao2_iterator mem_iter;
-
- /* Since a reload could have taken place, we have to traverse the list to
- be sure it's still valid */
- ao2_lock(q);
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((cur = ao2_iterator_next(&mem_iter))) {
- if (member != cur) {
- ao2_ref(cur, -1);
- continue;
- }
-
- cur->status = status;
- if (!q->maskmemberstatus) {
- manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "MemberName: %s\r\n"
- "Membership: %s\r\n"
- "Penalty: %d\r\n"
- "CallsTaken: %d\r\n"
- "LastCall: %d\r\n"
- "Status: %d\r\n"
- "Paused: %d\r\n",
- q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime": "static",
- cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
- }
- ao2_ref(cur, -1);
- }
- ao2_unlock(q);
- return 0;
-}
-
-static int update_dial_status(struct call_queue *q, struct member *member, int status)
-{
- if (status == AST_CAUSE_BUSY)
- status = AST_DEVICE_BUSY;
- else if (status == AST_CAUSE_UNREGISTERED)
- status = AST_DEVICE_UNAVAILABLE;
- else if (status == AST_CAUSE_NOSUCHDRIVER)
- status = AST_DEVICE_INVALID;
- else
- status = AST_DEVICE_UNKNOWN;
- return update_status(q, member, status);
-}
-
-/* traverse all defined queues which have calls waiting and contain this member
- return 0 if no other queue has precedence (higher weight) or 1 if found */
-static int compare_weight(struct call_queue *rq, struct member *member)
-{
- struct call_queue *q;
- struct member *mem;
- int found = 0;
- struct ao2_iterator queue_iter;
-
- /* &qlock and &rq->lock already set by try_calling()
- * to solve deadlock */
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- if (q == rq) { /* don't check myself, could deadlock */
- queue_unref(q);
- continue;
- }
- ao2_lock(q);
- if (q->count && q->members) {
- if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
- ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
- if (q->weight > rq->weight) {
- ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
- found = 1;
- }
- ao2_ref(mem, -1);
- }
- }
- ao2_unlock(q);
- if (found) {
- queue_unref(q);
- break;
- }
- queue_unref(q);
- }
- return found;
-}
-
-/*! \brief common hangup actions */
-static void do_hang(struct callattempt *o)
-{
- o->stillgoing = 0;
- ast_hangup(o->chan);
- o->chan = NULL;
-}
-
-static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
-{
- struct ast_str *buf = ast_str_alloca(len + 1);
- char *tmp;
-
- if (pbx_builtin_serialize_variables(chan, &buf)) {
- int i, j;
-
- /* convert "\n" to "\nVariable: " */
- strcpy(vars, "Variable: ");
- tmp = buf->str;
-
- for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
- vars[j] = tmp[i];
-
- if (tmp[i + 1] == '\0')
- break;
- if (tmp[i] == '\n') {
- vars[j++] = '\r';
- vars[j++] = '\n';
-
- ast_copy_string(&(vars[j]), "Variable: ", len - j);
- j += 9;
- }
- }
- if (j > len - 1)
- j = len - 1;
- vars[j - 2] = '\r';
- vars[j - 1] = '\n';
- vars[j] = '\0';
- } else {
- /* there are no channel variables; leave it blank */
- *vars = '\0';
- }
- return vars;
-}
-
-/*! \brief Part 2 of ring_one
- *
- * Does error checking before attempting to request a channel and call a member. This
- * function is only called from ring_one
- */
-static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
-{
- int res;
- int status;
- char tech[256];
- char *location;
-
- /* on entry here, we know that tmp->chan == NULL */
- if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
- (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
- ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
- (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
- if (qe->chan->cdr)
- ast_cdr_busy(qe->chan->cdr);
- tmp->stillgoing = 0;
- (*busies)++;
- return 0;
- }
-
- if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
- ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
- if (qe->chan->cdr)
- ast_cdr_busy(qe->chan->cdr);
- tmp->stillgoing = 0;
- return 0;
- }
-
- if (tmp->member->paused) {
- ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
- if (qe->chan->cdr)
- ast_cdr_busy(qe->chan->cdr);
- tmp->stillgoing = 0;
- return 0;
- }
- if (use_weight && compare_weight(qe->parent,tmp->member)) {
- ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
- if (qe->chan->cdr)
- ast_cdr_busy(qe->chan->cdr);
- tmp->stillgoing = 0;
- (*busies)++;
- return 0;
- }
-
- ast_copy_string(tech, tmp->interface, sizeof(tech));
- if ((location = strchr(tech, '/')))
- *location++ = '\0';
- else
- location = "";
-
- /* Request the peer */
- tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
- if (!tmp->chan) { /* If we can't, just go on to the next call */
- if (qe->chan->cdr)
- ast_cdr_busy(qe->chan->cdr);
- tmp->stillgoing = 0;
- update_dial_status(qe->parent, tmp->member, status);
-
- ao2_lock(qe->parent);
- qe->parent->rrpos++;
- qe->linpos++;
- ao2_unlock(qe->parent);
-
-
- (*busies)++;
- return 0;
- } else if (status != tmp->oldstatus)
- update_dial_status(qe->parent, tmp->member, status);
-
- tmp->chan->appl = "AppQueue";
- tmp->chan->data = "(Outgoing Line)";
- tmp->chan->whentohangup = 0;
- if (tmp->chan->cid.cid_num)
- ast_free(tmp->chan->cid.cid_num);
- tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
- if (tmp->chan->cid.cid_name)
- ast_free(tmp->chan->cid.cid_name);
- tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
- if (tmp->chan->cid.cid_ani)
- ast_free(tmp->chan->cid.cid_ani);
- tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
-
- /* Inherit specially named variables from parent channel */
- ast_channel_inherit_variables(qe->chan, tmp->chan);
-
- /* Presense of ADSI CPE on outgoing channel follows ours */
- tmp->chan->adsicpe = qe->chan->adsicpe;
-
- /* Inherit context and extension */
- if (!ast_strlen_zero(qe->chan->macrocontext))
- ast_copy_string(tmp->chan->dialcontext, qe->chan->macrocontext, sizeof(tmp->chan->dialcontext));
- else
- ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
- if (!ast_strlen_zero(qe->chan->macroexten))
- ast_copy_string(tmp->chan->exten, qe->chan->macroexten, sizeof(tmp->chan->exten));
- else
- ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
-
- /* Place the call, but don't wait on the answer */
- if ((res = ast_call(tmp->chan, location, 0))) {
- /* Again, keep going even if there's an error */
- ast_debug(1, "ast call on peer returned %d\n", res);
- ast_verb(3, "Couldn't call %s\n", tmp->interface);
- do_hang(tmp);
- (*busies)++;
- return 0;
- } else if (qe->parent->eventwhencalled) {
- char vars[2048];
-
- manager_event(EVENT_FLAG_AGENT, "AgentCalled",
- "Queue: %s\r\n"
- "AgentCalled: %s\r\n"
- "AgentName: %s\r\n"
- "ChannelCalling: %s\r\n"
- "DestinationChannel: %s\r\n"
- "CallerIDNum: %s\r\n"
- "CallerIDName: %s\r\n"
- "Context: %s\r\n"
- "Extension: %s\r\n"
- "Priority: %d\r\n"
- "%s",
- qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
- tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
- tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
- qe->chan->context, qe->chan->exten, qe->chan->priority,
- qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
- ast_verb(3, "Called %s\n", tmp->interface);
- }
-
- return 1;
-}
-
-/*! \brief find the entry with the best metric, or NULL */
-static struct callattempt *find_best(struct callattempt *outgoing)
-{
- struct callattempt *best = NULL, *cur;
-
- for (cur = outgoing; cur; cur = cur->q_next) {
- if (cur->stillgoing && /* Not already done */
- !cur->chan && /* Isn't already going */
- (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
- best = cur;
- }
- }
-
- return best;
-}
-
-/*! \brief Place a call to a queue member
- *
- * Once metrics have been calculated for each member, this function is used
- * to place a call to the appropriate member (or members). The low-level
- * channel-handling and error detection is handled in ring_entry
- *
- * Returns 1 if a member was called successfully, 0 otherwise
- */
-static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
-{
- int ret = 0;
-
- while (ret == 0) {
- struct callattempt *best = find_best(outgoing);
- if (!best) {
- ast_debug(1, "Nobody left to try ringing in queue\n");
- break;
- }
- if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
- struct callattempt *cur;
- /* Ring everyone who shares this best metric (for ringall) */
- for (cur = outgoing; cur; cur = cur->q_next) {
- if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
- ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
- ret |= ring_entry(qe, cur, busies);
- }
- }
- } else {
- /* Ring just the best channel */
- ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
- ret = ring_entry(qe, best, busies);
- }
- }
-
- return ret;
-}
-
-static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
-{
- struct callattempt *best = find_best(outgoing);
-
- if (best) {
- /* Ring just the best channel */
- ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
- qe->parent->rrpos = best->metric % 1000;
- } else {
- /* Just increment rrpos */
- if (qe->parent->wrapped) {
- /* No more channels, start over */
- qe->parent->rrpos = 0;
- } else {
- /* Prioritize next entry */
- qe->parent->rrpos++;
- }
- }
- qe->parent->wrapped = 0;
-
- return 0;
-}
-
-static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
-{
- struct callattempt *best = find_best(outgoing);
-
- if (best) {
- /* Ring just the best channel */
- ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
- qe->linpos = best->metric % 1000;
- } else {
- /* Just increment rrpos */
- if (qe->linwrapped) {
- /* No more channels, start over */
- qe->linpos = 0;
- } else {
- /* Prioritize next entry */
- qe->linpos++;
- }
- }
- qe->linwrapped = 0;
-
- return 0;
-}
-
-static int say_periodic_announcement(struct queue_ent *qe, int ringing)
-{
- int res = 0;
- time_t now;
-
- /* Get the current time */
- time(&now);
-
- /* Check to see if it is time to announce */
- if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
- return 0;
-
- /* Stop the music on hold so we can play our own file */
- if (ringing)
- ast_indicate(qe->chan,-1);
- else
- ast_moh_stop(qe->chan);
-
- ast_verb(3, "Playing periodic announcement\n");
-
- /* Check to make sure we have a sound file. If not, reset to the first sound file */
- if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS ||
- !qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound] ||
- ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
- qe->last_periodic_announce_sound = 0;
- }
-
- /* play the announcement */
- res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
-
- if ((res > 0 && !valid_exit(qe, res)) || res < 0)
- res = 0;
-
- /* Resume Music on Hold if the caller is going to stay in the queue */
- if (!res) {
- if (ringing)
- ast_indicate(qe->chan, AST_CONTROL_RINGING);
- else
- ast_moh_start(qe->chan, qe->moh, NULL);
- }
-
- /* update last_periodic_announce_time */
- qe->last_periodic_announce_time = now;
-
- /* Update the current periodic announcement to the next announcement */
- qe->last_periodic_announce_sound++;
-
- return res;
-}
-
-static void record_abandoned(struct queue_ent *qe)
-{
- ao2_lock(qe->parent);
- set_queue_variables(qe);
- manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
- "Queue: %s\r\n"
- "Uniqueid: %s\r\n"
- "Position: %d\r\n"
- "OriginalPosition: %d\r\n"
- "HoldTime: %d\r\n",
- qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
-
- qe->parent->callsabandoned++;
- ao2_unlock(qe->parent);
-}
-
-/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
-static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
-{
- ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
- ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
- if (qe->parent->autopause) {
- if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
- ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
- } else {
- ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
- }
- }
- return;
-}
-
-#define AST_MAX_WATCHERS 256
-/*! \brief Wait for a member to answer the call
- *
- * \param[in] qe the queue_ent corresponding to the caller in the queue
- * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
- * \param[in] to the amount of time (in milliseconds) to wait for a response
- * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
- * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
- * \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
- * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
- */
-static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
-{
- const char *queue = qe->parent->name;
- struct callattempt *o, *start = NULL, *prev = NULL;
- int status;
- int numbusies = prebusies;
- int numnochan = 0;
- int stillgoing = 0;
- int orig = *to;
- struct ast_frame *f;
- struct callattempt *peer = NULL;
- struct ast_channel *winner;
- struct ast_channel *in = qe->chan;
- char on[80] = "";
- char membername[80] = "";
- long starttime = 0;
- long endtime = 0;
-#ifdef HAVE_EPOLL
- struct callattempt *epollo;
-#endif
-
- starttime = (long) time(NULL);
-#ifdef HAVE_EPOLL
- for (epollo = outgoing; epollo; epollo = epollo->q_next) {
- if (epollo->chan)
- ast_poll_channel_add(in, epollo->chan);
- }
-#endif
-
- while (*to && !peer) {
- int numlines, retry, pos = 1;
- struct ast_channel *watchers[AST_MAX_WATCHERS];
- watchers[0] = in;
- start = NULL;
-
- for (retry = 0; retry < 2; retry++) {
- numlines = 0;
- for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
- if (o->stillgoing) { /* Keep track of important channels */
- stillgoing = 1;
- if (o->chan) {
- watchers[pos++] = o->chan;
- if (!start)
- start = o;
- else
- prev->call_next = o;
- prev = o;
- }
- }
- numlines++;
- }
- if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
- (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
- break;
- /* On "ringall" strategy we only move to the next penalty level
- when *all* ringing phones are done in the current penalty level */
- ring_one(qe, outgoing, &numbusies);
- /* and retry... */
- }
- if (pos == 1 /* not found */) {
- if (numlines == (numbusies + numnochan)) {
- ast_debug(1, "Everyone is busy at this time\n");
- } else {
- ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
- }
- *to = 0;
- return NULL;
- }
- winner = ast_waitfor_n(watchers, pos, to);
- for (o = start; o; o = o->call_next) {
- if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
- if (!peer) {
- ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
- peer = o;
- }
- } else if (o->chan && (o->chan == winner)) {
-
- ast_copy_string(on, o->member->interface, sizeof(on));
- ast_copy_string(membername, o->member->membername, sizeof(membername));
-
- if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
- ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
- numnochan++;
- do_hang(o);
- winner = NULL;
- continue;
- } else if (!ast_strlen_zero(o->chan->call_forward)) {
- char tmpchan[256];
- char *stuff;
- char *tech;
-
- ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
- if ((stuff = strchr(tmpchan, '/'))) {
- *stuff++ = '\0';
- tech = tmpchan;
- } else {
- snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
- stuff = tmpchan;
- tech = "Local";
- }
- /* Before processing channel, go ahead and check for forwarding */
- ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
- /* Setup parameters */
- o->chan = ast_request(tech, in->nativeformats, stuff, &status);
- if (status != o->oldstatus)
- update_dial_status(qe->parent, o->member, status);
- if (!o->chan) {
- ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
- o->stillgoing = 0;
- numnochan++;
- } else {
- ast_channel_inherit_variables(in, o->chan);
- ast_channel_datastore_inherit(in, o->chan);
- if (o->chan->cid.cid_num)
- ast_free(o->chan->cid.cid_num);
- o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
-
- if (o->chan->cid.cid_name)
- ast_free(o->chan->cid.cid_name);
- o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
-
- ast_string_field_set(o->chan, accountcode, in->accountcode);
- o->chan->cdrflags = in->cdrflags;
-
- if (in->cid.cid_ani) {
- if (o->chan->cid.cid_ani)
- ast_free(o->chan->cid.cid_ani);
- o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
- }
- if (o->chan->cid.cid_rdnis)
- ast_free(o->chan->cid.cid_rdnis);
- o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
- if (ast_call(o->chan, tmpchan, 0)) {
- ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
- do_hang(o);
- numnochan++;
- }
- }
- /* Hangup the original channel now, in case we needed it */
- ast_hangup(winner);
- continue;
- }
- f = ast_read(winner);
- if (f) {
- if (f->frametype == AST_FRAME_CONTROL) {
- switch (f->subclass) {
- case AST_CONTROL_ANSWER:
- /* This is our guy if someone answered. */
- if (!peer) {
- ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
- peer = o;
- }
- break;
- case AST_CONTROL_BUSY:
- ast_verb(3, "%s is busy\n", o->chan->name);
- if (in->cdr)
- ast_cdr_busy(in->cdr);
- do_hang(o);
- endtime = (long) time(NULL);
- endtime -= starttime;
- rna(endtime*1000, qe, on, membername);
- if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
- if (qe->parent->timeoutrestart)
- *to = orig;
- ring_one(qe, outgoing, &numbusies);
- }
- numbusies++;
- break;
- case AST_CONTROL_CONGESTION:
- ast_verb(3, "%s is circuit-busy\n", o->chan->name);
- if (in->cdr)
- ast_cdr_busy(in->cdr);
- endtime = (long) time(NULL);
- endtime -= starttime;
- rna(endtime*1000, qe, on, membername);
- do_hang(o);
- if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
- if (qe->parent->timeoutrestart)
- *to = orig;
- ring_one(qe, outgoing, &numbusies);
- }
- numbusies++;
- break;
- case AST_CONTROL_RINGING:
- ast_verb(3, "%s is ringing\n", o->chan->name);
- break;
- case AST_CONTROL_OFFHOOK:
- /* Ignore going off hook */
- break;
- default:
- ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
- }
- }
- ast_frfree(f);
- } else {
- endtime = (long) time(NULL) - starttime;
- rna(endtime * 1000, qe, on, membername);
- do_hang(o);
- if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
- if (qe->parent->timeoutrestart)
- *to = orig;
- ring_one(qe, outgoing, &numbusies);
- }
- }
- }
- }
- if (winner == in) {
- f = ast_read(in);
- if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
- /* Got hung up */
- *to = -1;
- if (f) {
- ast_frfree(f);
- }
- return NULL;
- }
- if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
- ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
- *to = 0;
- ast_frfree(f);
- return NULL;
- }
- if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
- ast_verb(3, "User pressed digit: %c\n", f->subclass);
- *to = 0;
- *digit = f->subclass;
- ast_frfree(f);
- return NULL;
- }
- ast_frfree(f);
- }
- if (!*to) {
- for (o = start; o; o = o->call_next)
- rna(orig, qe, o->interface, o->member->membername);
- }
- }
-
-#ifdef HAVE_EPOLL
- for (epollo = outgoing; epollo; epollo = epollo->q_next) {
- if (epollo->chan)
- ast_poll_channel_del(in, epollo->chan);
- }
-#endif
-
- return peer;
-}
-/*! \brief Check if we should start attempting to call queue members
- *
- * The behavior of this function is dependent first on whether autofill is enabled
- * and second on whether the ring strategy is ringall. If autofill is not enabled,
- * then return true if we're the head of the queue. If autofill is enabled, then
- * we count the available members and see if the number of available members is enough
- * that given our position in the queue, we would theoretically be able to connect to
- * one of those available members
- */
-static int is_our_turn(struct queue_ent *qe)
-{
- struct queue_ent *ch;
- struct member *cur;
- int avl = 0;
- int idx = 0;
- int res;
-
- if (!qe->parent->autofill) {
- /* Atomically read the parent head -- does not need a lock */
- ch = qe->parent->head;
- /* If we are now at the top of the head, break out */
- if (ch == qe) {
- ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
- res = 1;
- } else {
- ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
- res = 0;
- }
-
- } else {
- /* This needs a lock. How many members are available to be served? */
- ao2_lock(qe->parent);
-
- ch = qe->parent->head;
-
- if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
- ast_debug(1, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
- avl = 1;
- } else {
- struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
- while ((cur = ao2_iterator_next(&mem_iter))) {
- switch (cur->status) {
- case AST_DEVICE_NOT_INUSE:
- case AST_DEVICE_UNKNOWN:
- if (!cur->paused)
- avl++;
- break;
- }
- ao2_ref(cur, -1);
- }
- }
-
- ast_debug(1, "There are %d available members.\n", avl);
-
- while ((idx < avl) && (ch) && !ch->pending && (ch != qe)) {
- idx++;
- ch = ch->next;
- }
-
- /* If the queue entry is within avl [the number of available members] calls from the top ... */
- if (ch && idx < avl) {
- ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
- res = 1;
- } else {
- ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
- res = 0;
- }
-
- ao2_unlock(qe->parent);
- }
-
- return res;
-}
-static void update_qe_rule(struct queue_ent *qe)
-{
- int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
- int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
- char max_penalty_str[20], min_penalty_str[20];
- /* a relative change to the penalty could put it below 0 */
- if (max_penalty < 0)
- max_penalty = 0;
- if (min_penalty < 0)
- min_penalty = 0;
- if (min_penalty > max_penalty)
- min_penalty = max_penalty;
- snprintf(max_penalty_str, sizeof(max_penalty_str) - 1, "%d", max_penalty);
- snprintf(min_penalty_str, sizeof(min_penalty_str) - 1, "%d", min_penalty);
- pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
- pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
- qe->max_penalty = max_penalty;
- qe->min_penalty = min_penalty;
- ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time);
- qe->pr = AST_LIST_NEXT(qe->pr, list);
-}
-
-/*! \brief The waiting areas for callers who are not actively calling members
- *
- * This function is one large loop. This function will return if a caller
- * either exits the queue or it becomes that caller's turn to attempt calling
- * queue members. Inside the loop, we service the caller with periodic announcements,
- * holdtime announcements, etc. as configured in queues.conf
- *
- * \retval 0 if the caller's turn has arrived
- * \retval -1 if the caller should exit the queue.
- */
-static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
-{
- int res = 0;
-
- /* This is the holding pen for callers 2 through maxlen */
- for (;;) {
- enum queue_member_status stat;
-
- if (is_our_turn(qe))
- break;
-
- /* If we have timed out, break out */
- if (qe->expire && (time(NULL) > qe->expire)) {
- *reason = QUEUE_TIMEOUT;
- break;
- }
-
- stat = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
-
- /* leave the queue if no agents, if enabled */
- if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
- *reason = QUEUE_LEAVEEMPTY;
- ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
- leave_queue(qe);
- break;
- }
-
- /* leave the queue if no reachable agents, if enabled */
- if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
- *reason = QUEUE_LEAVEUNAVAIL;
- ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
- leave_queue(qe);
- break;
- }
- if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
- *reason = QUEUE_LEAVEUNAVAIL;
- ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
- leave_queue(qe);
- break;
- }
-
- /* Make a position announcement, if enabled */
- if (qe->parent->announcefrequency &&
- (res = say_position(qe,ringing)))
- break;
-
- /* Make a periodic announcement, if enabled */
- if (qe->parent->periodicannouncefrequency &&
- (res = say_periodic_announcement(qe,ringing)))
- break;
-
- /* see if we need to move to the next penalty level for this queue */
- while (qe->pr && ((time(NULL) - qe->start) > qe->pr->time)) {
- update_qe_rule(qe);
- }
-
- /* Wait a second before checking again */
- if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
- if (res > 0 && !valid_exit(qe, res))
- res = 0;
- else
- break;
- }
- }
-
- return res;
-}
-
-static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
-{
- struct member *mem;
- struct call_queue *qtmp;
- struct ao2_iterator queue_iter;
-
- if (shared_lastcall) {
- queue_iter = ao2_iterator_init(queues, 0);
- while ((qtmp = ao2_iterator_next(&queue_iter))) {
- ao2_lock(qtmp);
- if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
- time(&mem->lastcall);
- mem->calls++;
- mem->lastqueue = q;
- ao2_ref(mem, -1);
- }
- ao2_unlock(qtmp);
- ao2_ref(qtmp, -1);
- }
- } else {
- ao2_lock(q);
- time(&member->lastcall);
- member->calls++;
- member->lastqueue = q;
- ao2_unlock(q);
- }
- ao2_lock(q);
- q->callscompleted++;
- if (callcompletedinsl)
- q->callscompletedinsl++;
- ao2_unlock(q);
- return 0;
-}
-
-/*! \brief Calculate the metric of each member in the outgoing callattempts
- *
- * A numeric metric is given to each member depending on the ring strategy used
- * by the queue. Members with lower metrics will be called before members with
- * higher metrics
- */
-static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
-{
- if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
- return -1;
-
- switch (q->strategy) {
- case QUEUE_STRATEGY_RINGALL:
- /* Everyone equal, except for penalty */
- tmp->metric = mem->penalty * 1000000;
- break;
- case QUEUE_STRATEGY_LINEAR:
- if (pos < qe->linpos) {
- tmp->metric = 1000 + pos;
- } else {
- if (pos > qe->linpos)
- /* Indicate there is another priority */
- qe->linwrapped = 1;
- tmp->metric = pos;
- }
- tmp->metric += mem->penalty * 1000000;
- break;
- case QUEUE_STRATEGY_RRMEMORY:
- if (pos < q->rrpos) {
- tmp->metric = 1000 + pos;
- } else {
- if (pos > q->rrpos)
- /* Indicate there is another priority */
- q->wrapped = 1;
- tmp->metric = pos;
- }
- tmp->metric += mem->penalty * 1000000;
- break;
- case QUEUE_STRATEGY_RANDOM:
- tmp->metric = ast_random() % 1000;
- tmp->metric += mem->penalty * 1000000;
- break;
- case QUEUE_STRATEGY_WRANDOM:
- tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
- break;
- case QUEUE_STRATEGY_FEWESTCALLS:
- tmp->metric = mem->calls;
- tmp->metric += mem->penalty * 1000000;
- break;
- case QUEUE_STRATEGY_LEASTRECENT:
- if (!mem->lastcall)
- tmp->metric = 0;
- else
- tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
- tmp->metric += mem->penalty * 1000000;
- break;
- default:
- ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
- break;
- }
- return 0;
-}
-
-enum agent_complete_reason {
- CALLER,
- AGENT,
- TRANSFER
-};
-
-static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
- const struct ast_channel *peer, const struct member *member, time_t callstart,
- char *vars, size_t vars_len, enum agent_complete_reason rsn)
-{
- const char *reason = NULL; /* silence dumb compilers */
-
- if (!qe->parent->eventwhencalled)
- return;
-
- switch (rsn) {
- case CALLER:
- reason = "caller";
- break;
- case AGENT:
- reason = "agent";
- break;
- case TRANSFER:
- reason = "transfer";
- break;
- }
-
- manager_event(EVENT_FLAG_AGENT, "AgentComplete",
- "Queue: %s\r\n"
- "Uniqueid: %s\r\n"
- "Channel: %s\r\n"
- "Member: %s\r\n"
- "MemberName: %s\r\n"
- "HoldTime: %ld\r\n"
- "TalkTime: %ld\r\n"
- "Reason: %s\r\n"
- "%s",
- queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
- (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
- qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
-}
-/*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
- *
- * Here is the process of this function
- * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
- * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
- * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
- * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
- * during each iteration, we call calc_metric to determine which members should be rung when.
- * 3. Call ring_one to place a call to the appropriate member(s)
- * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
- * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
- * 6. Start the monitor or mixmonitor if the option is set
- * 7. Remove the caller from the queue to allow other callers to advance
- * 8. Bridge the call.
- * 9. Do any post processing after the call has disconnected.
- *
- * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
- * \param[in] options the options passed as the third parameter to the Queue() application
- * \param[in] url the url passed as the fourth parameter to the Queue() application
- * \param[in,out] tries the number of times we have tried calling queue members
- * \param[out] noption set if the call to Queue() has the 'n' option set.
- * \param[in] agi the agi passed as the fifth parameter to the Queue() application
- * \param[in] macro the macro passed as the sixth parameter to the Queue() application
- * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
- * \param[in] ringing 1 if the 'r' option is set, otherwise 0
- */
-static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
-{
- struct member *cur;
- struct callattempt *outgoing = NULL; /* the list of calls we are building */
- int to, orig;
- char oldexten[AST_MAX_EXTENSION]="";
- char oldcontext[AST_MAX_CONTEXT]="";
- char queuename[256]="";
- char interfacevar[256]="";
- struct ast_channel *peer;
- struct ast_channel *which;
- struct callattempt *lpeer;
- struct member *member;
- struct ast_app *app;
- int res = 0, bridge = 0;
- int numbusies = 0;
- int x=0;
- char *announce = NULL;
- char digit = 0;
- time_t callstart;
- time_t now = time(NULL);
- struct ast_bridge_config bridge_config;
- char nondataquality = 1;
- char *agiexec = NULL;
- char *macroexec = NULL;
- char *gosubexec = NULL;
- int ret = 0;
- const char *monitorfilename;
- const char *monitor_exec;
- const char *monitor_options;
- char tmpid[256], tmpid2[256];
- char meid[1024], meid2[1024];
- char mixmonargs[1512];
- struct ast_app *mixmonapp = NULL;
- char *p;
- char vars[2048];
- int forwardsallowed = 1;
- int callcompletedinsl;
- struct ao2_iterator memi;
- struct ast_datastore *datastore;
-
- ast_channel_lock(qe->chan);
- datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
- ast_channel_unlock(qe->chan);
-
- memset(&bridge_config, 0, sizeof(bridge_config));
- tmpid[0] = 0;
- meid[0] = 0;
- time(&now);
-
- for (; options && *options; options++)
- switch (*options) {
- case 't':
- ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
- break;
- case 'T':
- ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
- break;
- case 'w':
- ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
- break;
- case 'W':
- ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
- break;
- case 'd':
- nondataquality = 0;
- break;
- case 'h':
- ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
- break;
- case 'H':
- ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
- break;
- case 'k':
- ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
- break;
- case 'K':
- ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
- break;
- case 'n':
- if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
- (*tries)++;
- else
- *tries = qe->parent->membercount;
- *noption = 1;
- break;
- case 'i':
- forwardsallowed = 0;
- break;
- case 'x':
- ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
- break;
- case 'X':
- ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
- break;
-
- }
-
- /* Hold the lock while we setup the outgoing calls */
- if (use_weight)
- ao2_lock(queues);
- ao2_lock(qe->parent);
- ast_debug(1, "%s is trying to call a queue member.\n",
- qe->chan->name);
- ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
- if (!ast_strlen_zero(qe->announce))
- announce = qe->announce;
- if (!ast_strlen_zero(announceoverride))
- announce = announceoverride;
-
- memi = ao2_iterator_init(qe->parent->members, 0);
- while ((cur = ao2_iterator_next(&memi))) {
- struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
- struct ast_dialed_interface *di;
- AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
- if (!tmp) {
- ao2_ref(cur, -1);
- ao2_unlock(qe->parent);
- if (use_weight)
- ao2_unlock(queues);
- goto out;
- }
- if (!datastore) {
- if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
- ao2_ref(cur, -1);
- ao2_unlock(qe->parent);
- if (use_weight)
- ao2_unlock(queues);
- free(tmp);
- goto out;
- }
- datastore->inheritance = DATASTORE_INHERIT_FOREVER;
- if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
- ao2_ref(cur, -1);
- ao2_unlock(&qe->parent);
- if (use_weight)
- ao2_unlock(queues);
- free(tmp);
- goto out;
- }
- datastore->data = dialed_interfaces;
- AST_LIST_HEAD_INIT(dialed_interfaces);
-
- ast_channel_lock(qe->chan);
- ast_channel_datastore_add(qe->chan, datastore);
- ast_channel_unlock(qe->chan);
- } else
- dialed_interfaces = datastore->data;
-
- AST_LIST_LOCK(dialed_interfaces);
- AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
- if (!strcasecmp(cur->interface, di->interface)) {
- ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n",
- di->interface);
- break;
- }
- }
- AST_LIST_UNLOCK(dialed_interfaces);
-
- if (di) {
- free(tmp);
- continue;
- }
-
- /* It is always ok to dial a Local interface. We only keep track of
- * which "real" interfaces have been dialed. The Local channel will
- * inherit this list so that if it ends up dialing a real interface,
- * it won't call one that has already been called. */
- if (strncasecmp(cur->interface, "Local/", 6)) {
- if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
- ao2_ref(cur, -1);
- ao2_unlock(qe->parent);
- if (use_weight)
- ao2_unlock(queues);
- free(tmp);
- goto out;
- }
- strcpy(di->interface, cur->interface);
-
- AST_LIST_LOCK(dialed_interfaces);
- AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
- AST_LIST_UNLOCK(dialed_interfaces);
- }
-
- tmp->stillgoing = -1;
- tmp->member = cur;
- tmp->oldstatus = cur->status;
- tmp->lastcall = cur->lastcall;
- tmp->lastqueue = cur->lastqueue;
- ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
- /* Special case: If we ring everyone, go ahead and ring them, otherwise
- just calculate their metric for the appropriate strategy */
- if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
- /* Put them in the list of outgoing thingies... We're ready now.
- XXX If we're forcibly removed, these outgoing calls won't get
- hung up XXX */
- tmp->q_next = outgoing;
- outgoing = tmp;
- /* If this line is up, don't try anybody else */
- if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
- break;
- } else {
- ao2_ref(cur, -1);
- ast_free(tmp);
- }
- }
- if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
- to = (qe->expire - now) * 1000;
- else
- to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
- orig = to;
- ++qe->pending;
- ring_one(qe, outgoing, &numbusies);
- ao2_unlock(qe->parent);
- if (use_weight)
- ao2_unlock(queues);
- lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
- if (datastore) {
- ast_channel_datastore_remove(qe->chan, datastore);
- ast_channel_datastore_free(datastore);
- }
- ao2_lock(qe->parent);
- if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
- store_next_rr(qe, outgoing);
- }
- if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
- store_next_lin(qe, outgoing);
- }
- ao2_unlock(qe->parent);
- peer = lpeer ? lpeer->chan : NULL;
- if (!peer) {
- qe->pending = 0;
- if (to) {
- /* Must gotten hung up */
- res = -1;
- } else {
- /* User exited by pressing a digit */
- res = digit;
- }
- if (res == -1)
- ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
- } else { /* peer is valid */
- /* Ah ha! Someone answered within the desired timeframe. Of course after this
- we will always return with -1 so that it is hung up properly after the
- conversation. */
- qe->handled++;
- if (!strcmp(qe->chan->tech->type, "Zap"))
- ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
- if (!strcmp(peer->tech->type, "Zap"))
- ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
- /* Update parameters for the queue */
- time(&now);
- recalc_holdtime(qe, (now - qe->start));
- ao2_lock(qe->parent);
- callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
- ao2_unlock(qe->parent);
- member = lpeer->member;
- /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
- ao2_ref(member, 1);
- hangupcalls(outgoing, peer);
- outgoing = NULL;
- if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
- int res2;
-
- res2 = ast_autoservice_start(qe->chan);
- if (!res2) {
- if (qe->parent->memberdelay) {
- ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
- res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
- }
- if (!res2 && announce) {
- play_file(peer, announce);
- }
- if (!res2 && qe->parent->reportholdtime) {
- if (!play_file(peer, qe->parent->sound_reporthold)) {
- int holdtime;
-
- time(&now);
- holdtime = abs((now - qe->start) / 60);
- if (holdtime < 2) {
- play_file(peer, qe->parent->sound_lessthan);
- ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
- } else
- ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
- play_file(peer, qe->parent->sound_minutes);
- }
- }
- }
- res2 |= ast_autoservice_stop(qe->chan);
- if (ast_check_hangup(peer)) {
- /* Agent must have hung up */
- ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
- ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
- record_abandoned(qe);
- if (qe->parent->eventwhencalled)
- manager_event(EVENT_FLAG_AGENT, "AgentDump",
- "Queue: %s\r\n"
- "Uniqueid: %s\r\n"
- "Channel: %s\r\n"
- "Member: %s\r\n"
- "MemberName: %s\r\n"
- "%s",
- queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
- qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
- ast_hangup(peer);
- ao2_ref(member, -1);
- goto out;
- } else if (res2) {
- /* Caller must have hung up just before being connected*/
- ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
- ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
- record_abandoned(qe);
- ast_hangup(peer);
- ao2_ref(member, -1);
- return -1;
- }
- }
- /* Stop music on hold */
- if (ringing)
- ast_indicate(qe->chan,-1);
- else
- ast_moh_stop(qe->chan);
- /* If appropriate, log that we have a destination channel */
- if (qe->chan->cdr)
- ast_cdr_setdestchan(qe->chan->cdr, peer->name);
- /* Make sure channels are compatible */
- res = ast_channel_make_compatible(qe->chan, peer);
- if (res < 0) {
- ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
- ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
- record_abandoned(qe);
- ast_hangup(peer);
- ao2_ref(member, -1);
- return -1;
- }
-
- /* Play announcement to the caller telling it's his turn if defined */
- if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
- if (play_file(qe->chan, qe->parent->sound_callerannounce))
- ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
- }
-
- ao2_lock(qe->parent);
- /* if setinterfacevar is defined, make member variables available to the channel */
- /* use pbx_builtin_setvar to set a load of variables with one call */
- if (qe->parent->setinterfacevar) {
- snprintf(interfacevar,sizeof(interfacevar), "MEMBERINTERFACE=%s|MEMBERNAME=%s|MEMBERCALLS=%d|MEMBERLASTCALL=%ld|MEMBERPENALTY=%d|MEMBERDYNAMIC=%d|MEMBERREALTIME=%d",
- member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
- pbx_builtin_setvar(qe->chan, interfacevar);
- }
-
- /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
- /* use pbx_builtin_setvar to set a load of variables with one call */
- if (qe->parent->setqueueentryvar) {
- snprintf(interfacevar,sizeof(interfacevar), "QEHOLDTIME=%ld|QEORIGINALPOS=%d",
- (long) time(NULL) - qe->start, qe->opos);
- pbx_builtin_setvar(qe->chan, interfacevar);
- }
-
- /* try to set queue variables if configured to do so*/
- set_queue_variables(qe);
- ao2_unlock(qe->parent);
-
- /* Begin Monitoring */
- if (qe->parent->monfmt && *qe->parent->monfmt) {
- if (!qe->parent->montype) {
- ast_debug(1, "Starting Monitor as requested.\n");
- monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
- if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
- which = qe->chan;
- else
- which = peer;
- if (monitorfilename)
- ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
- else if (qe->chan->cdr)
- ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
- else {
- /* Last ditch effort -- no CDR, make up something */
- snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
- ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
- }
- } else {
- ast_debug(1, "Starting MixMonitor as requested.\n");
- monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
- if (!monitorfilename) {
- if (qe->chan->cdr)
- ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
- else
- snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
- } else {
- const char *m = monitorfilename;
- for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
- switch (*m) {
- case '^':
- if (*(m + 1) == '{')
- *p = '$';
- break;
- case ',':
- *p++ = '\\';
- /* Fall through */
- default:
- *p = *m;
- }
- if (*m == '\0')
- break;
- }
- if (p == tmpid2 + sizeof(tmpid2))
- tmpid2[sizeof(tmpid2) - 1] = '\0';
-
- pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
- }
-
- monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
- monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
-
- if (monitor_exec) {
- const char *m = monitor_exec;
- for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
- switch (*m) {
- case '^':
- if (*(m + 1) == '{')
- *p = '$';
- break;
- case ',':
- *p++ = '\\';
- /* Fall through */
- default:
- *p = *m;
- }
- if (*m == '\0')
- break;
- }
- if (p == meid2 + sizeof(meid2))
- meid2[sizeof(meid2) - 1] = '\0';
-
- pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
- }
-
- snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
-
- mixmonapp = pbx_findapp("MixMonitor");
-
- if (!monitor_options)
- monitor_options = "";
-
- if (mixmonapp) {
- if (!ast_strlen_zero(monitor_exec))
- snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
- else
- snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
-
- ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
- /* We purposely lock the CDR so that pbx_exec does not update the application data */
- if (qe->chan->cdr)
- ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
- ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
- if (qe->chan->cdr)
- ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
-
- } else
- ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
-
- }
- }
- /* Drop out of the queue at this point, to prepare for next caller */
- leave_queue(qe);
- if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
- ast_debug(1, "app_queue: sendurl=%s.\n", url);
- ast_channel_sendurl(peer, url);
- }
-
- /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
- /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
- if (!ast_strlen_zero(macro)) {
- macroexec = ast_strdupa(macro);
- } else {
- if (qe->parent->membermacro)
- macroexec = ast_strdupa(qe->parent->membermacro);
- }
-
- if (!ast_strlen_zero(macroexec)) {
- ast_debug(1, "app_queue: macro=%s.\n", macroexec);
-
- res = ast_autoservice_start(qe->chan);
- if (res) {
- ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
- res = -1;
- }
-
- app = pbx_findapp("Macro");
-
- if (app) {
- res = pbx_exec(qe->chan, app, macroexec);
- ast_debug(1, "Macro exited with status %d\n", res);
- res = 0;
- } else {
- ast_log(LOG_ERROR, "Could not find application Macro\n");
- res = -1;
- }
-
- if (ast_autoservice_stop(qe->chan) < 0) {
- ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
- res = -1;
- }
- }
-
- /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
- /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
- if (!ast_strlen_zero(gosub)) {
- gosubexec = ast_strdupa(gosub);
- } else {
- if (qe->parent->membergosub)
- gosubexec = ast_strdupa(qe->parent->membergosub);
- }
-
- if (!ast_strlen_zero(gosubexec)) {
- if (option_debug)
- ast_log(LOG_DEBUG, "app_queue: gosub=%s.\n", gosubexec);
-
- res = ast_autoservice_start(qe->chan);
- if (res) {
- ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
- res = -1;
- }
-
- app = pbx_findapp("Gosub");
-
- if (app) {
- char *gosub_args, *gosub_argstart;
-
- /* Set where we came from */
- ast_copy_string(qe->chan->context, "app_dial_gosub_virtual_context", sizeof(qe->chan->context));
- ast_copy_string(qe->chan->exten, "s", sizeof(qe->chan->exten));
- qe->chan->priority = 0;
-
- gosub_argstart = strchr(gosubexec, ',');
- if (gosub_argstart) {
- *gosub_argstart = 0;
- asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1);
- *gosub_argstart = '|';
- } else {
- asprintf(&gosub_args, "%s,s,1", gosubexec);
- }
- if (gosub_args) {
- res = pbx_exec(qe->chan, app, gosub_args);
- ast_pbx_run(qe->chan);
- free(gosub_args);
- if (option_debug)
- ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
- } else
- ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
-
- res = 0;
- } else {
- ast_log(LOG_ERROR, "Could not find application Gosub\n");
- res = -1;
- }
-
- if (ast_autoservice_stop(qe->chan) < 0) {
- ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
- res = -1;
- }
- }
-
- if (!ast_strlen_zero(agi)) {
- ast_debug(1, "app_queue: agi=%s.\n", agi);
- app = pbx_findapp("agi");
- if (app) {
- agiexec = ast_strdupa(agi);
- ret = pbx_exec(qe->chan, app, agiexec);
- } else
- ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
- }
- ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
- (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
- if (update_cdr && qe->chan->cdr)
- ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
- if (qe->parent->eventwhencalled)
- manager_event(EVENT_FLAG_AGENT, "AgentConnect",
- "Queue: %s\r\n"
- "Uniqueid: %s\r\n"
- "Channel: %s\r\n"
- "Member: %s\r\n"
- "MemberName: %s\r\n"
- "Holdtime: %ld\r\n"
- "BridgedChannel: %s\r\n"
- "Ringtime: %ld\r\n"
- "%s",
- queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
- (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
- qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
- ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
- ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
- time(&callstart);
-
- bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
-
- if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
- ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
- qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
- (long) (time(NULL) - callstart));
- send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
- } else if (ast_check_hangup(qe->chan)) {
- ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
- (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
- send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
- } else {
- ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
- (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
- send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
- }
-
- if (bridge != AST_PBX_NO_HANGUP_PEER)
- ast_hangup(peer);
- update_queue(qe->parent, member, callcompletedinsl);
- res = bridge ? bridge : 1;
- ao2_ref(member, -1);
- }
-out:
- hangupcalls(outgoing, NULL);
-
- return res;
-}
-
-static int wait_a_bit(struct queue_ent *qe)
-{
- /* Don't need to hold the lock while we setup the outgoing calls */
- int retrywait = qe->parent->retry * 1000;
-
- int res = ast_waitfordigit(qe->chan, retrywait);
- if (res > 0 && !valid_exit(qe, res))
- res = 0;
-
- return res;
-}
-
-static struct member *interface_exists(struct call_queue *q, const char *interface)
-{
- struct member *mem;
- struct ao2_iterator mem_iter;
-
- if (!q)
- return NULL;
-
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((mem = ao2_iterator_next(&mem_iter))) {
- if (!strcasecmp(interface, mem->interface))
- return mem;
- ao2_ref(mem, -1);
- }
-
- return NULL;
-}
-
-
-/* Dump all members in a specific queue to the database
- *
- * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
- *
- */
-static void dump_queue_members(struct call_queue *pm_queue)
-{
- struct member *cur_member;
- char value[PM_MAX_LEN];
- int value_len = 0;
- int res;
- struct ao2_iterator mem_iter;
-
- memset(value, 0, sizeof(value));
-
- if (!pm_queue)
- return;
-
- mem_iter = ao2_iterator_init(pm_queue->members, 0);
- while ((cur_member = ao2_iterator_next(&mem_iter))) {
- if (!cur_member->dynamic) {
- ao2_ref(cur_member, -1);
- continue;
- }
-
- res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
- value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
-
- ao2_ref(cur_member, -1);
-
- if (res != strlen(value + value_len)) {
- ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
- break;
- }
- value_len += res;
- }
-
- if (value_len && !cur_member) {
- if (ast_db_put(pm_family, pm_queue->name, value))
- ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
- } else
- /* Delete the entry if the queue is empty or there is an error */
- ast_db_del(pm_family, pm_queue->name);
-}
-
-static int remove_from_queue(const char *queuename, const char *interface)
-{
- struct call_queue *q, tmpq = {
- .name = queuename,
- };
- struct member *mem, tmpmem;
- int res = RES_NOSUCHQUEUE;
-
- ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
- if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
- ao2_lock(q);
- if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
- /* XXX future changes should beware of this assumption!! */
- if (!mem->dynamic) {
- ao2_ref(mem, -1);
- ao2_unlock(q);
- return RES_NOT_DYNAMIC;
- }
- q->membercount--;
- manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "MemberName: %s\r\n",
- q->name, mem->interface, mem->membername);
- ao2_unlink(q->members, mem);
- remove_from_interfaces(mem->state_interface);
- ao2_ref(mem, -1);
-
- if (queue_persistent_members)
- dump_queue_members(q);
-
- res = RES_OKAY;
- } else {
- res = RES_EXISTS;
- }
- ao2_unlock(q);
- queue_unref(q);
- }
-
- return res;
-}
-
-
-static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
-{
- struct call_queue *q;
- struct member *new_member, *old_member;
- int res = RES_NOSUCHQUEUE;
-
- /* \note Ensure the appropriate realtime queue is loaded. Note that this
- * short-circuits if the queue is already in memory. */
- if (!(q = load_realtime_queue(queuename)))
- return res;
-
- ao2_lock(queues);
-
- ao2_lock(q);
- if ((old_member = interface_exists(q, interface)) == NULL) {
- if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
- add_to_interfaces(new_member->state_interface);
- new_member->dynamic = 1;
- ao2_link(q->members, new_member);
- q->membercount++;
- manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "MemberName: %s\r\n"
- "Membership: %s\r\n"
- "Penalty: %d\r\n"
- "CallsTaken: %d\r\n"
- "LastCall: %d\r\n"
- "Status: %d\r\n"
- "Paused: %d\r\n",
- q->name, new_member->interface, new_member->membername,
- "dynamic",
- new_member->penalty, new_member->calls, (int) new_member->lastcall,
- new_member->status, new_member->paused);
-
- ao2_ref(new_member, -1);
- new_member = NULL;
-
- if (dump)
- dump_queue_members(q);
-
- res = RES_OKAY;
- } else {
- res = RES_OUTOFMEMORY;
- }
- } else {
- ao2_ref(old_member, -1);
- res = RES_EXISTS;
- }
- ao2_unlock(q);
- ao2_unlock(queues);
-
- return res;
-}
-
-static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
-{
- int found = 0;
- struct call_queue *q;
- struct member *mem;
- struct ao2_iterator queue_iter;
-
- /* Special event for when all queues are paused - individual events still generated */
- /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
- if (ast_strlen_zero(queuename))
- ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
-
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- ao2_lock(q);
- if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
- if ((mem = interface_exists(q, interface))) {
- found++;
- if (mem->paused == paused) {
- ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
- }
- mem->paused = paused;
-
- if (queue_persistent_members)
- dump_queue_members(q);
-
- if (mem->realtime)
- update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
-
- ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
-
- if (!ast_strlen_zero(reason)) {
- manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "MemberName: %s\r\n"
- "Paused: %d\r\n"
- "Reason: %s\r\n",
- q->name, mem->interface, mem->membername, paused, reason);
- } else {
- manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "MemberName: %s\r\n"
- "Paused: %d\r\n",
- q->name, mem->interface, mem->membername, paused);
- }
- ao2_ref(mem, -1);
- }
- }
- ao2_unlock(q);
- queue_unref(q);
- }
-
- return found ? RESULT_SUCCESS : RESULT_FAILURE;
-}
-
-/* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
-static int set_member_penalty(char *queuename, char *interface, int penalty)
-{
- int foundinterface = 0, foundqueue = 0;
- struct call_queue *q;
- struct member *mem;
- struct ao2_iterator queue_iter;
-
- if (penalty < 0) {
- ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
- return RESULT_FAILURE;
- }
-
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- ao2_lock(q);
- if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
- foundqueue++;
- if ((mem = interface_exists(q, interface))) {
- foundinterface++;
- mem->penalty = penalty;
-
- ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
- manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "Penalty: %d\r\n",
- q->name, mem->interface, penalty);
-
- }
- }
- ao2_unlock(q);
- queue_unref(q);
- }
-
- if (foundinterface) {
- return RESULT_SUCCESS;
- } else if (!foundqueue) {
- ast_log (LOG_ERROR, "Invalid queuename\n");
- } else {
- ast_log (LOG_ERROR, "Invalid interface\n");
- }
-
- return RESULT_FAILURE;
-}
-
-/* \brief Gets members penalty.
- *
- * \return Return the members penalty or RESULT_FAILURE on error. */
-static int get_member_penalty(char *queuename, char *interface)
-{
- int foundqueue = 0, penalty;
- struct call_queue *q, tmpq = {
- .name = queuename,
- };
- struct member *mem;
-
- if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
- foundqueue = 1;
- ao2_lock(q);
- if ((mem = interface_exists(q, interface))) {
- penalty = mem->penalty;
- ao2_unlock(q);
- queue_unref(q);
- return penalty;
- }
- ao2_unlock(q);
- queue_unref(q);
- }
-
- /* some useful debuging */
- if (foundqueue)
- ast_log (LOG_ERROR, "Invalid queuename\n");
- else
- ast_log (LOG_ERROR, "Invalid interface\n");
-
- return RESULT_FAILURE;
-}
-
-/* Reload dynamic queue members persisted into the astdb */
-static void reload_queue_members(void)
-{
- char *cur_ptr;
- const char *queue_name;
- char *member;
- char *interface;
- char *membername = NULL;
- char *state_interface;
- char *penalty_tok;
- int penalty = 0;
- char *paused_tok;
- int paused = 0;
- struct ast_db_entry *db_tree;
- struct ast_db_entry *entry;
- struct call_queue *cur_queue;
- char queue_data[PM_MAX_LEN];
-
- ao2_lock(queues);
-
- /* Each key in 'pm_family' is the name of a queue */
- db_tree = ast_db_gettree(pm_family, NULL);
- for (entry = db_tree; entry; entry = entry->next) {
-
- queue_name = entry->key + strlen(pm_family) + 2;
-
- {
- struct call_queue tmpq = {
- .name = queue_name,
- };
- cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
- }
-
- if (!cur_queue)
- cur_queue = load_realtime_queue(queue_name);
-
- if (!cur_queue) {
- /* If the queue no longer exists, remove it from the
- * database */
- ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
- ast_db_del(pm_family, queue_name);
- continue;
- }
-
- if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
- queue_unref(cur_queue);
- continue;
- }
-
- cur_ptr = queue_data;
- while ((member = strsep(&cur_ptr, ",|"))) {
- if (ast_strlen_zero(member))
- continue;
-
- interface = strsep(&member, ";");
- penalty_tok = strsep(&member, ";");
- paused_tok = strsep(&member, ";");
- membername = strsep(&member, ";");
- state_interface = strsep(&member, ";");
-
- if (!penalty_tok) {
- ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
- break;
- }
- penalty = strtol(penalty_tok, NULL, 10);
- if (errno == ERANGE) {
- ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
- break;
- }
-
- if (!paused_tok) {
- ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
- break;
- }
- paused = strtol(paused_tok, NULL, 10);
- if ((errno == ERANGE) || paused < 0 || paused > 1) {
- ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
- break;
- }
-
- ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
-
- if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
- ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
- break;
- }
- }
- queue_unref(cur_queue);
- }
-
- ao2_unlock(queues);
- if (db_tree) {
- ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
- ast_db_freetree(db_tree);
- }
-}
-
-static int pqm_exec(struct ast_channel *chan, void *data)
-{
- char *parse;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(queuename);
- AST_APP_ARG(interface);
- AST_APP_ARG(options);
- AST_APP_ARG(reason);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options][|reason])\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (ast_strlen_zero(args.interface)) {
- ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options[|reason]])\n");
- return -1;
- }
-
- if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
- ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
- pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
- return -1;
- }
-
- pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
-
- return 0;
-}
-
-static int upqm_exec(struct ast_channel *chan, void *data)
-{
- char *parse;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(queuename);
- AST_APP_ARG(interface);
- AST_APP_ARG(options);
- AST_APP_ARG(reason);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options[|reason]])\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (ast_strlen_zero(args.interface)) {
- ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options[|reason]])\n");
- return -1;
- }
-
- if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
- ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
- pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
- return -1;
- }
-
- pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
-
- return 0;
-}
-
-static int rqm_exec(struct ast_channel *chan, void *data)
-{
- int res=-1;
- char *parse, *temppos = NULL;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(queuename);
- AST_APP_ARG(interface);
- AST_APP_ARG(options);
- );
-
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (ast_strlen_zero(args.interface)) {
- args.interface = ast_strdupa(chan->name);
- temppos = strrchr(args.interface, '-');
- if (temppos)
- *temppos = '\0';
- }
-
- switch (remove_from_queue(args.queuename, args.interface)) {
- case RES_OKAY:
- ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
- ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
- pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
- res = 0;
- break;
- case RES_EXISTS:
- ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
- pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
- res = 0;
- break;
- case RES_NOSUCHQUEUE:
- ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
- pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
- res = 0;
- break;
- case RES_NOT_DYNAMIC:
- ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
- pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
- res = 0;
- break;
- }
-
- return res;
-}
-
-static int aqm_exec(struct ast_channel *chan, void *data)
-{
- int res=-1;
- char *parse, *temppos = NULL;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(queuename);
- AST_APP_ARG(interface);
- AST_APP_ARG(penalty);
- AST_APP_ARG(options);
- AST_APP_ARG(membername);
- AST_APP_ARG(state_interface);
- );
- int penalty = 0;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (ast_strlen_zero(args.interface)) {
- args.interface = ast_strdupa(chan->name);
- temppos = strrchr(args.interface, '-');
- if (temppos)
- *temppos = '\0';
- }
-
- if (!ast_strlen_zero(args.penalty)) {
- if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
- ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
- penalty = 0;
- }
- }
-
- switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
- case RES_OKAY:
- ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
- ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
- pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
- res = 0;
- break;
- case RES_EXISTS:
- ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
- pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
- res = 0;
- break;
- case RES_NOSUCHQUEUE:
- ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
- pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
- res = 0;
- break;
- case RES_OUTOFMEMORY:
- ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
- break;
- }
-
- return res;
-}
-
-static int ql_exec(struct ast_channel *chan, void *data)
-{
- char *parse;
-
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(queuename);
- AST_APP_ARG(uniqueid);
- AST_APP_ARG(membername);
- AST_APP_ARG(event);
- AST_APP_ARG(params);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
- || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
- ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
- return -1;
- }
-
- ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
- "%s", args.params ? args.params : "");
-
- return 0;
-}
-
-static void copy_rules(struct queue_ent *qe, const char *rulename)
-{
- struct penalty_rule *pr_iter;
- struct rule_list *rl_iter;
- const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
- AST_LIST_LOCK(&rule_lists);
- AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
- if (!strcasecmp(rl_iter->name, tmp))
- break;
- }
- if (rl_iter) {
- AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
- struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
- if (!new_pr) {
- ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
- AST_LIST_UNLOCK(&rule_lists);
- break;
- }
- new_pr->time = pr_iter->time;
- new_pr->max_value = pr_iter->max_value;
- new_pr->min_value = pr_iter->min_value;
- new_pr->max_relative = pr_iter->max_relative;
- new_pr->min_relative = pr_iter->min_relative;
- AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
- }
- }
- AST_LIST_UNLOCK(&rule_lists);
-}
-
-/*!\brief The starting point for all queue calls
- *
- * The process involved here is to
- * 1. Parse the options specified in the call to Queue()
- * 2. Join the queue
- * 3. Wait in a loop until it is our turn to try calling a queue member
- * 4. Attempt to call a queue member
- * 5. If 4. did not result in a bridged call, then check for between
- * call options such as periodic announcements etc.
- * 6. Try 4 again uless some condition (such as an expiration time) causes us to
- * exit the queue.
- */
-static int queue_exec(struct ast_channel *chan, void *data)
-{
- int res=-1;
- int ringing=0;
- const char *user_priority;
- const char *max_penalty_str;
- const char *min_penalty_str;
- int prio;
- int qcontinue = 0;
- int max_penalty, min_penalty;
- enum queue_result reason = QUEUE_UNKNOWN;
- /* whether to exit Queue application after the timeout hits */
- int tries = 0;
- int noption = 0;
- char *parse;
- int makeannouncement = 0;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(queuename);
- AST_APP_ARG(options);
- AST_APP_ARG(url);
- AST_APP_ARG(announceoverride);
- AST_APP_ARG(queuetimeoutstr);
- AST_APP_ARG(agi);
- AST_APP_ARG(macro);
- AST_APP_ARG(gosub);
- AST_APP_ARG(rule);
- );
- /* Our queue entry */
- struct queue_ent qe;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
-
- /* Setup our queue entry */
- memset(&qe, 0, sizeof(qe));
- qe.start = time(NULL);
-
- /* set the expire time based on the supplied timeout; */
- if (args.queuetimeoutstr)
- qe.expire = qe.start + atoi(args.queuetimeoutstr);
- else
- qe.expire = 0;
-
- /* Get the priority from the variable ${QUEUE_PRIO} */
- user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
- if (user_priority) {
- if (sscanf(user_priority, "%d", &prio) == 1) {
- ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
- } else {
- ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
- user_priority, chan->name);
- prio = 0;
- }
- } else {
- ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
- prio = 0;
- }
-
- /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
-
- if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
- if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
- ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
- } else {
- ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
- max_penalty_str, chan->name);
- max_penalty = 0;
- }
- } else {
- max_penalty = 0;
- }
-
- if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
- if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) {
- ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
- } else {
- ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
- min_penalty_str, chan->name);
- min_penalty = 0;
- }
- } else {
- min_penalty = 0;
- }
-
- if (args.options && (strchr(args.options, 'r')))
- ringing = 1;
-
- if (args.options && (strchr(args.options, 'c')))
- qcontinue = 1;
-
- ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
- args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
-
- qe.chan = chan;
- qe.prio = prio;
- qe.max_penalty = max_penalty;
- qe.min_penalty = min_penalty;
- qe.last_pos_said = 0;
- qe.last_pos = 0;
- qe.last_periodic_announce_time = time(NULL);
- qe.last_periodic_announce_sound = 0;
- qe.valid_digits = 0;
- if (join_queue(args.queuename, &qe, &reason)) {
- ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
- set_queue_result(chan, reason);
- return 0;
- }
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
- S_OR(chan->cid.cid_num, ""));
- copy_rules(&qe, args.rule);
- qe.pr = AST_LIST_FIRST(&qe.qe_rules);
-check_turns:
- if (ringing) {
- ast_indicate(chan, AST_CONTROL_RINGING);
- } else {
- ast_moh_start(chan, qe.moh, NULL);
- }
-
- /* This is the wait loop for callers 2 through maxlen */
- res = wait_our_turn(&qe, ringing, &reason);
- if (res) {
- goto stop;
- }
-
- makeannouncement = 0;
-
- for (;;) {
- /* This is the wait loop for the head caller*/
- /* To exit, they may get their call answered; */
- /* they may dial a digit from the queue context; */
- /* or, they may timeout. */
-
- enum queue_member_status stat;
-
- /* Leave if we have exceeded our queuetimeout */
- if (qe.expire && (time(NULL) > qe.expire)) {
- record_abandoned(&qe);
- reason = QUEUE_TIMEOUT;
- res = 0;
- ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
- qe.pos, qe.opos, (long) time(NULL) - qe.start);
- break;
- }
-
- if (makeannouncement) {
- /* Make a position announcement, if enabled */
- if (qe.parent->announcefrequency)
- if ((res = say_position(&qe,ringing)))
- goto stop;
- }
- makeannouncement = 1;
-
- /* Make a periodic announcement, if enabled */
- if (qe.parent->periodicannouncefrequency)
- if ((res = say_periodic_announcement(&qe,ringing)))
- goto stop;
-
- /* see if we need to move to the next penalty level for this queue */
- while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
- update_qe_rule(&qe);
- }
-
- /* Try calling all queue members for 'timeout' seconds */
- res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
- if (res) {
- goto stop;
- }
-
- stat = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
-
- /* exit after 'timeout' cycle if 'n' option enabled */
- if (noption && tries >= qe.parent->membercount) {
- ast_verb(3, "Exiting on time-out cycle\n");
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
- record_abandoned(&qe);
- reason = QUEUE_TIMEOUT;
- res = 0;
- break;
- }
-
- /* leave the queue if no agents, if enabled */
- if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
- record_abandoned(&qe);
- reason = QUEUE_LEAVEEMPTY;
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
- res = 0;
- break;
- }
-
- /* leave the queue if no reachable agents, if enabled */
- if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
- record_abandoned(&qe);
- reason = QUEUE_LEAVEUNAVAIL;
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
- res = 0;
- break;
- }
- if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
- record_abandoned(&qe);
- reason = QUEUE_LEAVEUNAVAIL;
- res = 0;
- break;
- }
-
- /* Leave if we have exceeded our queuetimeout */
- if (qe.expire && (time(NULL) > qe.expire)) {
- record_abandoned(&qe);
- reason = QUEUE_TIMEOUT;
- res = 0;
- ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
- break;
- }
-
- /* If using dynamic realtime members, we should regenerate the member list for this queue */
- update_realtime_members(qe.parent);
-
- /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
- res = wait_a_bit(&qe);
- if (res)
- goto stop;
-
- /* Since this is a priority queue and
- * it is not sure that we are still at the head
- * of the queue, go and check for our turn again.
- */
- if (!is_our_turn(&qe)) {
- ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
- goto check_turns;
- }
- }
-
-stop:
- if (res) {
- if (res < 0) {
- if (!qe.handled) {
- record_abandoned(&qe);
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
- "%d|%d|%ld", qe.pos, qe.opos,
- (long) time(NULL) - qe.start);
- }
- res = -1;
- } else if (qe.valid_digits) {
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
- "%s|%d", qe.digits, qe.pos);
- }
- }
-
- /* Don't allow return code > 0 */
- if (res >= 0 && res != AST_PBX_KEEPALIVE) {
- res = 0;
- if (ringing) {
- ast_indicate(chan, -1);
- } else {
- ast_moh_stop(chan);
- }
- ast_stopstream(chan);
- }
-
- set_queue_variables(&qe);
-
- leave_queue(&qe);
- if (reason != QUEUE_UNKNOWN)
- set_queue_result(chan, reason);
-
- return res;
-}
-
-static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
-{
- int res = -1;
- struct call_queue *q, tmpq = {
- .name = data,
- };
-
- char interfacevar[256]="";
- float sl = 0;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
- return -1;
- }
-
- if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
- ao2_lock(q);
- if (q->setqueuevar) {
- sl = 0;
- res = 0;
-
- if (q->callscompleted > 0)
- sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
-
- snprintf(interfacevar,sizeof(interfacevar),
- "QUEUEMAX=%d|QUEUESTRATEGY=%s|QUEUECALLS=%d|QUEUEHOLDTIME=%d|QUEUECOMPLETED=%d|QUEUEABANDONED=%d|QUEUESRVLEVEL=%d|QUEUESRVLEVELPERF=%2.1f",
- q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
-
- pbx_builtin_setvar(chan, interfacevar);
- }
-
- ao2_unlock(q);
- queue_unref(q);
- } else
- ast_log(LOG_WARNING, "queue %s was not found\n", data);
-
- snprintf(buf, len, "%d", res);
-
- return 0;
-}
-
-static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
-{
- int count = 0;
- struct member *m;
- struct ao2_iterator mem_iter;
- struct call_queue *q;
- char *option;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
- return -1;
- }
-
- if ((option = strchr(data, ',')))
- *option++ = '\0';
- else
- option = "logged";
- if ((q = load_realtime_queue(data))) {
- ao2_lock(q);
- if (!strcasecmp(option, "logged")) {
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- /* Count the agents who are logged in and presently answering calls */
- if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
- count++;
- }
- ao2_ref(m, -1);
- }
- } else if (!strcasecmp(option, "free")) {
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- /* Count the agents who are logged in and presently answering calls */
- if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
- count++;
- }
- ao2_ref(m, -1);
- }
- } else /* must be "count" */
- count = q->membercount;
- ao2_unlock(q);
- queue_unref(q);
- } else
- ast_log(LOG_WARNING, "queue %s was not found\n", data);
-
- snprintf(buf, len, "%d", count);
-
- return 0;
-}
-
-static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
-{
- int count = 0;
- struct member *m;
- struct call_queue *q;
- struct ao2_iterator mem_iter;
- static int depflag = 1;
-
- if (depflag) {
- depflag = 0;
- ast_log(LOG_NOTICE, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
- }
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
- return -1;
- }
-
- if ((q = load_realtime_queue(data))) {
- ao2_lock(q);
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- /* Count the agents who are logged in and presently answering calls */
- if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
- count++;
- }
- ao2_ref(m, -1);
- }
- ao2_unlock(q);
- queue_unref(q);
- } else
- ast_log(LOG_WARNING, "queue %s was not found\n", data);
-
- snprintf(buf, len, "%d", count);
-
- return 0;
-}
-
-
-static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
-{
- int count = 0;
- struct call_queue *q, tmpq = {
- .name = data,
- };
-
- buf[0] = '\0';
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
- return -1;
- }
-
- if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
- ao2_lock(q);
- count = q->count;
- ao2_unlock(q);
- queue_unref(q);
- } else
- ast_log(LOG_WARNING, "queue %s was not found\n", data);
-
- snprintf(buf, len, "%d", count);
-
- return 0;
-}
-
-static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
-{
- struct call_queue *q, tmpq = {
- .name = data,
- };
- struct member *m;
-
- /* Ensure an otherwise empty list doesn't return garbage */
- buf[0] = '\0';
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
- return -1;
- }
-
- if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
- int buflen = 0, count = 0;
- struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
-
- ao2_lock(q);
- while ((m = ao2_iterator_next(&mem_iter))) {
- /* strcat() is always faster than printf() */
- if (count++) {
- strncat(buf + buflen, ",", len - buflen - 1);
- buflen++;
- }
- strncat(buf + buflen, m->membername, len - buflen - 1);
- buflen += strlen(m->membername);
- /* Safeguard against overflow (negative length) */
- if (buflen >= len - 2) {
- ao2_ref(m, -1);
- ast_log(LOG_WARNING, "Truncating list\n");
- break;
- }
- ao2_ref(m, -1);
- }
- ao2_unlock(q);
- queue_unref(q);
- } else
- ast_log(LOG_WARNING, "queue %s was not found\n", data);
-
- /* We should already be terminated, but let's make sure. */
- buf[len - 1] = '\0';
-
- return 0;
-}
-
-/*! \brief Dialplan function QUEUE_MEMBER_PENALTY()
- * Gets the members penalty. */
-static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
-{
- int penalty;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(queuename);
- AST_APP_ARG(interface);
- );
- /* Make sure the returned value on error is NULL. */
- buf[0] = '\0';
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
- return -1;
- }
-
- AST_STANDARD_APP_ARGS(args, data);
-
- if (args.argc < 2) {
- ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
- return -1;
- }
-
- penalty = get_member_penalty (args.queuename, args.interface);
-
- if (penalty >= 0) /* remember that buf is already '\0' */
- snprintf (buf, len, "%d", penalty);
-
- return 0;
-}
-
-/*! Dialplan function QUEUE_MEMBER_PENALTY()
- * Sets the members penalty. */
-static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
-{
- int penalty;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(queuename);
- AST_APP_ARG(interface);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
- return -1;
- }
-
- AST_STANDARD_APP_ARGS(args, data);
-
- if (args.argc < 2) {
- ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
- return -1;
- }
-
- penalty = atoi(value);
-
- if (ast_strlen_zero(args.interface)) {
- ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
- return -1;
- }
-
- /* if queuename = NULL then penalty will be set for interface in all the queues. */
- if (set_member_penalty(args.queuename, args.interface, penalty)) {
- ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
- return -1;
- }
-
- return 0;
-}
-
-static struct ast_custom_function queuevar_function = {
- .name = "QUEUE_VARIABLES",
- .synopsis = "Return Queue information in variables",
- .syntax = "QUEUE_VARIABLES(<queuename>)",
- .desc =
-"Makes the following queue variables available.\n"
-"QUEUEMAX maxmimum number of calls allowed\n"
-"QUEUESTRATEGY the strategy of the queue\n"
-"QUEUECALLS number of calls currently in the queue\n"
-"QUEUEHOLDTIME current average hold time\n"
-"QUEUECOMPLETED number of completed calls for the queue\n"
-"QUEUEABANDONED number of abandoned calls\n"
-"QUEUESRVLEVEL queue service level\n"
-"QUEUESRVLEVELPERF current service level performance\n"
-"Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
- .read = queue_function_var,
-};
-
-static struct ast_custom_function queuemembercount_function = {
- .name = "QUEUE_MEMBER",
- .synopsis = "Count number of members answering a queue",
- .syntax = "QUEUE_MEMBER(<queuename>, <option>)",
- .desc =
-"Returns the number of members currently associated with the specified queue.\n"
-"One of three options may be passed to determine the count returned:\n"
- "\"logged\" - Returns the number of logged-in members for the specified queue\n"
- "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
- "\"count\" - Returns the total number of members for the specified queue\n",
- .read = queue_function_qac,
-};
-
-static struct ast_custom_function queuemembercount_dep = {
- .name = "QUEUE_MEMBER_COUNT",
- .synopsis = "Count number of members answering a queue",
- .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
- .desc =
-"Returns the number of members currently associated with the specified queue.\n\n"
-"This function has been deprecated in favor of the QUEUE_MEMBER function\n",
- .read = queue_function_qac_dep,
-};
-
-static struct ast_custom_function queuewaitingcount_function = {
- .name = "QUEUE_WAITING_COUNT",
- .synopsis = "Count number of calls currently waiting in a queue",
- .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
- .desc =
-"Returns the number of callers currently waiting in the specified queue.\n",
- .read = queue_function_queuewaitingcount,
-};
-
-static struct ast_custom_function queuememberlist_function = {
- .name = "QUEUE_MEMBER_LIST",
- .synopsis = "Returns a list of interfaces on a queue",
- .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
- .desc =
-"Returns a comma-separated list of members associated with the specified queue.\n",
- .read = queue_function_queuememberlist,
-};
-
-static struct ast_custom_function queuememberpenalty_function = {
- .name = "QUEUE_MEMBER_PENALTY",
- .synopsis = "Gets or sets queue members penalty.",
- .syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
- .desc =
-"Gets or sets queue members penalty\n",
- .read = queue_function_memberpenalty_read,
- .write = queue_function_memberpenalty_write,
-};
-
-static int reload_queue_rules(int reload)
-{
- struct ast_config *cfg;
- struct rule_list *rl_iter, *new_rl;
- struct penalty_rule *pr_iter;
- char *rulecat = NULL;
- struct ast_variable *rulevar = NULL;
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
-
- if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
- ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
- } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
- ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
- return AST_MODULE_LOAD_SUCCESS;
- } else {
- AST_LIST_LOCK(&rule_lists);
- while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
- while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
- ast_free(pr_iter);
- ast_free(rl_iter);
- }
- while ((rulecat = ast_category_browse(cfg, rulecat))) {
- if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
- ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
- AST_LIST_UNLOCK(&rule_lists);
- return AST_MODULE_LOAD_FAILURE;
- } else {
- ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
- AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
- for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
- if(!strcasecmp(rulevar->name, "penaltychange"))
- insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
- else
- ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
- }
- }
- AST_LIST_UNLOCK(&rule_lists);
- }
-
- ast_config_destroy(cfg);
-
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-
-static int reload_queues(int reload)
-{
- struct call_queue *q;
- struct ast_config *cfg;
- char *cat, *tmp;
- struct ast_variable *var;
- struct member *cur, *newm;
- struct ao2_iterator mem_iter;
- int new;
- const char *general_val = NULL;
- char parse[80];
- char *interface, *state_interface;
- char *membername = NULL;
- int penalty;
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
- struct ao2_iterator queue_iter;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(interface);
- AST_APP_ARG(penalty);
- AST_APP_ARG(membername);
- AST_APP_ARG(state_interface);
- );
-
- /*First things first. Let's load queuerules.conf*/
- if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
- return AST_MODULE_LOAD_FAILURE;
-
- if (!(cfg = ast_config_load("queues.conf", config_flags))) {
- ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
- return 0;
- } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
- ao2_lock(queues);
- use_weight=0;
- /* Mark all queues as dead for the moment */
- queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
- while ((q = ao2_iterator_next(&queue_iter))) {
- if (!q->realtime) {
- q->dead = 1;
- q->found = 0;
- }
- queue_unref(q);
- }
-
- /* Chug through config file */
- cat = NULL;
- while ((cat = ast_category_browse(cfg, cat)) ) {
- if (!strcasecmp(cat, "general")) {
- /* Initialize global settings */
- queue_keep_stats = 0;
- if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
- queue_keep_stats = ast_true(general_val);
- queue_persistent_members = 0;
- if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
- queue_persistent_members = ast_true(general_val);
- autofill_default = 0;
- if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
- autofill_default = ast_true(general_val);
- montype_default = 0;
- if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
- if (!strcasecmp(general_val, "mixmonitor"))
- montype_default = 1;
- }
- update_cdr = 0;
- if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
- update_cdr = ast_true(general_val);
- shared_lastcall = 0;
- if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
- shared_lastcall = ast_true(general_val);
- } else { /* Define queue */
- /* Look for an existing one */
- struct call_queue tmpq = {
- .name = cat,
- };
- if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
- /* Make one then */
- if (!(q = alloc_queue(cat))) {
- /* TODO: Handle memory allocation failure */
- }
- new = 1;
- } else
- new = 0;
- if (q) {
- const char *tmpvar;
- if (!new)
- ao2_lock(q);
- /* Check if a queue with this name already exists */
- if (q->found) {
- ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
- if (!new) {
- ao2_unlock(q);
- queue_unref(q);
- }
- continue;
- }
- /* Due to the fact that the "linear" strategy will have a different allocation
- * scheme for queue members, we must devise the queue's strategy before other initializations
- */
- if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
- q->strategy = strat2int(tmpvar);
- if (q->strategy < 0) {
- ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
- tmpvar, q->name);
- q->strategy = QUEUE_STRATEGY_RINGALL;
- }
- } else
- q->strategy = QUEUE_STRATEGY_RINGALL;
- /* Re-initialize the queue, and clear statistics */
- init_queue(q);
- if (!queue_keep_stats)
- clear_queue(q);
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((cur = ao2_iterator_next(&mem_iter))) {
- if (!cur->dynamic) {
- cur->delme = 1;
- }
- ao2_ref(cur, -1);
- }
- for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
- if (!strcasecmp(var->name, "member")) {
- struct member tmpmem;
- membername = NULL;
-
- /* Add a new member */
- ast_copy_string(parse, var->value, sizeof(parse));
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- interface = args.interface;
- if (!ast_strlen_zero(args.penalty)) {
- tmp = args.penalty;
- while (*tmp && *tmp < 33) tmp++;
- penalty = atoi(tmp);
- if (penalty < 0) {
- penalty = 0;
- }
- } else
- penalty = 0;
-
- if (!ast_strlen_zero(args.membername)) {
- membername = args.membername;
- while (*membername && *membername < 33) membername++;
- }
-
- if (!ast_strlen_zero(args.state_interface)) {
- state_interface = args.state_interface;
- while (*state_interface && *state_interface < 33) state_interface++;
- } else
- state_interface = interface;
-
- /* Find the old position in the list */
- ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
- cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
- /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
- if (cur && strcasecmp(cur->state_interface, state_interface)) {
- remove_from_interfaces(cur->state_interface);
- }
- newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
- if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
- add_to_interfaces(state_interface);
- ao2_link(q->members, newm);
- ao2_ref(newm, -1);
- newm = NULL;
-
- if (cur)
- ao2_ref(cur, -1);
- else {
- q->membercount++;
- }
- } else {
- queue_set_param(q, var->name, var->value, var->lineno, 1);
- }
- }
-
- /* Free remaining members marked as delme */
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((cur = ao2_iterator_next(&mem_iter))) {
- if (! cur->delme) {
- ao2_ref(cur, -1);
- continue;
- }
- ast_log(LOG_DEBUG, "%s in queue marked as delme, we should be deleting...\n", cur->interface);
- q->membercount--;
- ao2_unlink(q->members, cur);
- remove_from_interfaces(cur->interface);
- ao2_ref(cur, -1);
- }
-
- if (new) {
- ao2_link(queues, q);
- } else
- ao2_unlock(q);
- queue_unref(q);
- }
- }
- }
- ast_config_destroy(cfg);
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- if (q->dead) {
- ao2_unlink(queues, q);
- } else {
- ao2_lock(q);
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((cur = ao2_iterator_next(&mem_iter))) {
- if (cur->dynamic)
- q->membercount++;
- cur->status = ast_device_state(cur->interface);
- ao2_ref(cur, -1);
- }
- ao2_unlock(q);
- }
- queue_unref(q);
- }
- ao2_unlock(queues);
- return 1;
-}
-
-/*! \brief direct ouput to manager or cli with proper terminator */
-static void do_print(struct mansession *s, int fd, const char *str)
-{
- if (s)
- astman_append(s, "%s\r\n", str);
- else
- ast_cli(fd, "%s\n", str);
-}
-
-static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
-{
- struct call_queue *q;
- struct ast_str *out = ast_str_alloca(240);
- int found = 0;
- time_t now = time(NULL);
- struct ao2_iterator queue_iter;
- struct ao2_iterator mem_iter;
-
- if (argc != 2 && argc != 3)
- return CLI_SHOWUSAGE;
-
- /* We only want to load realtime queues when a specific queue is asked for. */
- if (argc == 3) /* specific queue */
- load_realtime_queue(argv[2]);
-
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- float sl;
-
- ao2_lock(q);
- if (argc == 3 && strcasecmp(q->name, argv[2])) {
- ao2_unlock(q);
- continue;
- }
- found = 1;
-
- ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
- if (q->maxlen)
- ast_str_append(&out, 0, "%d", q->maxlen);
- else
- ast_str_append(&out, 0, "unlimited");
- sl = 0;
- if (q->callscompleted > 0)
- sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
- ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
- int2strat(q->strategy), q->holdtime, q->weight,
- q->callscompleted, q->callsabandoned,sl,q->servicelevel);
- do_print(s, fd, out->str);
- if (!ao2_container_count(q->members))
- do_print(s, fd, " No Members");
- else {
- struct member *mem;
-
- do_print(s, fd, " Members: ");
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((mem = ao2_iterator_next(&mem_iter))) {
- ast_str_set(&out, 0, " %s", mem->membername);
- if (mem->penalty)
- ast_str_append(&out, 0, " with penalty %d", mem->penalty);
- ast_str_append(&out, 0, "%s%s%s (%s)",
- mem->dynamic ? " (dynamic)" : "",
- mem->realtime ? " (realtime)" : "",
- mem->paused ? " (paused)" : "",
- devstate2str(mem->status));
- if (mem->calls)
- ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
- mem->calls, (long) (time(NULL) - mem->lastcall));
- else
- ast_str_append(&out, 0, " has taken no calls yet");
- do_print(s, fd, out->str);
- ao2_ref(mem, -1);
- }
- }
- if (!q->head)
- do_print(s, fd, " No Callers");
- else {
- struct queue_ent *qe;
- int pos = 1;
-
- do_print(s, fd, " Callers: ");
- for (qe = q->head; qe; qe = qe->next) {
- ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
- pos++, qe->chan->name, (long) (now - qe->start) / 60,
- (long) (now - qe->start) % 60, qe->prio);
- do_print(s, fd, out->str);
- }
- }
- do_print(s, fd, ""); /* blank line between entries */
- ao2_unlock(q);
- if (argc == 3) { /* print a specific entry */
- queue_unref(q);
- break;
- }
- queue_unref(q);
- }
- if (!found) {
- if (argc == 3)
- ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
- else
- ast_str_set(&out, 0, "No queues.");
- do_print(s, fd, out->str);
- }
- return CLI_SUCCESS;
-}
-
-static char *complete_queue(const char *line, const char *word, int pos, int state)
-{
- struct call_queue *q;
- char *ret = NULL;
- int which = 0;
- int wordlen = strlen(word);
- struct ao2_iterator queue_iter;
-
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
- ret = ast_strdup(q->name);
- queue_unref(q);
- break;
- }
- queue_unref(q);
- }
-
- return ret;
-}
-
-static char *complete_queue_show(const char *line, const char *word, int pos, int state)
-{
- if (pos == 2)
- return complete_queue(line, word, pos, state);
- return NULL;
-}
-
-static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- switch ( cmd ) {
- case CLI_INIT:
- e->command = "queue show";
- e->usage =
- "Usage: queue show\n"
- " Provides summary information on a specified queue.\n";
- return NULL;
- case CLI_GENERATE:
- return complete_queue_show(a->line, a->word, a->pos, a->n);
- }
-
- return __queues_show(NULL, a->fd, a->argc, a->argv);
-}
-
-/*!\brief callback to display queues status in manager
- \addtogroup Group_AMI
- */
-static int manager_queues_show(struct mansession *s, const struct message *m)
-{
- char *a[] = { "queue", "show" };
-
- __queues_show(s, -1, 2, a);
- astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
-
- return RESULT_SUCCESS;
-}
-
-static int manager_queue_rule_show(struct mansession *s, const struct message *m)
-{
- const char *rule = astman_get_header(m, "Rule");
- struct rule_list *rl_iter;
- struct penalty_rule *pr_iter;
-
- AST_LIST_LOCK(&rule_lists);
- AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
- if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
- astman_append(s, "RuleList: %s\r\n", rl_iter->name);
- AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
- astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
- }
- if (!ast_strlen_zero(rule))
- break;
- }
- }
- AST_LIST_UNLOCK(&rule_lists);
-
- astman_append(s, "\r\n\r\n");
-
- return RESULT_SUCCESS;
-}
-
-/* Dump summary of queue info */
-static int manager_queues_summary(struct mansession *s, const struct message *m)
-{
- time_t now;
- int qmemcount = 0;
- int qmemavail = 0;
- int qchancount = 0;
- int qlongestholdtime = 0;
- const char *id = astman_get_header(m, "ActionID");
- const char *queuefilter = astman_get_header(m, "Queue");
- char idText[256] = "";
- struct call_queue *q;
- struct queue_ent *qe;
- struct member *mem;
- struct ao2_iterator queue_iter;
- struct ao2_iterator mem_iter;
-
- astman_send_ack(s, m, "Queue summary will follow");
- time(&now);
- if (!ast_strlen_zero(id))
- snprintf(idText, 256, "ActionID: %s\r\n", id);
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- ao2_lock(q);
-
- /* List queue properties */
- if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
- /* Reset the necessary local variables if no queuefilter is set*/
- qmemcount = 0;
- qmemavail = 0;
- qchancount = 0;
- qlongestholdtime = 0;
-
- /* List Queue Members */
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((mem = ao2_iterator_next(&mem_iter))) {
- if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
- ++qmemcount;
- if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
- ++qmemavail;
- }
- }
- ao2_ref(mem, -1);
- }
- for (qe = q->head; qe; qe = qe->next) {
- if ((now - qe->start) > qlongestholdtime) {
- qlongestholdtime = now - qe->start;
- }
- ++qchancount;
- }
- astman_append(s, "Event: QueueSummary\r\n"
- "Queue: %s\r\n"
- "LoggedIn: %d\r\n"
- "Available: %d\r\n"
- "Callers: %d\r\n"
- "HoldTime: %d\r\n"
- "LongestHoldTime: %d\r\n"
- "%s"
- "\r\n",
- q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
- }
- ao2_unlock(q);
- queue_unref(q);
- }
- astman_append(s,
- "Event: QueueSummaryComplete\r\n"
- "%s"
- "\r\n", idText);
-
- return RESULT_SUCCESS;
-}
-
-/* Dump queue status */
-static int manager_queues_status(struct mansession *s, const struct message *m)
-{
- time_t now;
- int pos;
- const char *id = astman_get_header(m,"ActionID");
- const char *queuefilter = astman_get_header(m,"Queue");
- const char *memberfilter = astman_get_header(m,"Member");
- char idText[256] = "";
- struct call_queue *q;
- struct queue_ent *qe;
- float sl = 0;
- struct member *mem;
- struct ao2_iterator queue_iter;
- struct ao2_iterator mem_iter;
-
- astman_send_ack(s, m, "Queue status will follow");
- time(&now);
- if (!ast_strlen_zero(id))
- snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
-
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- ao2_lock(q);
-
- /* List queue properties */
- if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
- sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
- astman_append(s, "Event: QueueParams\r\n"
- "Queue: %s\r\n"
- "Max: %d\r\n"
- "Strategy: %s\r\n"
- "Calls: %d\r\n"
- "Holdtime: %d\r\n"
- "Completed: %d\r\n"
- "Abandoned: %d\r\n"
- "ServiceLevel: %d\r\n"
- "ServicelevelPerf: %2.1f\r\n"
- "Weight: %d\r\n"
- "%s"
- "\r\n",
- q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
- q->callsabandoned, q->servicelevel, sl, q->weight, idText);
- /* List Queue Members */
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((mem = ao2_iterator_next(&mem_iter))) {
- if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
- astman_append(s, "Event: QueueMember\r\n"
- "Queue: %s\r\n"
- "Name: %s\r\n"
- "Location: %s\r\n"
- "Membership: %s\r\n"
- "Penalty: %d\r\n"
- "CallsTaken: %d\r\n"
- "LastCall: %d\r\n"
- "Status: %d\r\n"
- "Paused: %d\r\n"
- "%s"
- "\r\n",
- q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
- mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
- }
- ao2_ref(mem, -1);
- }
- /* List Queue Entries */
- pos = 1;
- for (qe = q->head; qe; qe = qe->next) {
- astman_append(s, "Event: QueueEntry\r\n"
- "Queue: %s\r\n"
- "Position: %d\r\n"
- "Channel: %s\r\n"
- "CallerIDNum: %s\r\n"
- "CallerIDName: %s\r\n"
- "Wait: %ld\r\n"
- "%s"
- "\r\n",
- q->name, pos++, qe->chan->name,
- S_OR(qe->chan->cid.cid_num, "unknown"),
- S_OR(qe->chan->cid.cid_name, "unknown"),
- (long) (now - qe->start), idText);
- }
- }
- ao2_unlock(q);
- queue_unref(q);
- }
-
- astman_append(s,
- "Event: QueueStatusComplete\r\n"
- "%s"
- "\r\n",idText);
-
- return RESULT_SUCCESS;
-}
-
-static int manager_add_queue_member(struct mansession *s, const struct message *m)
-{
- const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
- int paused, penalty = 0;
-
- queuename = astman_get_header(m, "Queue");
- interface = astman_get_header(m, "Interface");
- penalty_s = astman_get_header(m, "Penalty");
- paused_s = astman_get_header(m, "Paused");
- membername = astman_get_header(m, "MemberName");
- state_interface = astman_get_header(m, "StateInterface");
-
- if (ast_strlen_zero(queuename)) {
- astman_send_error(s, m, "'Queue' not specified.");
- return 0;
- }
-
- if (ast_strlen_zero(interface)) {
- astman_send_error(s, m, "'Interface' not specified.");
- return 0;
- }
-
- if (ast_strlen_zero(penalty_s))
- penalty = 0;
- else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
- penalty = 0;
-
- if (ast_strlen_zero(paused_s))
- paused = 0;
- else
- paused = abs(ast_true(paused_s));
-
- switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
- case RES_OKAY:
- ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
- astman_send_ack(s, m, "Added interface to queue");
- break;
- case RES_EXISTS:
- astman_send_error(s, m, "Unable to add interface: Already there");
- break;
- case RES_NOSUCHQUEUE:
- astman_send_error(s, m, "Unable to add interface to queue: No such queue");
- break;
- case RES_OUTOFMEMORY:
- astman_send_error(s, m, "Out of memory");
- break;
- }
-
- return 0;
-}
-
-static int manager_remove_queue_member(struct mansession *s, const struct message *m)
-{
- const char *queuename, *interface;
-
- queuename = astman_get_header(m, "Queue");
- interface = astman_get_header(m, "Interface");
-
- if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
- astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
- return 0;
- }
-
- switch (remove_from_queue(queuename, interface)) {
- case RES_OKAY:
- ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
- astman_send_ack(s, m, "Removed interface from queue");
- break;
- case RES_EXISTS:
- astman_send_error(s, m, "Unable to remove interface: Not there");
- break;
- case RES_NOSUCHQUEUE:
- astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
- break;
- case RES_OUTOFMEMORY:
- astman_send_error(s, m, "Out of memory");
- break;
- case RES_NOT_DYNAMIC:
- astman_send_error(s, m, "Member not dynamic");
- break;
- }
-
- return 0;
-}
-
-static int manager_pause_queue_member(struct mansession *s, const struct message *m)
-{
- const char *queuename, *interface, *paused_s, *reason;
- int paused;
-
- interface = astman_get_header(m, "Interface");
- paused_s = astman_get_header(m, "Paused");
- queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
- reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */
-
- if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
- astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
- return 0;
- }
-
- paused = abs(ast_true(paused_s));
-
- if (set_member_paused(queuename, interface, reason, paused))
- astman_send_error(s, m, "Interface not found");
- else
- astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
- return 0;
-}
-
-static int manager_queue_log_custom(struct mansession *s, const struct message *m)
-{
- const char *queuename, *event, *message, *interface, *uniqueid;
-
- queuename = astman_get_header(m, "Queue");
- uniqueid = astman_get_header(m, "UniqueId");
- interface = astman_get_header(m, "Interface");
- event = astman_get_header(m, "Event");
- message = astman_get_header(m, "Message");
-
- if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
- astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
- return 0;
- }
-
- ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
- astman_send_ack(s, m, "Event added successfully");
-
- return 0;
-}
-
-static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
-{
- /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
- switch (pos) {
- case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
- return NULL;
- case 4: /* only one possible match, "to" */
- return state == 0 ? ast_strdup("to") : NULL;
- case 5: /* <queue> */
- return complete_queue(line, word, pos, state);
- case 6: /* only one possible match, "penalty" */
- return state == 0 ? ast_strdup("penalty") : NULL;
- case 7:
- if (state < 100) { /* 0-99 */
- char *num;
- if ((num = ast_malloc(3))) {
- sprintf(num, "%d", state);
- }
- return num;
- } else {
- return NULL;
- }
- case 8: /* only one possible match, "as" */
- return state == 0 ? ast_strdup("as") : NULL;
- case 9: /* Don't attempt to complete name of member (infinite possibilities) */
- return NULL;
- default:
- return NULL;
- }
-}
-
-static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
-{
- const char *queuename, *interface, *penalty_s;
- int penalty;
-
- interface = astman_get_header(m, "Interface");
- penalty_s = astman_get_header(m, "Penalty");
- /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
- queuename = astman_get_header(m, "Queue");
-
- if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
- astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
- return 0;
- }
-
- penalty = atoi(penalty_s);
-
- if (set_member_penalty((char *)queuename, (char *)interface, penalty))
- astman_send_error(s, m, "Invalid interface, queuename or penalty");
- else
- astman_send_ack(s, m, "Interface penalty set successfully");
-
- return 0;
-}
-
-static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- char *queuename, *interface, *membername = NULL, *state_interface = NULL;
- int penalty;
-
- switch ( cmd ) {
- case CLI_INIT:
- e->command = "queue add member";
- e->usage =
- "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n";
- return NULL;
- case CLI_GENERATE:
- return complete_queue_add_member(a->line, a->word, a->pos, a->n);
- }
-
- if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
- return CLI_SHOWUSAGE;
- } else if (strcmp(a->argv[4], "to")) {
- return CLI_SHOWUSAGE;
- } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
- return CLI_SHOWUSAGE;
- } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
- return CLI_SHOWUSAGE;
- } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
- return CLI_SHOWUSAGE;
- }
-
- queuename = a->argv[5];
- interface = a->argv[3];
- if (a->argc >= 8) {
- if (sscanf(a->argv[7], "%d", &penalty) == 1) {
- if (penalty < 0) {
- ast_cli(a->fd, "Penalty must be >= 0\n");
- penalty = 0;
- }
- } else {
- ast_cli(a->fd, "Penalty must be an integer >= 0\n");
- penalty = 0;
- }
- } else {
- penalty = 0;
- }
-
- if (a->argc >= 10) {
- membername = a->argv[9];
- }
-
- if (a->argc >= 12) {
- state_interface = a->argv[11];
- }
-
- switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
- case RES_OKAY:
- ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
- ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
- return CLI_SUCCESS;
- case RES_EXISTS:
- ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
- return CLI_FAILURE;
- case RES_NOSUCHQUEUE:
- ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
- return CLI_FAILURE;
- case RES_OUTOFMEMORY:
- ast_cli(a->fd, "Out of memory\n");
- return CLI_FAILURE;
- case RES_NOT_DYNAMIC:
- ast_cli(a->fd, "Member not dynamic\n");
- return CLI_FAILURE;
- default:
- return CLI_FAILURE;
- }
-}
-
-static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
-{
- int which = 0;
- struct call_queue *q;
- struct member *m;
- struct ao2_iterator queue_iter;
- struct ao2_iterator mem_iter;
- int wordlen = strlen(word);
-
- /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
- if (pos > 5 || pos < 3)
- return NULL;
- if (pos == 4) /* only one possible match, 'from' */
- return (state == 0 ? ast_strdup("from") : NULL);
-
- if (pos == 5) /* No need to duplicate code */
- return complete_queue(line, word, pos, state);
-
- /* here is the case for 3, <member> */
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_iterator_next(&queue_iter))) {
- ao2_lock(q);
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
- char *tmp;
- ao2_unlock(q);
- tmp = m->membername;
- ao2_ref(m, -1);
- queue_unref(q);
- return ast_strdup(tmp);
- }
- ao2_ref(m, -1);
- }
- ao2_unlock(q);
- queue_unref(q);
- }
-
- return NULL;
-}
-
-static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- char *queuename, *interface;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "queue remove member";
- e->usage = "Usage: queue remove member <channel> from <queue>\n";
- return NULL;
- case CLI_GENERATE:
- return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
- }
-
- if (a->argc != 6) {
- return CLI_SHOWUSAGE;
- } else if (strcmp(a->argv[4], "from")) {
- return CLI_SHOWUSAGE;
- }
-
- queuename = a->argv[5];
- interface = a->argv[3];
-
- switch (remove_from_queue(queuename, interface)) {
- case RES_OKAY:
- ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
- ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
- return CLI_SUCCESS;
- case RES_EXISTS:
- ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
- return CLI_FAILURE;
- case RES_NOSUCHQUEUE:
- ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
- return CLI_FAILURE;
- case RES_OUTOFMEMORY:
- ast_cli(a->fd, "Out of memory\n");
- return CLI_FAILURE;
- default:
- return CLI_FAILURE;
- }
-}
-
-static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
-{
- /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
- switch (pos) {
- case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
- return NULL;
- case 4: /* only one possible match, "queue" */
- return state == 0 ? ast_strdup("queue") : NULL;
- case 5: /* <queue> */
- return complete_queue(line, word, pos, state);
- case 6: /* "reason" */
- return state == 0 ? ast_strdup("reason") : NULL;
- case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
- return NULL;
- default:
- return NULL;
- }
-}
-
-static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- char *queuename, *interface, *reason;
- int paused;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "queue {pause|unpause} member";
- e->usage =
- "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
- " Pause or unpause a queue member. Not specifying a particular queue\n"
- " will pause or unpause a member across all queues to which the member\n"
- " belongs.\n";
- return NULL;
- case CLI_GENERATE:
- return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
- }
-
- if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
- return CLI_SHOWUSAGE;
- } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
- return CLI_SHOWUSAGE;
- } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
- return CLI_SHOWUSAGE;
- }
-
-
- interface = a->argv[3];
- queuename = a->argc >= 6 ? a->argv[5] : NULL;
- reason = a->argc == 8 ? a->argv[7] : NULL;
- paused = !strcasecmp(a->argv[1], "pause");
-
- if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
- ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
- if (!ast_strlen_zero(queuename))
- ast_cli(a->fd, " in queue '%s'", queuename);
- if (!ast_strlen_zero(reason))
- ast_cli(a->fd, " for reason '%s'", reason);
- ast_cli(a->fd, "\n");
- return CLI_SUCCESS;
- } else {
- ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
- if (!ast_strlen_zero(queuename))
- ast_cli(a->fd, " in queue '%s'", queuename);
- if (!ast_strlen_zero(reason))
- ast_cli(a->fd, " for reason '%s'", reason);
- ast_cli(a->fd, "\n");
- return CLI_FAILURE;
- }
-}
-
-static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
-{
- /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
- switch (pos) {
- case 4:
- if (state == 0) {
- return ast_strdup("on");
- } else {
- return NULL;
- }
- case 6:
- if (state == 0) {
- return ast_strdup("in");
- } else {
- return NULL;
- }
- case 7:
- return complete_queue(line, word, pos, state);
- default:
- return NULL;
- }
-}
-
-static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- char *queuename = NULL, *interface;
- int penalty = 0;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "queue set penalty";
- e->usage =
- "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
- "Set a member's penalty in the queue specified. If no queue is specified\n"
- "then that interface's penalty is set in all queues to which that interface is a member\n";
- return NULL;
- case CLI_GENERATE:
- return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
- }
-
- if (a->argc != 6 && a->argc != 8) {
- return CLI_SHOWUSAGE;
- } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
- return CLI_SHOWUSAGE;
- }
-
- if (a->argc == 8)
- queuename = a->argv[7];
- interface = a->argv[5];
- penalty = atoi(a->argv[3]);
-
- switch (set_member_penalty(queuename, interface, penalty)) {
- case RESULT_SUCCESS:
- ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
- return CLI_SUCCESS;
- case RESULT_FAILURE:
- ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
- return CLI_FAILURE;
- default:
- return CLI_FAILURE;
- }
-}
-
-static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
-{
- int which = 0;
- struct rule_list *rl_iter;
- int wordlen = strlen(word);
- char *ret = NULL;
- if (pos != 3) /* Wha? */ {
- ast_log(LOG_DEBUG, "Hitting this???, pos is %d\n", pos);
- return NULL;
- }
-
- AST_LIST_LOCK(&rule_lists);
- AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
- if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
- ret = ast_strdup(rl_iter->name);
- break;
- }
- }
- AST_LIST_UNLOCK(&rule_lists);
-
- return ret;
-}
-
-static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- char *rule;
- struct rule_list *rl_iter;
- struct penalty_rule *pr_iter;
- switch (cmd) {
- case CLI_INIT:
- e->command = "queue rules show";
- e->usage =
- "Usage: queue rules show [rulename]\n"
- "Show the list of rules associated with rulename. If no\n"
- "rulename is specified, list all rules defined in queuerules.conf\n";
- return NULL;
- case CLI_GENERATE:
- return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
- }
-
- if (a->argc != 3 && a->argc != 4)
- return CLI_SHOWUSAGE;
-
- rule = a->argc == 4 ? a->argv[3] : "";
- AST_LIST_LOCK(&rule_lists);
- AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
- if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
- ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
- AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
- ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value);
- }
- }
- }
- AST_LIST_UNLOCK(&rule_lists);
- return CLI_SUCCESS;
-}
-
-static char *handle_queue_rule_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- switch (cmd) {
- case CLI_INIT:
- e->command = "queue rules reload";
- e->usage =
- "Usage: queue rules reload\n"
- "Reloads rules defined in queuerules.conf\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- reload_queue_rules(1);
- return CLI_SUCCESS;
-}
-
-static const char qpm_cmd_usage[] =
-"Usage: queue pause member <channel> in <queue> reason <reason>\n";
-
-static const char qum_cmd_usage[] =
-"Usage: queue unpause member <channel> in <queue> reason <reason>\n";
-
-static const char qsmp_cmd_usage[] =
-"Usage: queue set member penalty <channel> from <queue> <penalty>\n";
-
-static struct ast_cli_entry cli_queue[] = {
- AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
- AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
- AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
- AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
- AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
- AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
- AST_CLI_DEFINE(handle_queue_rule_reload, "Reload the rules defined in queuerules.conf"),
-};
-
-static int unload_module(void)
-{
- int res;
- struct ast_context *con;
-
- if (device_state.thread != AST_PTHREADT_NULL) {
- device_state.stop = 1;
- ast_mutex_lock(&device_state.lock);
- ast_cond_signal(&device_state.cond);
- ast_mutex_unlock(&device_state.lock);
- pthread_join(device_state.thread, NULL);
- }
-
- ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
- res = ast_manager_unregister("QueueStatus");
- res |= ast_manager_unregister("Queues");
- res |= ast_manager_unregister("QueueStatus");
- res |= ast_manager_unregister("QueueSummary");
- res |= ast_manager_unregister("QueueAdd");
- res |= ast_manager_unregister("QueueRemove");
- res |= ast_manager_unregister("QueuePause");
- res |= ast_manager_unregister("QueueLog");
- res |= ast_manager_unregister("QueuePenalty");
- res |= ast_unregister_application(app_aqm);
- res |= ast_unregister_application(app_rqm);
- res |= ast_unregister_application(app_pqm);
- res |= ast_unregister_application(app_upqm);
- res |= ast_unregister_application(app_ql);
- res |= ast_unregister_application(app);
- res |= ast_custom_function_unregister(&queuevar_function);
- res |= ast_custom_function_unregister(&queuemembercount_function);
- res |= ast_custom_function_unregister(&queuemembercount_dep);
- res |= ast_custom_function_unregister(&queuememberlist_function);
- res |= ast_custom_function_unregister(&queuewaitingcount_function);
- res |= ast_custom_function_unregister(&queuememberpenalty_function);
-
- if (device_state_sub)
- ast_event_unsubscribe(device_state_sub);
-
- if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
- ast_context_remove_extension2(con, "s", 1, NULL);
- ast_context_destroy(con, "app_queue"); /* leave no trace */
- }
-
- clear_and_free_interfaces();
-
- ao2_ref(queues, -1);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
- struct ast_context *con;
-
- queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
-
- if (!reload_queues(0))
- return AST_MODULE_LOAD_DECLINE;
-
- con = ast_context_find("app_queue_gosub_virtual_context");
- if (!con)
- con = ast_context_create(NULL, "app_queue_gosub_virtual_context", "app_queue");
- if (!con)
- ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
- else
- ast_add_extension2(con, 1, "s", 1, NULL, NULL, "KeepAlive", ast_strdup(""), ast_free_ptr, "app_queue");
-
- if (queue_persistent_members)
- reload_queue_members();
-
- ast_mutex_init(&device_state.lock);
- ast_cond_init(&device_state.cond, NULL);
- ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
-
- ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
- res = ast_register_application(app, queue_exec, synopsis, descrip);
- res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
- res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
- res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
- res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
- res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
- res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
- res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
- res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
- res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
- res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
- res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
- res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
- res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member");
- res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
- res |= ast_custom_function_register(&queuevar_function);
- res |= ast_custom_function_register(&queuemembercount_function);
- res |= ast_custom_function_register(&queuemembercount_dep);
- res |= ast_custom_function_register(&queuememberlist_function);
- res |= ast_custom_function_register(&queuewaitingcount_function);
- res |= ast_custom_function_register(&queuememberpenalty_function);
- if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END)))
- res = -1;
-
- return res ? AST_MODULE_LOAD_DECLINE : 0;
-}
-
-static int reload(void)
-{
- reload_queues(1);
- return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
-
diff --git a/trunk/apps/app_read.c b/trunk/apps/app_read.c
deleted file mode 100644
index a7fdf624b..000000000
--- a/trunk/apps/app_read.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Trivial application to read a variable
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/pbx.h"
-#include "asterisk/channel.h"
-#include "asterisk/app.h"
-#include "asterisk/module.h"
-#include "asterisk/indications.h"
-
-enum {
- OPT_SKIP = (1 << 0),
- OPT_INDICATION = (1 << 1),
- OPT_NOANSWER = (1 << 2),
-} read_option_flags;
-
-AST_APP_OPTIONS(read_app_options, {
- AST_APP_OPTION('s', OPT_SKIP),
- AST_APP_OPTION('i', OPT_INDICATION),
- AST_APP_OPTION('n', OPT_NOANSWER),
-});
-
-static char *app = "Read";
-
-static char *synopsis = "Read a variable";
-
-static char *descrip =
-" Read(variable[,filename[&filename2...]][,maxdigits][,option][,attempts][,timeout])\n\n"
-"Reads a #-terminated string of digits a certain number of times from the\n"
-"user in to the given variable.\n"
-" filename -- file(s) to play before reading digits or tone with option i\n"
-" maxdigits -- maximum acceptable number of digits. Stops reading after\n"
-" maxdigits have been entered (without requiring the user to\n"
-" press the '#' key).\n"
-" Defaults to 0 - no limit - wait for the user press the '#' key.\n"
-" Any value below 0 means the same. Max accepted value is 255.\n"
-" option -- options are 's' , 'i', 'n'\n"
-" 's' to return immediately if the line is not up,\n"
-" 'i' to play filename as an indication tone from your indications.conf\n"
-" 'n' to read digits even if the line is not up.\n"
-" attempts -- if greater than 1, that many attempts will be made in the \n"
-" event no data is entered.\n"
-" timeout -- The number of seconds to wait for a digit response. If greater\n"
-" than 0, that value will override the default timeout. Can be floating point.\n"
-"This application sets the following channel variable upon completion:\n"
-" READSTATUS - This is the status of the read operation.\n"
-" Possible values are:\n"
-" OK | ERROR | HANGUP | INTERRUPTED | SKIPPED | TIMEOUT\n";
-
-
-#define ast_next_data(instr,ptr,delim) if((ptr=strchr(instr,delim))) { *(ptr) = '\0' ; ptr++;}
-
-static int read_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char tmp[256] = "";
- int maxdigits = 255;
- int tries = 1, to = 0, x = 0;
- double tosec;
- char *argcopy = NULL;
- struct ind_tone_zone_sound *ts = NULL;
- struct ast_flags flags = {0};
- const char *status = "ERROR";
-
- AST_DECLARE_APP_ARGS(arglist,
- AST_APP_ARG(variable);
- AST_APP_ARG(filename);
- AST_APP_ARG(maxdigits);
- AST_APP_ARG(options);
- AST_APP_ARG(attempts);
- AST_APP_ARG(timeout);
- );
-
- pbx_builtin_setvar_helper(chan, "READSTATUS", status);
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Read requires an argument (variable)\n");
- return 0;
- }
-
- argcopy = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(arglist, argcopy);
-
- if (!ast_strlen_zero(arglist.options)) {
- ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);
- }
-
- if (!ast_strlen_zero(arglist.attempts)) {
- tries = atoi(arglist.attempts);
- if (tries <= 0)
- tries = 1;
- }
-
- if (!ast_strlen_zero(arglist.timeout)) {
- tosec = atof(arglist.timeout);
- if (tosec <= 0)
- to = 0;
- else
- to = tosec * 1000.0;
- }
-
- if (ast_strlen_zero(arglist.filename)) {
- arglist.filename = NULL;
- }
- if (!ast_strlen_zero(arglist.maxdigits)) {
- maxdigits = atoi(arglist.maxdigits);
- if ((maxdigits < 1) || (maxdigits > 255)) {
- maxdigits = 255;
- } else
- ast_verb(3, "Accepting a maximum of %d digits.\n", maxdigits);
- }
- if (ast_strlen_zero(arglist.variable)) {
- ast_log(LOG_WARNING, "Invalid! Usage: Read(variable[,filename][,maxdigits][,option][,attempts][,timeout])\n\n");
- return 0;
- }
- if (ast_test_flag(&flags, OPT_INDICATION)) {
- if (! ast_strlen_zero(arglist.filename)) {
- ts = ast_get_indication_tone(chan->zone, arglist.filename);
- }
- }
- if (chan->_state != AST_STATE_UP) {
- if (ast_test_flag(&flags, OPT_SKIP)) {
- /* At the user's option, skip if the line is not up */
- pbx_builtin_setvar_helper(chan, arglist.variable, "");
- pbx_builtin_setvar_helper(chan, "READSTATUS", "SKIPPED");
- return 0;
- } else if (!ast_test_flag(&flags, OPT_NOANSWER)) {
- /* Otherwise answer unless we're supposed to read while on-hook */
- res = ast_answer(chan);
- }
- }
- if (!res) {
- while (tries && !res) {
- ast_stopstream(chan);
- if (ts && ts->data[0]) {
- if (!to)
- to = chan->pbx ? chan->pbx->rtimeout * 1000 : 6000;
- res = ast_playtones_start(chan, 0, ts->data, 0);
- for (x = 0; x < maxdigits; ) {
- res = ast_waitfordigit(chan, to);
- ast_playtones_stop(chan);
- if (res < 1) {
- if (res == 0)
- status = "TIMEOUT";
- tmp[x]='\0';
- break;
- }
- tmp[x++] = res;
- if (tmp[x-1] == '#') {
- tmp[x-1] = '\0';
- status = "OK";
- break;
- }
- if (x >= maxdigits) {
- status = "OK";
- }
- }
- } else {
- res = ast_app_getdata(chan, arglist.filename, tmp, maxdigits, to);
- if (res == 0)
- status = "OK";
- else if (res == 1)
- status = "TIMEOUT";
- else if (res == 2)
- status = "INTERRUPTED";
- }
- if (res > -1) {
- pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
- if (!ast_strlen_zero(tmp)) {
- ast_verb(3, "User entered '%s'\n", tmp);
- tries = 0;
- } else {
- tries--;
- if (tries)
- ast_verb(3, "User entered nothing, %d chance%s left\n", tries, (tries != 1) ? "s" : "");
- else
- ast_verb(3, "User entered nothing.\n");
- }
- res = 0;
- } else {
- pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
- ast_verb(3, "User disconnected\n");
- }
- }
- }
-
- if (ast_check_hangup(chan))
- status = "HANGUP";
- pbx_builtin_setvar_helper(chan, "READSTATUS", status);
- return 0;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, read_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read Variable Application");
diff --git a/trunk/apps/app_readexten.c b/trunk/apps/app_readexten.c
deleted file mode 100644
index cb4fd8c2a..000000000
--- a/trunk/apps/app_readexten.c
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2007 Dave Chappell
- *
- * David Chappell <David.Chappell@trincoll.edu>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Trivial application to read an extension into a variable
- *
- * \author David Chappell <David.Chappell@trincoll.edu>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/pbx.h"
-#include "asterisk/app.h"
-#include "asterisk/module.h"
-#include "asterisk/indications.h"
-#include "asterisk/channel.h"
-
-enum {
- OPT_SKIP = (1 << 0),
- OPT_INDICATION = (1 << 1),
- OPT_NOANSWER = (1 << 2),
-} readexten_option_flags;
-
-AST_APP_OPTIONS(readexten_app_options, {
- AST_APP_OPTION('s', OPT_SKIP),
- AST_APP_OPTION('i', OPT_INDICATION),
- AST_APP_OPTION('n', OPT_NOANSWER),
-});
-
-static char *app = "ReadExten";
-
-static char *synopsis = "Read an extension into a variable";
-
-static char *descrip =
-" ReadExten(<variable>[,[<filename>][,[<context>][,[<option>][,<timeout>]]]])\n\n"
-"Reads a #-terminated string of digits from the user into the given variable.\n"
-" filename file to play before reading digits or tone with option i\n"
-" context context in which to match extensions\n"
-" option options are:\n"
-" s - Return immediately if the channel is not answered,\n"
-" i - Play filename as an indication tone from your\n"
-" indications.conf\n"
-" n - Read digits even if the channel is not answered.\n"
-" timeout An integer number of seconds to wait for a digit response. If\n"
-" greater than 0, that value will override the default timeout.\n\n"
-"ReadExten will set READEXTENSTATUS on exit with one of the following statuses:\n"
-" OK A valid extension exists in ${variable}\n"
-" TIMEOUT No extension was entered in the specified time\n"
-" INVALID An invalid extension, ${INVALID_EXTEN}, was entered\n"
-" SKIP Line was not up and the option 's' was specified\n"
-" ERROR Invalid arguments were passed\n";
-
-
-static int readexten_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char exten[256] = "";
- int maxdigits = sizeof(exten) - 1;
- int timeout = 0, digit_timeout = 0, x = 0;
- char *argcopy = NULL, *status = "";
- struct ind_tone_zone_sound *ts = NULL;
- struct ast_flags flags = {0};
-
- AST_DECLARE_APP_ARGS(arglist,
- AST_APP_ARG(variable);
- AST_APP_ARG(filename);
- AST_APP_ARG(context);
- AST_APP_ARG(options);
- AST_APP_ARG(timeout);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "ReadExten requires at least one argument\n");
- pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", "ERROR");
- return 0;
- }
-
- argcopy = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(arglist, argcopy);
-
- if (ast_strlen_zero(arglist.variable)) {
- ast_log(LOG_WARNING, "Invalid! Usage: ReadExten(variable[|filename][|context][|options][|timeout])\n\n");
- pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", "ERROR");
- return 0;
- }
-
- if (ast_strlen_zero(arglist.filename))
- arglist.filename = NULL;
-
- if (ast_strlen_zero(arglist.context))
- arglist.context = chan->context;
-
- if (!ast_strlen_zero(arglist.options))
- ast_app_parse_options(readexten_app_options, &flags, NULL, arglist.options);
-
- if (!ast_strlen_zero(arglist.timeout)) {
- timeout = atoi(arglist.timeout);
- if (timeout > 0)
- timeout *= 1000;
- }
-
- if (timeout <= 0)
- timeout = chan->pbx ? chan->pbx->rtimeout * 1000 : 10000;
-
- if (digit_timeout <= 0)
- digit_timeout = chan->pbx ? chan->pbx->dtimeout * 1000 : 5000;
-
- if (ast_test_flag(&flags, OPT_INDICATION) && !ast_strlen_zero(arglist.filename))
- ts = ast_get_indication_tone(chan->zone, arglist.filename);
-
- do {
- if (chan->_state != AST_STATE_UP) {
- if (ast_test_flag(&flags, OPT_SKIP)) {
- /* At the user's option, skip if the line is not up */
- pbx_builtin_setvar_helper(chan, arglist.variable, "");
- status = "SKIP";
- break;
- } else if (!ast_test_flag(&flags, OPT_NOANSWER)) {
- /* Otherwise answer unless we're supposed to read while on-hook */
- res = ast_answer(chan);
- }
- }
-
- if (res < 0) {
- status = "HANGUP";
- break;
- }
-
- ast_playtones_stop(chan);
- ast_stopstream(chan);
-
- if (ts && ts->data[0])
- res = ast_playtones_start(chan, 0, ts->data, 0);
- else if (arglist.filename)
- res = ast_streamfile(chan, arglist.filename, chan->language);
-
- for (x = 0; x < maxdigits; x++) {
- ast_debug(3, "extension so far: '%s', timeout: %d\n", exten, timeout);
- res = ast_waitfordigit(chan, timeout);
-
- ast_playtones_stop(chan);
- ast_stopstream(chan);
- timeout = digit_timeout;
-
- if (res < 1) { /* timeout expired or hangup */
- if (ast_check_hangup(chan))
- status = "HANGUP";
- else
- status = "TIMEOUT";
- break;
- } else if (res == '#') {
- break;
- }
-
- exten[x] = res;
- if (!ast_matchmore_extension(chan, arglist.context, exten, 1 /* priority */, chan->cid.cid_num)) {
- break;
- }
- }
-
- if (!ast_strlen_zero(status))
- break;
-
- if (ast_exists_extension(chan, arglist.context, exten, 1, chan->cid.cid_num)) {
- ast_debug(3, "User entered valid extension '%s'\n", exten);
- pbx_builtin_setvar_helper(chan, arglist.variable, exten);
- status = "OK";
- } else {
- ast_debug(3, "User dialed invalid extension '%s' in context '%s' on %s\n", exten, arglist.context, chan->name);
- pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
- status = "INVALID";
- }
- } while (0);
-
- pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", status);
-
- return status[0] == 'H' ? -1 : 0;
-}
-
-static int acf_isexten_exec(struct ast_channel *chan, const char *cmd, char *parse, char *buffer, size_t buflen)
-{
- int priority_int;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(context);
- AST_APP_ARG(extension);
- AST_APP_ARG(priority);
- );
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (ast_strlen_zero(args.context))
- args.context = chan->context;
-
- if (ast_strlen_zero(args.extension)) {
- ast_log(LOG_WARNING, "Syntax: VALID_EXTEN([<context>],<extension>[,<priority>]) - missing argument <extension>!\n");
- return -1;
- }
-
- if (ast_strlen_zero(args.priority))
- priority_int = 1;
- else
- priority_int = atoi(args.priority);
-
- if (ast_exists_extension(chan, args.context, args.extension, priority_int, chan->cid.cid_num))
- ast_copy_string(buffer, "1", buflen);
- else
- ast_copy_string(buffer, "0", buflen);
-
- return 0;
-}
-
-static struct ast_custom_function acf_isexten = {
- .name = "VALID_EXTEN",
- .synopsis = "Determine whether an extension exists or not",
- .syntax = "VALID_EXTEN([<context>],<extension>[,<priority>])",
- .desc =
-"Returns a true value if the indicated context, extension, and priority exist.\n"
-"Context defaults to the current context, priority defaults to 1.\n",
- .read = acf_isexten_exec,
-};
-
-static int unload_module(void)
-{
- int res = ast_unregister_application(app);
- res |= ast_custom_function_unregister(&acf_isexten);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res = ast_register_application(app, readexten_exec, synopsis, descrip);
- res |= ast_custom_function_register(&acf_isexten);
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read and evaluate extension validity");
diff --git a/trunk/apps/app_readfile.c b/trunk/apps/app_readfile.c
deleted file mode 100644
index 8762ef860..000000000
--- a/trunk/apps/app_readfile.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Matt O'Gorman <mogorman@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief ReadFile application -- Reads in a File for you.
- *
- * \author Matt O'Gorman <mogorman@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/app.h"
-#include "asterisk/module.h"
-
-static char *app_readfile = "ReadFile";
-
-static char *readfile_synopsis = "Read the contents of a text file into a channel variable";
-
-static char *readfile_descrip =
-"ReadFile(varname=file,length)\n"
-" varname - Result stored here.\n"
-" file - The name of the file to read.\n"
-" length - Maximum number of characters to capture.\n";
-
-
-static int readfile_exec(struct ast_channel *chan, void *data)
-{
- int res=0;
- char *s, *varname=NULL, *file=NULL, *length=NULL, *returnvar=NULL;
- int len=0;
- static int deprecation_warning = 0;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "ReadFile require an argument!\n");
- return -1;
- }
-
- s = ast_strdupa(data);
-
- varname = strsep(&s, "=");
- file = strsep(&s, ",");
- length = s;
-
- if (deprecation_warning++ % 10 == 0)
- ast_log(LOG_WARNING, "ReadFile has been deprecated in favor of Set(%s=${FILE(%s,0,%s)})\n", varname, file, length);
-
- if (!varname || !file) {
- ast_log(LOG_ERROR, "No file or variable specified!\n");
- return -1;
- }
-
- if (length) {
- if ((sscanf(length, "%d", &len) != 1) || (len < 0)) {
- ast_log(LOG_WARNING, "%s is not a positive number, defaulting length to max\n", length);
- len = 0;
- }
- }
-
- if ((returnvar = ast_read_textfile(file))) {
- if (len > 0) {
- if (len < strlen(returnvar))
- returnvar[len]='\0';
- else
- ast_log(LOG_WARNING, "%s is longer than %d, and %d \n", file, len, (int)strlen(returnvar));
- }
- pbx_builtin_setvar_helper(chan, varname, returnvar);
- ast_free(returnvar);
- }
-
- return res;
-}
-
-
-static int unload_module(void)
-{
- return ast_unregister_application(app_readfile);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app_readfile, readfile_exec, readfile_synopsis, readfile_descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Stores output of file into a variable");
diff --git a/trunk/apps/app_record.c b/trunk/apps/app_record.c
deleted file mode 100644
index 7214e1f07..000000000
--- a/trunk/apps/app_record.c
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Matthew Fredrickson <creslin@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Trivial application to record a sound file
- *
- * \author Matthew Fredrickson <creslin@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-#include "asterisk/channel.h"
-#include "asterisk/dsp.h" /* use dsp routines for silence detection */
-
-
-static char *app = "Record";
-
-static char *synopsis = "Record to a file";
-
-static char *descrip =
-" Record(filename.format,silence[,maxduration][,options])\n\n"
-"Records from the channel into a given filename. If the file exists it will\n"
-"be overwritten.\n"
-"- 'format' is the format of the file type to be recorded (wav, gsm, etc).\n"
-"- 'silence' is the number of seconds of silence to allow before returning.\n"
-"- 'maxduration' is the maximum recording duration in seconds. If missing\n"
-"or 0 there is no maximum.\n"
-"- 'options' may contain any of the following letters:\n"
-" 'a' : append to existing recording rather than replacing\n"
-" 'n' : do not answer, but record anyway if line not yet answered\n"
-" 'q' : quiet (do not play a beep tone)\n"
-" 's' : skip recording if the line is not yet answered\n"
-" 't' : use alternate '*' terminator key (DTMF) instead of default '#'\n"
-" 'x' : ignore all terminator keys (DTMF) and keep recording until hangup\n"
-"\n"
-"If filename contains '%d', these characters will be replaced with a number\n"
-"incremented by one each time the file is recorded. A channel variable\n"
-"named RECORDED_FILE will also be set, which contains the final filemname.\n\n"
-"Use 'core show file formats' to see the available formats on your system\n\n"
-"User can press '#' to terminate the recording and continue to the next priority.\n\n"
-"If the user should hangup during a recording, all data will be lost and the\n"
-"application will teminate. \n";
-
-enum {
- OPTION_APPEND = (1 << 0),
- OPTION_NOANSWER = (1 << 1),
- OPTION_QUIET = (1 << 2),
- OPTION_SKIP = (1 << 3),
- OPTION_STAR_TERMINATE = (1 << 4),
- OPTION_IGNORE_TERMINATE = (1 << 5),
- FLAG_HAS_PERCENT = (1 << 6),
-};
-
-AST_APP_OPTIONS(app_opts,{
- AST_APP_OPTION('a', OPTION_APPEND),
- AST_APP_OPTION('n', OPTION_NOANSWER),
- AST_APP_OPTION('q', OPTION_QUIET),
- AST_APP_OPTION('s', OPTION_SKIP),
- AST_APP_OPTION('t', OPTION_STAR_TERMINATE),
- AST_APP_OPTION('x', OPTION_IGNORE_TERMINATE),
-});
-
-static int record_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- int count = 0;
- char *ext = NULL, *opts[0];
- char *parse, *dir, *file;
- int i = 0;
- char tmp[256];
-
- struct ast_filestream *s = NULL;
- struct ast_frame *f = NULL;
-
- struct ast_dsp *sildet = NULL; /* silence detector dsp */
- int totalsilence = 0;
- int dspsilence = 0;
- int silence = 0; /* amount of silence to allow */
- int gotsilence = 0; /* did we timeout for silence? */
- int maxduration = 0; /* max duration of recording in milliseconds */
- int gottimeout = 0; /* did we timeout for maxduration exceeded? */
- int terminator = '#';
- int rfmt = 0;
- int ioflags;
- int waitres;
- struct ast_silence_generator *silgen = NULL;
- struct ast_flags flags = { 0, };
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(filename);
- AST_APP_ARG(silence);
- AST_APP_ARG(maxduration);
- AST_APP_ARG(options);
- );
-
- /* The next few lines of code parse out the filename and header from the input string */
- if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
- ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
- if (args.argc == 4)
- ast_app_parse_options(app_opts, &flags, opts, args.options);
-
- if (!ast_strlen_zero(args.filename)) {
- if (strstr(args.filename, "%d"))
- ast_set_flag(&flags, FLAG_HAS_PERCENT);
- ext = strrchr(args.filename, '.'); /* to support filename with a . in the filename, not format */
- if (!ext)
- ext = strchr(args.filename, ':');
- if (ext) {
- *ext = '\0';
- ext++;
- }
- }
- if (!ext) {
- ast_log(LOG_WARNING, "No extension specified to filename!\n");
- return -1;
- }
- if (args.silence) {
- if ((sscanf(args.silence, "%d", &i) == 1) && (i > -1)) {
- silence = i * 1000;
- } else if (!ast_strlen_zero(args.silence)) {
- ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", args.silence);
- }
- }
-
- if (args.maxduration) {
- if ((sscanf(args.maxduration, "%d", &i) == 1) && (i > -1))
- /* Convert duration to milliseconds */
- maxduration = i * 1000;
- else if (!ast_strlen_zero(args.maxduration))
- ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", args.maxduration);
- }
-
- if (ast_test_flag(&flags, OPTION_STAR_TERMINATE))
- terminator = '*';
- if (ast_test_flag(&flags, OPTION_IGNORE_TERMINATE))
- terminator = '\0';
-
- /* done parsing */
-
- /* these are to allow the use of the %d in the config file for a wild card of sort to
- create a new file with the inputed name scheme */
- if (ast_test_flag(&flags, FLAG_HAS_PERCENT)) {
- AST_DECLARE_APP_ARGS(fname,
- AST_APP_ARG(piece)[100];
- );
- char *tmp2 = ast_strdupa(args.filename);
- char countstring[15];
- int i;
-
- /* Separate each piece out by the format specifier */
- AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%');
- do {
- int tmplen;
- /* First piece has no leading percent, so it's copied verbatim */
- ast_copy_string(tmp, fname.piece[0], sizeof(tmp));
- tmplen = strlen(tmp);
- for (i = 1; i < fname.argc; i++) {
- if (fname.piece[i][0] == 'd') {
- /* Substitute the count */
- snprintf(countstring, sizeof(countstring), "%d", count);
- ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen);
- tmplen += strlen(countstring);
- } else if (tmplen + 2 < sizeof(tmp)) {
- /* Unknown format specifier - just copy it verbatim */
- tmp[tmplen++] = '%';
- tmp[tmplen++] = fname.piece[i][0];
- }
- /* Copy the remaining portion of the piece */
- ast_copy_string(tmp + tmplen, &(fname.piece[i][1]), sizeof(tmp) - tmplen);
- }
- count++;
- } while (ast_fileexists(tmp, ext, chan->language) > 0);
- pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
- } else
- ast_copy_string(tmp, args.filename, sizeof(tmp));
- /* end of routine mentioned */
-
- if (chan->_state != AST_STATE_UP) {
- if (ast_test_flag(&flags, OPTION_SKIP)) {
- /* At the user's option, skip if the line is not up */
- return 0;
- } else if (!ast_test_flag(&flags, OPTION_NOANSWER)) {
- /* Otherwise answer unless we're supposed to record while on-hook */
- res = ast_answer(chan);
- }
- }
-
- if (res) {
- ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
- goto out;
- }
-
- if (!ast_test_flag(&flags, OPTION_QUIET)) {
- /* Some code to play a nice little beep to signify the start of the record operation */
- res = ast_streamfile(chan, "beep", chan->language);
- if (!res) {
- res = ast_waitstream(chan, "");
- } else {
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
- }
- ast_stopstream(chan);
- }
-
- /* The end of beep code. Now the recording starts */
-
- if (silence > 0) {
- rfmt = chan->readformat;
- res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
- if (res < 0) {
- ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
- return -1;
- }
- sildet = ast_dsp_new();
- if (!sildet) {
- ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
- return -1;
- }
- ast_dsp_set_threshold(sildet, 256);
- }
-
- /* Create the directory if it does not exist. */
- dir = ast_strdupa(tmp);
- if ((file = strrchr(dir, '/')))
- *file++ = '\0';
- ast_mkdir (dir, 0777);
-
- ioflags = ast_test_flag(&flags, OPTION_APPEND) ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
- s = ast_writefile(tmp, ext, NULL, ioflags, 0, AST_FILE_MODE);
-
- if (!s) {
- ast_log(LOG_WARNING, "Could not create file %s\n", args.filename);
- goto out;
- }
-
- if (ast_opt_transmit_silence)
- silgen = ast_channel_start_silence_generator(chan);
-
- /* Request a video update */
- ast_indicate(chan, AST_CONTROL_VIDUPDATE);
-
- if (maxduration <= 0)
- maxduration = -1;
-
- while ((waitres = ast_waitfor(chan, maxduration)) > -1) {
- if (maxduration > 0) {
- if (waitres == 0) {
- gottimeout = 1;
- break;
- }
- maxduration = waitres;
- }
-
- f = ast_read(chan);
- if (!f) {
- res = -1;
- break;
- }
- if (f->frametype == AST_FRAME_VOICE) {
- res = ast_writestream(s, f);
-
- if (res) {
- ast_log(LOG_WARNING, "Problem writing frame\n");
- ast_frfree(f);
- break;
- }
-
- if (silence > 0) {
- dspsilence = 0;
- ast_dsp_silence(sildet, f, &dspsilence);
- if (dspsilence) {
- totalsilence = dspsilence;
- } else {
- totalsilence = 0;
- }
- if (totalsilence > silence) {
- /* Ended happily with silence */
- ast_frfree(f);
- gotsilence = 1;
- break;
- }
- }
- } else if (f->frametype == AST_FRAME_VIDEO) {
- res = ast_writestream(s, f);
-
- if (res) {
- ast_log(LOG_WARNING, "Problem writing frame\n");
- ast_frfree(f);
- break;
- }
- } else if ((f->frametype == AST_FRAME_DTMF) &&
- (f->subclass == terminator)) {
- ast_frfree(f);
- break;
- }
- ast_frfree(f);
- }
- if (!f) {
- ast_debug(1, "Got hangup\n");
- res = -1;
- }
-
- if (gotsilence) {
- ast_stream_rewind(s, silence - 1000);
- ast_truncstream(s);
- } else if (!gottimeout) {
- /* Strip off the last 1/4 second of it */
- ast_stream_rewind(s, 250);
- ast_truncstream(s);
- }
- ast_closestream(s);
-
- if (silgen)
- ast_channel_stop_silence_generator(chan, silgen);
-
-out:
- if ((silence > 0) && rfmt) {
- res = ast_set_read_format(chan, rfmt);
- if (res)
- ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
- if (sildet)
- ast_dsp_free(sildet);
- }
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, record_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial Record Application");
diff --git a/trunk/apps/app_rpt.c b/trunk/apps/app_rpt.c
deleted file mode 100644
index 6eb0bfca8..000000000
--- a/trunk/apps/app_rpt.c
+++ /dev/null
@@ -1,7482 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2002-2005, Jim Dixon, WB6NIL
- *
- * Jim Dixon, WB6NIL <jim@lambdatel.com>
- * Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Radio Repeater / Remote Base program
- * version 0.48 06/13/06
- *
- * \author Jim Dixon, WB6NIL <jim@lambdatel.com>
- *
- * \note Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
- *
- * See http://www.zapatatelephony.org/app_rpt.html
- *
- *
- * Repeater / Remote Functions:
- * "Simple" Mode: * - autopatch access, # - autopatch hangup
- * Normal mode:
- * See the function list in rpt.conf (autopatchup, autopatchdn)
- * autopatchup can optionally take comma delimited setting=value pairs:
- *
- *
- * context=string : Override default context with "string"
- * dialtime=ms : Specify the max number of milliseconds between phone number digits (1000 milliseconds = 1 second)
- * farenddisconnect=1 : Automatically disconnect when called party hangs up
- * noct=1 : Don't send repeater courtesy tone during autopatch calls
- * quiet=1 : Don't send dial tone, or connect messages. Do not send patch down message when called party hangs up
- *
- *
- * Example: 123=autopatchup,dialtime=20000,noct=1,farenddisconnect=1
- *
- * To send an asterisk (*) while dialing or talking on phone,
- * use the autopatch acess code.
- *
- *
- * status cmds:
- *
- * 1 - Force ID
- * 2 - Give Time of Day
- * 3 - Give software Version
- *
- * cop (control operator) cmds:
- *
- * 1 - System warm boot
- * 2 - System enable
- * 3 - System disable
- * 4 - Test Tone On
- * 5 - Dump System Variables on Console (debug)
- * 6 - PTT (phone mode only)
- *
- * ilink cmds:
- *
- * 1 - Disconnect specified link
- * 2 - Connect specified link -- monitor only
- * 3 - Connect specified link -- tranceive
- * 4 - Enter command mode on specified link
- * 5 - System status
- * 6 - Disconnect all links
- *
- * remote cmds:
- *
- * 1 - Recall Memory MM (*000-*099) (Gets memory from rpt.conf)
- * 2 - Set VFO MMMMM*KKK*O (Mhz digits, Khz digits, Offset)
- * 3 - Set Rx PL Tone HHH*D*
- * 4 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1)
- * 5 - Link Status (long)
- * 6 - Set operating mode M (FM, USB, LSB, AM, etc)
- * 100 - RX PL off (Default)
- * 101 - RX PL On
- * 102 - TX PL Off (Default)
- * 103 - TX PL On
- * 104 - Low Power
- * 105 - Med Power
- * 106 - Hi Power
- * 107 - Bump Down 20 Hz
- * 108 - Bump Down 100 Hz
- * 109 - Bump Down 500 Hz
- * 110 - Bump Up 20 Hz
- * 111 - Bump Up 100 Hz
- * 112 - Bump Up 500 Hz
- * 113 - Scan Down Slow
- * 114 - Scan Down Medium
- * 115 - Scan Down Fast
- * 116 - Scan Up Slow
- * 117 - Scan Up Medium
- * 118 - Scan Up Fast
- * 119 - Transmit allowing auto-tune
- * 140 - Link Status (brief)
- *
- *
- *
- * 'duplex' modes: (defaults to duplex=2)
- *
- * 0 - Only remote links key Tx and no main repeat audio.
- * 1 - Everything other then main Rx keys Tx, no main repeat audio.
- * 2 - Normal mode
- * 3 - Normal except no main repeat audio.
- * 4 - Normal except no main repeat audio during autopatch only
- *
-*/
-
-/*** MODULEINFO
- <depend>zaptel</depend>
- <depend>tonezone</depend>
- <defaultenabled>no</defaultenabled>
- ***/
-
-/* Un-comment the following to include support for MDC-1200 digital tone
- signalling protocol (using KA6SQG's GPL'ed implementation) */
-/* #include "mdc_decode.c" */
-
-/* Un-comment the following to include support for notch filters in the
- rx audio stream (using Tony Fisher's mknotch (mkfilter) implementation) */
-/* #include "rpt_notch.c" */
-
-/* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
-
-#define MAXDTMF 32
-#define MAXMACRO 2048
-#define MAXGOSUB 2048
-#define MACROTIME 100
-#define GOSUBTIME 100
-#define MACROPTIME 500
-#define GOSUBPTIME 500
-#define DTMF_TIMEOUT 3
-
-#ifdef __RPT_NOTCH
-#define MAXFILTERS 10
-#endif
-
-#define DISC_TIME 10000 /* report disc after 10 seconds of no connect */
-#define MAX_RETRIES 5
-
-#define REDUNDANT_TX_TIME 2000
-
-#define RETRY_TIMER_MS 5000
-
-#define MAXPEERSTR 31
-#define MAXREMSTR 15
-
-#define DELIMCHR ','
-#define QUOTECHR 34
-
-#define NODES "nodes"
-#define MEMORY "memory"
-#define MACRO "macro"
-#define GOSUB "gosub"
-#define FUNCTIONS "functions"
-#define TELEMETRY "telemetry"
-#define MORSE "morse"
-#define FUNCCHAR '*'
-#define ENDCHAR '#'
-
-#define DEFAULT_IOBASE 0x378
-
-#define MAXCONNECTTIME 5000
-
-#define MAXNODESTR 300
-
-#define MAXPATCHCONTEXT 100
-
-#define ACTIONSIZE 32
-
-#define TELEPARAMSIZE 256
-
-#define REM_SCANTIME 100
-
-
-enum {REM_OFF, REM_MONITOR, REM_TX};
-
-enum {ID, PROC, TERM, COMPLETE, UNKEY, REMDISC, REMALREADY, REMNOTFOUND, REMGO,
- CONNECTED, CONNFAIL, STATUS, TIMEOUT, ID1, STATS_TIME,
- STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH,
- TAILMSG, MACRO_NOTFOUND, GOSUB_NOTFOUND, MACRO_BUSY, GOSUB_BUSY, LASTNODEKEY};
-
-enum {REM_SIMPLEX, REM_MINUS, REM_PLUS};
-
-enum {REM_LOWPWR, REM_MEDPWR, REM_HIPWR};
-
-enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE, DC_DOKEY};
-
-enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE};
-
-enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM};
-
-enum {REM_MODE_FM, REM_MODE_USB, REM_MODE_LSB, REM_MODE_AM};
-
-enum {HF_SCAN_OFF, HF_SCAN_DOWN_SLOW, HF_SCAN_DOWN_QUICK,
- HF_SCAN_DOWN_FAST, HF_SCAN_UP_SLOW, HF_SCAN_UP_QUICK, HF_SCAN_UP_FAST};
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <signal.h>
-#include <search.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <sys/time.h>
-#include <sys/file.h>
-#include <sys/ioctl.h>
-#include <sys/io.h>
-#include <math.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include "asterisk/utils.h"
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/callerid.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/features.h"
-#include "asterisk/cli.h"
-#include "asterisk/config.h"
-#include "asterisk/say.h"
-#include "asterisk/localtime.h"
-#include "asterisk/app.h"
-
-#include "asterisk/zapata.h"
-
-static char *app = "Rpt";
-
-static char *synopsis = "Radio Repeater/Remote Base Control System";
-
-static char *descrip =
-" Rpt(nodename[,options]): Radio Remote Link or Remote Base Link Endpoint Process.\n"
-"\n"
-" Not specifying an option puts it in normal endpoint mode (where source\n"
-" IP and nodename are verified).\n"
-"\n"
-" Options are as follows:\n"
-"\n"
-" X - Normal endpoint mode WITHOUT security check. Only specify\n"
-" this if you have checked security already (like with an IAX2\n"
-" user/password or something).\n"
-"\n"
-" Rannounce-string[,timeout[,timeout-destination]] - Amateur Radio\n"
-" Reverse Autopatch. Caller is put on hold, and announcement (as\n"
-" specified by the 'announce-string') is played on radio system.\n"
-" Users of radio system can access autopatch, dial specified\n"
-" code, and pick up call. Announce-string is list of names of\n"
-" recordings, or \"PARKED\" to substitute code for un-parking,\n"
-" or \"NODE\" to substitute node number.\n"
-"\n"
-" P - Phone Control mode. This allows a regular phone user to have\n"
-" full control and audio access to the radio system. For the\n"
-" user to have DTMF control, the 'phone_functions' parameter\n"
-" must be specified for the node in 'rpt.conf'. An additional\n"
-" function (cop,6) must be listed so that PTT control is available.\n"
-"\n"
-" D - Dumb Phone Control mode. This allows a regular phone user to\n"
-" have full control and audio access to the radio system. In this\n"
-" mode, the PTT is activated for the entire length of the call.\n"
-" For the user to have DTMF control (not generally recomended in\n"
-" this mode), the 'dphone_functions' parameter must be specified\n"
-" for the node in 'rpt.conf'. Otherwise no DTMF control will be\n"
-" available to the phone user.\n"
-"\n";
-
-static unsigned int vmajor = 0;
-static unsigned int vminor = 47;
-
-static int debug = 0; /* FIXME Set this >0 for extra debug output */
-static int nrpts = 0;
-
-char *discstr = "!!DISCONNECT!!";
-static char *remote_rig_ft897 = "ft897";
-static char *remote_rig_rbi = "rbi";
-
-#define MSWAIT 200
-#define HANGTIME 5000
-#define TOTIME 180000
-#define IDTIME 300000
-#define MAXRPTS 20
-#define MAX_STAT_LINKS 32
-#define POLITEID 30000
-#define FUNCTDELAY 1500
-
-static pthread_t rpt_master_thread;
-
-struct rpt;
-
-struct rpt_link
-{
- struct rpt_link *next;
- struct rpt_link *prev;
- char mode; /* 1 if in tx mode */
- char isremote;
- char phonemode;
- char name[MAXNODESTR]; /* identifier (routing) string */
- char lasttx;
- char lastrx;
- char connected;
- char hasconnected;
- char outbound;
- char disced;
- char killme;
- long elaptime;
- long disctime;
- long retrytimer;
- long retxtimer;
- int retries;
- int reconnects;
- long long connecttime;
- struct ast_channel *chan;
- struct ast_channel *pchan;
-} ;
-
-struct rpt_lstat
-{
- struct rpt_lstat *next;
- struct rpt_lstat *prev;
- char peer[MAXPEERSTR];
- char name[MAXNODESTR];
- char mode;
- char outbound;
- char reconnects;
- long long connecttime;
-} ;
-
-struct rpt_tele
-{
- struct rpt_tele *next;
- struct rpt_tele *prev;
- struct rpt *rpt;
- struct ast_channel *chan;
- int mode;
- struct rpt_link mylink;
- char param[TELEPARAMSIZE];
- pthread_t threadid;
-} ;
-
-struct function_table_tag
-{
- char action[ACTIONSIZE];
- int (*function)(struct rpt *myrpt, char *param, char *digitbuf,
- int command_source, struct rpt_link *mylink);
-} ;
-
-/* Used to store the morse code patterns */
-
-struct morse_bits
-{
- int len;
- int ddcomb;
-} ;
-
-struct telem_defaults
-{
- char name[20];
- char value[80];
-} ;
-
-
-static struct rpt
-{
- ast_mutex_t lock;
- struct ast_config *cfg;
- char reload;
-
- char *name;
- char *rxchanname;
- char *txchanname;
- char *remote;
-
- struct {
- char ourcontext[80];
- char ourcallerid[80];
- char acctcode[21];
- char ident[80];
- char tonezone[80];
- char simple;
- char functions[80];
- char link_functions[80];
- char phone_functions[80];
- char dphone_functions[80];
- char nodes[80];
- int hangtime;
- int totime;
- int idtime;
- int tailmessagetime;
- int tailsquashedtime;
- int duplex;
- int politeid;
- char *tailmsgbuf;
- AST_DECLARE_APP_ARGS(tailmsg,
- AST_APP_ARG(msgs)[100];
- );
- char memory[80];
- char macro[80];
- char gosub[80];
- char startupmacro[80];
- char startupgosub[80];
- int iobase;
- char funcchar;
- char endchar;
- char nobusyout;
- } p;
- struct rpt_link links;
- int unkeytocttimer;
- char keyed;
- char exttx;
- char localtx;
- char remoterx;
- char remotetx;
- char remoteon;
- char tounkeyed;
- char tonotify;
- char enable;
- char dtmfbuf[MAXDTMF];
- char macrobuf[MAXMACRO];
- char gosubbuf[MAXGOSUB];
- char rem_dtmfbuf[MAXDTMF];
- char lastdtmfcommand[MAXDTMF];
- char cmdnode[50];
- struct ast_channel *rxchannel, *txchannel;
- struct ast_channel *pchannel, *txpchannel, *remchannel;
- struct rpt_tele tele;
- struct timeval lasttv, curtv;
- pthread_t rpt_call_thread, rpt_thread;
- time_t dtmf_time, rem_dtmf_time, dtmf_time_rem;
- int tailtimer, totimer, idtimer, txconf, conf, callmode, cidx, scantimer, tmsgtimer, skedtimer;
- int mustid, tailid;
- int tailevent;
- int telemrefcount;
- int dtmfidx, rem_dtmfidx;
- int dailytxtime, dailykerchunks, totalkerchunks, dailykeyups, totalkeyups, timeouts;
- int totalexecdcommands, dailyexecdcommands;
- long retxtimer;
- long long totaltxtime;
- char mydtmf;
- char exten[AST_MAX_EXTENSION];
- char freq[MAXREMSTR], rxpl[MAXREMSTR], txpl[MAXREMSTR];
- char offset;
- char powerlevel;
- char txplon;
- char rxplon;
- char remmode;
- char tunerequest;
- char hfscanmode;
- int hfscanstatus;
- char lastlinknode[MAXNODESTR];
- char stopgen;
- char patchfarenddisconnect;
- char patchnoct;
- char patchquiet;
- char patchcontext[MAXPATCHCONTEXT];
- int patchdialtime;
- int macro_longest;
- int gosub_longest;
- int phone_longestfunc;
- int dphone_longestfunc;
- int link_longestfunc;
- int longestfunc;
- int longestnode;
- int threadrestarts;
- int tailmessagen;
- time_t disgorgetime;
- time_t lastthreadrestarttime;
- long macrotimer;
- long gosubtimer;
- char lastnodewhichkeyedusup[MAXNODESTR];
-#ifdef __RPT_NOTCH
- struct rptfilter
- {
- char desc[100];
- float x0;
- float x1;
- float x2;
- float y0;
- float y1;
- float y2;
- float gain;
- float const0;
- float const1;
- float const2;
- } filters[MAXFILTERS];
-#endif
-#ifdef _MDC_DECODE_H_
- mdc_decoder_t *mdc;
- unsigned short lastunit;
-#endif
-} rpt_vars[MAXRPTS];
-
-
-#ifdef APP_RPT_LOCK_DEBUG
-
-#warning COMPILING WITH LOCK-DEBUGGING ENABLED!!
-
-#define MAXLOCKTHREAD 100
-
-#define rpt_mutex_lock(x) _rpt_mutex_lock(x, myrpt, __LINE__)
-#define rpt_mutex_unlock(x) _rpt_mutex_unlock(x, myrpt, __LINE__)
-
-struct lockthread
-{
- pthread_t id;
- int lockcount;
- int lastlock;
- int lastunlock;
-} lockthreads[MAXLOCKTHREAD];
-
-
-struct by_lightning
-{
- int line;
- struct timeval tv;
- struct rpt *rpt;
- struct lockthread lockthread;
-} lock_ring[32];
-
-
-int lock_ring_index = 0;
-
-AST_MUTEX_DEFINE_STATIC(locklock);
-
-static struct lockthread *get_lockthread(pthread_t id)
-{
- int i;
-
- for (i = 0; i < MAXLOCKTHREAD; i++) {
- if (lockthreads[i].id == id)
- return(&lockthreads[i]);
- }
- return NULL;
-}
-
-static struct lockthread *put_lockthread(pthread_t id)
-{
- int i;
-
- for (i = 0; i < MAXLOCKTHREAD; i++) {
- if (lockthreads[i].id == id)
- return(&lockthreads[i]);
- }
- for (i = 0; i < MAXLOCKTHREAD; i++) {
- if (!lockthreads[i].id) {
- lockthreads[i].lockcount = 0;
- lockthreads[i].lastlock = 0;
- lockthreads[i].lastunlock = 0;
- lockthreads[i].id = id;
- return &lockthreads[i];
- }
- }
- return NULL;
-}
-
-
-static void rpt_mutex_spew(void)
-{
- struct by_lightning lock_ring_copy[32];
- int lock_ring_index_copy;
- int i, j;
- long long diff;
- char a[100] = "";
- struct ast_tm tm;
- struct timeval lasttv;
-
- ast_mutex_lock(&locklock);
- memcpy(&lock_ring_copy, &lock_ring, sizeof(lock_ring_copy));
- lock_ring_index_copy = lock_ring_index;
- ast_mutex_unlock(&locklock);
-
- lasttv.tv_sec = lasttv.tv_usec = 0;
- for (i = 0; i < 32; i++) {
- j = (i + lock_ring_index_copy) % 32;
- ast_strftime(a, sizeof(a) - 1, "%m/%d/%Y %H:%M:%S",
- ast_localtime(&lock_ring_copy[j].tv, &tm, NULL));
- diff = 0;
- if (lasttv.tv_sec) {
- diff = (lock_ring_copy[j].tv.tv_sec - lasttv.tv_sec) * 1000000;
- diff += (lock_ring_copy[j].tv.tv_usec - lasttv.tv_usec);
- }
- lasttv.tv_sec = lock_ring_copy[j].tv.tv_sec;
- lasttv.tv_usec = lock_ring_copy[j].tv.tv_usec;
- if (!lock_ring_copy[j].tv.tv_sec)
- continue;
- if (lock_ring_copy[j].line < 0) {
- ast_log(LOG_NOTICE, "LOCKDEBUG [#%d] UNLOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n",
- i - 31, -lock_ring_copy[j].line, lock_ring_copy[j].rpt->name,
- (int) lock_ring_copy[j].lockthread.id, diff, a, (int)lock_ring_copy[j].tv.tv_usec);
- } else {
- ast_log(LOG_NOTICE, "LOCKDEBUG [#%d] LOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n",
- i - 31, lock_ring_copy[j].line, lock_ring_copy[j].rpt->name,
- (int) lock_ring_copy[j].lockthread.id, diff, a, (int)lock_ring_copy[j].tv.tv_usec);
- }
- }
-}
-
-
-static void _rpt_mutex_lock(ast_mutex_t *lockp, struct rpt *myrpt, int line)
-{
- struct lockthread *t;
- pthread_t id;
-
- id = pthread_self();
- ast_mutex_lock(&locklock);
- t = put_lockthread(id);
- if (!t) {
- ast_mutex_unlock(&locklock);
- return;
- }
- if (t->lockcount) {
- int lastline = t->lastlock;
- ast_mutex_unlock(&locklock);
- ast_log(LOG_NOTICE, "rpt_mutex_lock: Double lock request line %d node %s pid %x, last lock was line %d\n",
- line, myrpt->name, (int) t->id, lastline);
- rpt_mutex_spew();
- return;
- }
- t->lastlock = line;
- t->lockcount = 1;
- gettimeofday(&lock_ring[lock_ring_index].tv, NULL);
- lock_ring[lock_ring_index].rpt = myrpt;
- memcpy(&lock_ring[lock_ring_index].lockthread, t, sizeof(struct lockthread));
- lock_ring[lock_ring_index++].line = line;
- if (lock_ring_index == 32)
- lock_ring_index = 0;
- ast_mutex_unlock(&locklock);
- ast_mutex_lock(lockp);
-}
-
-
-static void _rpt_mutex_unlock(ast_mutex_t *lockp, struct rpt *myrpt, int line)
-{
- struct lockthread *t;
- pthread_t id;
-
- id = pthread_self();
- ast_mutex_lock(&locklock);
- t = put_lockthread(id);
- if (!t) {
- ast_mutex_unlock(&locklock);
- return;
- }
- if (!t->lockcount) {
- int lastline = t->lastunlock;
- ast_mutex_unlock(&locklock);
- ast_log(LOG_NOTICE, "rpt_mutex_lock: Double un-lock request line %d node %s pid %x, last un-lock was line %d\n",
- line, myrpt->name, (int) t->id, lastline);
- rpt_mutex_spew();
- return;
- }
- t->lastunlock = line;
- t->lockcount = 0;
- gettimeofday(&lock_ring[lock_ring_index].tv, NULL);
- lock_ring[lock_ring_index].rpt = myrpt;
- memcpy(&lock_ring[lock_ring_index].lockthread, t, sizeof(struct lockthread));
- lock_ring[lock_ring_index++].line = -line;
- if (lock_ring_index == 32)
- lock_ring_index = 0;
- ast_mutex_unlock(&locklock);
- ast_mutex_unlock(lockp);
-}
-
-#else /* APP_RPT_LOCK_DEBUG */
-
-#define rpt_mutex_lock(x) ast_mutex_lock(x)
-#define rpt_mutex_unlock(x) ast_mutex_unlock(x)
-
-#endif /* APP_RPT_LOCK_DEBUG */
-
-/*
-* CLI extensions
-*/
-
-/* Debug mode */
-static char *handle_cli_rpt_debug_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
-static char *handle_cli_rpt_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
-static char *handle_cli_rpt_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
-static char *handle_cli_rpt_lstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
-static char *handle_cli_rpt_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
-static char *handle_cli_rpt_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
-
-static struct ast_cli_entry cli_rpt[] = {
- AST_CLI_DEFINE(handle_cli_rpt_debug_level, "Enable app_rpt debuggin"),
- AST_CLI_DEFINE(handle_cli_rpt_dump, "Dump app_rpt structs for debugging"),
- AST_CLI_DEFINE(handle_cli_rpt_stats, "Dump node statistics"),
- AST_CLI_DEFINE(handle_cli_rpt_lstats, "Dump link statistics"),
- AST_CLI_DEFINE(handle_cli_rpt_reload, "Reload app_rpt config"),
- AST_CLI_DEFINE(handle_cli_rpt_restart, "Restart app_rpt")
-};
-
-/*
-* Telemetry defaults
-*/
-
-
-static struct telem_defaults tele_defs[] = {
- {"ct1", "|t(350,0,100,3072)(500,0,100,3072)(660,0,100,3072)"},
- {"ct2", "|t(660,880,150,3072)"},
- {"ct3", "|t(440,0,150,3072)"},
- {"ct4", "|t(550,0,150,3072)"},
- {"ct5", "|t(660,0,150,3072)"},
- {"ct6", "|t(880,0,150,3072)"},
- {"ct7", "|t(660,440,150,3072)"},
- {"ct8", "|t(700,1100,150,3072)"},
- {"remotemon", "|t(1600,0,75,2048)"},
- {"remotetx", "|t(2000,0,75,2048)(0,0,75,0)(1600,0,75,2048)"},
- {"cmdmode", "|t(900,904,200,2048)"},
- {"functcomplete", "|t(1000,0,100,2048)(0,0,100,0)(1000,0,100,2048)"}
-} ;
-
-/*
-* Forward decl's - these suppress compiler warnings when funcs coded further down the file than their invocation
-*/
-
-static int setrbi(struct rpt *myrpt);
-
-
-
-/*
-* Define function protos for function table here
-*/
-
-static int function_ilink(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
-static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
-static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
-static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
-static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
-static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
-static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
-static int function_gosub(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
-/*
-* Function table
-*/
-
-static struct function_table_tag function_table[] = {
- {"cop", function_cop},
- {"autopatchup", function_autopatchup},
- {"autopatchdn", function_autopatchdn},
- {"ilink", function_ilink},
- {"status", function_status},
- {"remote", function_remote},
- {"macro", function_macro},
- {"gosub", function_gosub},
-} ;
-
-/*
-* Match a keyword in a list, and return index of string plus 1 if there was a match,
-* else return 0. If param is passed in non-null, then it will be set to the first character past the match
-*/
-
-static int matchkeyword(char *string, char **param, char *keywords[])
-{
- int i, ls;
- for (i = 0; keywords[i]; i++) {
- ls = strlen(keywords[i]);
- if (!ls) {
- *param = NULL;
- return 0;
- }
- if (!strncmp(string, keywords[i], ls)) {
- if (param)
- *param = string + ls;
- return i + 1;
- }
- }
- param = NULL;
- return 0;
-}
-
-/*
-* Skip characters in string which are in charlist, and return a pointer to the
-* first non-matching character
-*/
-
-static char *skipchars(char *string, char *charlist)
-{
- int i;
- while (*string) {
- for (i = 0; charlist[i] ; i++) {
- if (*string == charlist[i]) {
- string++;
- break;
- }
- }
- if (!charlist[i])
- return string;
- }
- return string;
-}
-
-
-
-static int myatoi(const char *str)
-{
- int ret;
-
- if (str == NULL)
- return -1;
- /* leave this %i alone, non-base-10 input is useful here */
- if (sscanf(str, "%i", &ret) != 1)
- return -1;
- return ret;
-}
-
-
-#ifdef __RPT_NOTCH
-
-/* rpt filter routine */
-static void rpt_filter(struct rpt *myrpt, volatile short *buf, int len)
-{
- int i, j;
- struct rptfilter *f;
-
- for (i = 0; i < len; i++) {
- for (j = 0; j < MAXFILTERS; j++) {
- f = &myrpt->filters[j];
- if (!*f->desc)
- continue;
- f->x0 = f->x1; f->x1 = f->x2;
- f->x2 = ((float)buf[i]) / f->gain;
- f->y0 = f->y1; f->y1 = f->y2;
- f->y2 = (f->x0 + f->x2) + f->const0 * f->x1
- + (f->const1 * f->y0) + (f->const2 * f->y1);
- buf[i] = (short)f->y2;
- }
- }
-}
-
-#endif
-
-/* Retrieve an int from a config file */
-static int retrieve_astcfgint(struct rpt *myrpt, const char *category, const char *name, int min, int max, int defl)
-{
- const char *var = ast_variable_retrieve(myrpt->cfg, category, name);
- int ret;
-
- if (var) {
- ret = myatoi(var);
- if (ret < min)
- ret = min;
- else if (ret > max)
- ret = max;
- } else
- ret = defl;
- return ret;
-}
-
-
-static void load_rpt_vars(int n, int init)
-{
- int j;
- struct ast_variable *vp, *var;
- struct ast_config *cfg;
- struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
-#ifdef __RPT_NOTCH
- AST_DECLARE_APP_ARGS(strs,
- AST_APP_ARG(str)[100];
- );
-#endif
-
- ast_verb(3, "%s config for repeater %s\n",
- (init) ? "Loading initial" : "Re-Loading", rpt_vars[n].name);
- ast_mutex_lock(&rpt_vars[n].lock);
- if (rpt_vars[n].cfg)
- ast_config_destroy(rpt_vars[n].cfg);
- cfg = ast_config_load("rpt.conf", config_flags);
- if (!cfg) {
- ast_mutex_unlock(&rpt_vars[n].lock);
- ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n");
- pthread_exit(NULL);
- }
- rpt_vars[n].cfg = cfg;
- /* Free previously malloc'ed buffer */
- if (!init && rpt_vars[n].p.tailmsgbuf)
- ast_free(rpt_vars[n].p.tailmsgbuf);
- memset(&rpt_vars[n].p, 0, sizeof(rpt_vars[n].p));
- if (init) {
- char *cp;
- int savearea = (char *)&rpt_vars[n].p - (char *)&rpt_vars[n];
-
- cp = (char *) &rpt_vars[n].p;
- memset(cp + sizeof(rpt_vars[n].p), 0,
- sizeof(rpt_vars[n]) - (sizeof(rpt_vars[n].p) + savearea));
- rpt_vars[n].tele.next = &rpt_vars[n].tele;
- rpt_vars[n].tele.prev = &rpt_vars[n].tele;
- rpt_vars[n].rpt_thread = AST_PTHREADT_NULL;
- rpt_vars[n].tailmessagen = 0;
- }
-#ifdef __RPT_NOTCH
- /* zot out filters stuff */
- memset(&rpt_vars[n].filters, 0, sizeof(rpt_vars[n].filters));
-#endif
-
- /* Defaults */
- ast_copy_string(rpt_vars[n].p.ourcontext, rpt_vars[n].name, sizeof(rpt_vars[n].p.ourcontext));
- rpt_vars[n].p.hangtime = HANGTIME;
- rpt_vars[n].p.totime = TOTIME;
- rpt_vars[n].p.duplex = 2;
- rpt_vars[n].p.idtime = IDTIME;
- rpt_vars[n].p.politeid = POLITEID;
- ast_copy_string(rpt_vars[n].p.memory, MEMORY, sizeof(rpt_vars[n].p.memory));
- ast_copy_string(rpt_vars[n].p.macro, MACRO, sizeof(rpt_vars[n].p.macro));
- ast_copy_string(rpt_vars[n].p.gosub, GOSUB, sizeof(rpt_vars[n].p.gosub));
- rpt_vars[n].p.iobase = DEFAULT_IOBASE;
- ast_copy_string(rpt_vars[n].p.functions, FUNCTIONS, sizeof(rpt_vars[n].p.functions));
- rpt_vars[n].p.simple = 1;
- rpt_vars[n].p.funcchar = FUNCCHAR;
- rpt_vars[n].p.endchar = ENDCHAR;
- ast_copy_string(rpt_vars[n].p.nodes, NODES, sizeof(rpt_vars[n].p.nodes));
-
- for (var = ast_variable_browse(cfg, rpt_vars[n].name); var; var = var->next) {
- if (!strcmp(var->name, "context")) {
- ast_copy_string(rpt_vars[n].p.ourcontext, var->value, sizeof(rpt_vars[n].p.ourcontext));
- } else if (!strcmp(var->name, "callerid")) {
- ast_copy_string(rpt_vars[n].p.ourcallerid, var->value, sizeof(rpt_vars[n].p.ourcallerid));
- } else if (!strcmp(var->name, "accountcode")) {
- ast_copy_string(rpt_vars[n].p.acctcode, var->value, sizeof(rpt_vars[n].p.acctcode));
- } else if (!strcmp(var->name, "idrecording")) {
- ast_copy_string(rpt_vars[n].p.ident, var->value, sizeof(rpt_vars[n].p.ident));
- } else if (!strcmp(var->name, "hangtime")) {
- rpt_vars[n].p.hangtime = atoi(var->value);
- } else if (!strcmp(var->name, "totime")) {
- rpt_vars[n].p.totime = atoi(var->value);
- } else if (!strcmp(var->name, "tailmessagetime")) {
- rpt_vars[n].p.tailmessagetime = atoi(var->value);
- if (rpt_vars[n].p.tailmessagetime < 0)
- rpt_vars[n].p.tailmessagetime = 0;
- else if (rpt_vars[n].p.tailmessagetime > 2400000)
- rpt_vars[n].p.tailmessagetime = 2400000;
- } else if (!strcmp(var->name, "tailsquashedtime")) {
- rpt_vars[n].p.tailsquashedtime = atoi(var->value);
- if (rpt_vars[n].p.tailsquashedtime < 0)
- rpt_vars[n].p.tailsquashedtime = 0;
- else if (rpt_vars[n].p.tailsquashedtime > 2400000)
- rpt_vars[n].p.tailsquashedtime = 2400000;
- } else if (!strcmp(var->name, "duplex")) {
- rpt_vars[n].p.duplex = atoi(var->value);
- if (rpt_vars[n].p.duplex < 0)
- rpt_vars[n].p.duplex = 0;
- else if (rpt_vars[n].p.duplex > 4)
- rpt_vars[n].p.duplex = 4;
- } else if (!strcmp(var->name, "idtime")) {
- rpt_vars[n].p.idtime = atoi(var->value);
- if (rpt_vars[n].p.idtime < 60000)
- rpt_vars[n].p.idtime = 60000;
- else if (rpt_vars[n].p.idtime > 2400000)
- rpt_vars[n].p.idtime = 2400000;
- } else if (!strcmp(var->name, "politeid")) {
- rpt_vars[n].p.politeid = atoi(var->value);
- if (rpt_vars[n].p.politeid < 30000)
- rpt_vars[n].p.politeid = 30000;
- else if (rpt_vars[n].p.politeid > 300000)
- rpt_vars[n].p.politeid = 300000;
- } else if (!strcmp(var->name, "tonezone")) {
- ast_copy_string(rpt_vars[n].p.tonezone, var->value, sizeof(rpt_vars[n].p.tonezone));
- } else if (!strcmp(var->name, "tailmessagelist")) {
- rpt_vars[n].p.tailmsgbuf = ast_strdup(var->value);
- AST_STANDARD_APP_ARGS(rpt_vars[n].p.tailmsg, rpt_vars[n].p.tailmsgbuf);
- } else if (!strcmp(var->name, "memory")) {
- ast_copy_string(rpt_vars[n].p.memory, var->value, sizeof(rpt_vars[n].p.memory));
- } else if (!strcmp(var->name, "macro")) {
- ast_copy_string(rpt_vars[n].p.macro, var->value, sizeof(rpt_vars[n].p.macro));
- } else if (!strcmp(var->name, "gosub")) {
- ast_copy_string(rpt_vars[n].p.gosub, var->value, sizeof(rpt_vars[n].p.gosub));
- } else if (!strcmp(var->name, "startup_macro")) {
- ast_copy_string(rpt_vars[n].p.startupmacro, var->value, sizeof(rpt_vars[n].p.startupmacro));
- } else if (!strcmp(var->name, "startup_gosub")) {
- ast_copy_string(rpt_vars[n].p.startupgosub, var->value, sizeof(rpt_vars[n].p.startupgosub));
- } else if (!strcmp(var->name, "iobase")) {
- /* do not use atoi() here, we need to be able to have
- the input specified in hex or decimal so we use
- sscanf with a %i */
- if (sscanf(var->value, "%i", &rpt_vars[n].p.iobase) != 1)
- rpt_vars[n].p.iobase = DEFAULT_IOBASE;
- } else if (!strcmp(var->name, "functions")) {
- rpt_vars[n].p.simple = 0;
- ast_copy_string(rpt_vars[n].p.functions, var->value, sizeof(rpt_vars[n].p.functions));
- } else if (!strcmp(var->name, "link_functions")) {
- ast_copy_string(rpt_vars[n].p.link_functions, var->value, sizeof(rpt_vars[n].p.link_functions));
- } else if (!strcmp(var->name, "phone_functions")) {
- ast_copy_string(rpt_vars[n].p.phone_functions, var->value, sizeof(rpt_vars[n].p.phone_functions));
- } else if (!strcmp(var->name, "dphone_functions")) {
- ast_copy_string(rpt_vars[n].p.dphone_functions, var->value, sizeof(rpt_vars[n].p.dphone_functions));
- } else if (!strcmp(var->name, "funcchar")) {
- rpt_vars[n].p.funcchar = *var->value;
- } else if (!strcmp(var->name, "endchar")) {
- rpt_vars[n].p.endchar = *var->value;
- } else if (!strcmp(var->name, "nobusyout")) {
- rpt_vars[n].p.nobusyout = ast_true(var->value);
- } else if (!strcmp(var->name, "nodes")) {
- ast_copy_string(rpt_vars[n].p.nodes, var->value, sizeof(rpt_vars[n].p.nodes));
-#ifdef __RPT_NOTCH
- } else if (!strcmp(var->name, "rxnotch")) {
- char *tmp = ast_strdupa(val);
- AST_STANDARD_APP_ARGS(strs, tmp);
- strs.argc &= ~1; /* force an even number, rounded down */
- if (strs.argc >= 2) {
- for (j = 0; j < strs.argc; j += 2) {
- rpt_mknotch(atof(strs.str[j]), atof(strs.str[j + 1]),
- &rpt_vars[n].filters[j >> 1].gain,
- &rpt_vars[n].filters[j >> 1].const0,
- &rpt_vars[n].filters[j >> 1].const1,
- &rpt_vars[n].filters[j >> 1].const2);
- sprintf(rpt_vars[n].filters[j >> 1].desc, "%s Hz, BW = %s",
- strs.str[j], strs.str[j + 1]);
- }
- }
-#endif
- }
- }
-
- /* If these aren't specified, copy them from the functions property. */
- if (ast_strlen_zero(rpt_vars[n].p.link_functions))
- ast_copy_string(rpt_vars[n].p.link_functions, rpt_vars[n].p.functions, sizeof(rpt_vars[n].p.link_functions));
-
- rpt_vars[n].longestnode = 0;
- for (vp = ast_variable_browse(cfg, rpt_vars[n].p.nodes); vp; vp = vp->next) {
- if ((j = strlen(vp->name)) > rpt_vars[n].longestnode)
- rpt_vars[n].longestnode = j;
- }
-
- /*
- * For this repeater, Determine the length of the longest function
- */
- rpt_vars[n].longestfunc = 0;
- for (vp = ast_variable_browse(cfg, rpt_vars[n].p.functions); vp; vp = vp->next) {
- if ((j = strlen(vp->name)) > rpt_vars[n].longestfunc)
- rpt_vars[n].longestfunc = j;
- }
-
- rpt_vars[n].link_longestfunc = 0;
- for (vp = ast_variable_browse(cfg, rpt_vars[n].p.link_functions); vp; vp = vp->next) {
- if ((j = strlen(vp->name)) > rpt_vars[n].link_longestfunc)
- rpt_vars[n].link_longestfunc = j;
- }
-
- rpt_vars[n].phone_longestfunc = 0;
- for (vp = ast_variable_browse(cfg, rpt_vars[n].p.phone_functions); vp; vp = vp->next) {
- if ((j = strlen(vp->name)) > rpt_vars[n].phone_longestfunc)
- rpt_vars[n].phone_longestfunc = j;
- }
-
- rpt_vars[n].dphone_longestfunc = 0;
- for (vp = ast_variable_browse(cfg, rpt_vars[n].p.dphone_functions); vp; vp = vp->next) {
- if ((j = strlen(vp->name)) > rpt_vars[n].dphone_longestfunc)
- rpt_vars[n].dphone_longestfunc = j;
- }
-
- rpt_vars[n].macro_longest = 1;
- for (vp = ast_variable_browse(cfg, rpt_vars[n].p.macro); vp; vp = vp->next) {
- if ((j = strlen(vp->name)) > rpt_vars[n].macro_longest)
- rpt_vars[n].macro_longest = j;
- }
-
- rpt_vars[n].gosub_longest = 1;
- for (vp = ast_variable_browse(cfg, rpt_vars[n].p.gosub); vp; vp = vp->next) {
- if ((j = strlen(vp->name)) > rpt_vars[n].gosub_longest)
- rpt_vars[n].gosub_longest = j;
- }
- ast_mutex_unlock(&rpt_vars[n].lock);
-}
-
-/*
-* Enable or disable debug output at a given level at the console
-*/
-static char *handle_cli_rpt_debug_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- int newlevel;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "rpt debug level";
- e->usage =
- "Usage: rpt debug level {0-7}\n"
- " Enables debug messages in app_rpt\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 4)
- return CLI_SHOWUSAGE;
- newlevel = myatoi(a->argv[3]);
- if ((newlevel < 0) || (newlevel > 7))
- return CLI_SHOWUSAGE;
- if (newlevel)
- ast_cli(a->fd, "app_rpt Debugging enabled, previous level: %d, new level: %d\n", debug, newlevel);
- else
- ast_cli(a->fd, "app_rpt Debugging disabled\n");
-
- debug = newlevel;
-
- return CLI_SUCCESS;
-}
-
-/*
-* Dump rpt struct debugging onto console
-*/
-static char *handle_cli_rpt_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- int i;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "rpt dump";
- e->usage =
- "Usage: rpt dump <nodename>\n"
- " Dumps struct debug info to log\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
-
- for (i = 0; i < nrpts; i++) {
- if (!strcmp(a->argv[2], rpt_vars[i].name)) {
- rpt_vars[i].disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */
- ast_cli(a->fd, "app_rpt struct dump requested for node %s\n", a->argv[2]);
- return CLI_SUCCESS;
- }
- }
- return CLI_FAILURE;
-}
-
-/*
-* Dump statistics onto console
-*/
-static char *handle_cli_rpt_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- int i, j;
- int dailytxtime, dailykerchunks;
- int totalkerchunks, dailykeyups, totalkeyups, timeouts;
- int totalexecdcommands, dailyexecdcommands, hours, minutes, seconds;
- long long totaltxtime;
- struct rpt_link *l;
- char *listoflinks[MAX_STAT_LINKS];
- char *lastnodewhichkeyedusup, *lastdtmfcommand;
- char *tot_state, *ider_state, *patch_state;
- char *reverse_patch_state, *enable_state, *input_signal, *called_number;
- struct rpt *myrpt;
-
- static char *not_applicable = "N/A";
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "rpt stats";
- e->usage =
- "Usage: rpt stats <nodename>\n"
- " Dumps node statistics to console\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
-
- for (i = 0 ; i <= MAX_STAT_LINKS; i++)
- listoflinks[i] = NULL;
-
- tot_state = ider_state =
- patch_state = reverse_patch_state =
- input_signal = called_number =
- lastdtmfcommand = not_applicable;
-
- for (i = 0; i < nrpts; i++) {
- if (!strcmp(a->argv[2], rpt_vars[i].name)) {
- /* Make a copy of all stat variables while locked */
- myrpt = &rpt_vars[i];
- rpt_mutex_lock(&myrpt->lock); /* LOCK */
-
- dailytxtime = myrpt->dailytxtime;
- totaltxtime = myrpt->totaltxtime;
- dailykeyups = myrpt->dailykeyups;
- totalkeyups = myrpt->totalkeyups;
- dailykerchunks = myrpt->dailykerchunks;
- totalkerchunks = myrpt->totalkerchunks;
- dailyexecdcommands = myrpt->dailyexecdcommands;
- totalexecdcommands = myrpt->totalexecdcommands;
- timeouts = myrpt->timeouts;
-
- /* Traverse the list of connected nodes */
- reverse_patch_state = "DOWN";
- j = 0;
- l = myrpt->links.next;
- while (l != &myrpt->links) {
- if (l->name[0] == '0') { /* Skip '0' nodes */
- reverse_patch_state = "UP";
- l = l->next;
- continue;
- }
- listoflinks[j] = ast_strdupa(l->name);
- if (listoflinks[j])
- j++;
- l = l->next;
- }
-
- lastnodewhichkeyedusup = ast_strdupa(myrpt->lastnodewhichkeyedusup);
- if ((!lastnodewhichkeyedusup) || (ast_strlen_zero(lastnodewhichkeyedusup)))
- lastnodewhichkeyedusup = not_applicable;
-
- if (myrpt->keyed)
- input_signal = "YES";
- else
- input_signal = "NO";
-
- if (myrpt->enable)
- enable_state = "YES";
- else
- enable_state = "NO";
-
- if (!myrpt->totimer)
- tot_state = "TIMED OUT!";
- else if (myrpt->totimer != myrpt->p.totime)
- tot_state = "ARMED";
- else
- tot_state = "RESET";
-
- if (myrpt->tailid)
- ider_state = "QUEUED IN TAIL";
- else if (myrpt->mustid)
- ider_state = "QUEUED FOR CLEANUP";
- else
- ider_state = "CLEAN";
-
- switch (myrpt->callmode) {
- case 1:
- patch_state = "DIALING";
- break;
- case 2:
- patch_state = "CONNECTING";
- break;
- case 3:
- patch_state = "UP";
- break;
- case 4:
- patch_state = "CALL FAILED";
- break;
- default:
- patch_state = "DOWN";
- }
-
- if (!ast_strlen_zero(myrpt->exten))
- called_number = ast_strdupa(myrpt->exten);
-
- if (!ast_strlen_zero(myrpt->lastdtmfcommand))
- lastdtmfcommand = ast_strdupa(myrpt->lastdtmfcommand);
-
- rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
-
- ast_cli(a->fd, "************************ NODE %s STATISTICS *************************\n\n", myrpt->name);
- ast_cli(a->fd, "Signal on input..................................: %s\n", input_signal);
- ast_cli(a->fd, "Transmitter enabled..............................: %s\n", enable_state);
- ast_cli(a->fd, "Time out timer state.............................: %s\n", tot_state);
- ast_cli(a->fd, "Time outs since system initialization............: %d\n", timeouts);
- ast_cli(a->fd, "Identifier state.................................: %s\n", ider_state);
- ast_cli(a->fd, "Kerchunks today..................................: %d\n", dailykerchunks);
- ast_cli(a->fd, "Kerchunks since system initialization............: %d\n", totalkerchunks);
- ast_cli(a->fd, "Keyups today.....................................: %d\n", dailykeyups);
- ast_cli(a->fd, "Keyups since system initialization...............: %d\n", totalkeyups);
- ast_cli(a->fd, "DTMF commands today..............................: %d\n", dailyexecdcommands);
- ast_cli(a->fd, "DTMF commands since system initialization........: %d\n", totalexecdcommands);
- ast_cli(a->fd, "Last DTMF command executed.......................: %s\n", lastdtmfcommand);
-
- hours = dailytxtime / 3600000;
- dailytxtime %= 3600000;
- minutes = dailytxtime / 60000;
- dailytxtime %= 60000;
- seconds = dailytxtime / 1000;
- dailytxtime %= 1000;
-
- ast_cli(a->fd, "TX time today ...................................: %02d:%02d:%02d.%d\n",
- hours, minutes, seconds, dailytxtime);
-
- hours = (int) totaltxtime / 3600000;
- totaltxtime %= 3600000;
- minutes = (int) totaltxtime / 60000;
- totaltxtime %= 60000;
- seconds = (int) totaltxtime / 1000;
- totaltxtime %= 1000;
-
- ast_cli(a->fd, "TX time since system initialization..............: %02d:%02d:%02d.%d\n",
- hours, minutes, seconds, (int) totaltxtime);
- ast_cli(a->fd, "Nodes currently connected to us..................: ");
- for (j = 0;; j++) {
- if (!listoflinks[j]) {
- if (!j) {
- ast_cli(a->fd, "<NONE>");
- }
- break;
- }
- ast_cli(a->fd, "%s", listoflinks[j]);
- if (j % 4 == 3) {
- ast_cli(a->fd, "\n");
- ast_cli(a->fd, " : ");
- } else {
- if (listoflinks[j + 1])
- ast_cli(a->fd, ", ");
- }
- }
- ast_cli(a->fd, "\n");
-
- ast_cli(a->fd, "Last node which transmitted to us................: %s\n", lastnodewhichkeyedusup);
- ast_cli(a->fd, "Autopatch state..................................: %s\n", patch_state);
- ast_cli(a->fd, "Autopatch called number..........................: %s\n", called_number);
- ast_cli(a->fd, "Reverse patch/IAXRPT connected...................: %s\n\n", reverse_patch_state);
-
- return CLI_SUCCESS;
- }
- }
- return CLI_FAILURE;
-}
-
-/*
-* Link stats function
-*/
-static char *handle_cli_rpt_lstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- int i, j;
- struct rpt *myrpt;
- struct rpt_link *l;
- struct rpt_lstat *s, *t;
- struct rpt_lstat s_head;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "rpt lstats";
- e->usage =
- "Usage: rpt lstats <nodename>\n"
- " Dumps link statistics to console\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
-
- s = NULL;
- s_head.next = &s_head;
- s_head.prev = &s_head;
-
- for (i = 0; i < nrpts; i++) {
- if (!strcmp(a->argv[2], rpt_vars[i].name)) {
- /* Make a copy of all stat variables while locked */
- myrpt = &rpt_vars[i];
- rpt_mutex_lock(&myrpt->lock); /* LOCK */
- /* Traverse the list of connected nodes */
- j = 0;
- l = myrpt->links.next;
- while (l != &myrpt->links) {
- if (l->name[0] == '0') { /* Skip '0' nodes */
- l = l->next;
- continue;
- }
- if ((s = ast_calloc(1, sizeof(*s))) == NULL) {
- ast_log(LOG_ERROR, "Malloc failed in rpt_do_lstats\n");
- rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
- return CLI_FAILURE;
- }
- ast_copy_string(s->name, l->name, MAXREMSTR);
- pbx_substitute_variables_helper(l->chan, "${IAXPEER(CURRENTCHANNEL)}", s->peer, MAXPEERSTR - 1);
- s->mode = l->mode;
- s->outbound = l->outbound;
- s->reconnects = l->reconnects;
- s->connecttime = l->connecttime;
- insque((struct qelem *) s, (struct qelem *) s_head.next);
- l = l->next;
- }
- rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
- ast_cli(a->fd, "NODE PEER RECONNECTS DIRECTION CONNECT TIME\n");
- ast_cli(a->fd, "---- ---- ---------- --------- ------------\n");
-
- for (s = s_head.next; s != &s_head; s = s->next) {
- int hours, minutes, seconds;
- long long connecttime = s->connecttime;
- char conntime[31];
- hours = (int) connecttime/3600000;
- connecttime %= 3600000;
- minutes = (int) connecttime/60000;
- connecttime %= 60000;
- seconds = (int) connecttime/1000;
- connecttime %= 1000;
- snprintf(conntime, sizeof(conntime), "%02d:%02d:%02d.%d",
- hours, minutes, seconds, (int) connecttime);
- ast_cli(a->fd, "%-10s%-20s%-12d%-11s%-30s\n",
- s->name, s->peer, s->reconnects, (s->outbound)? "OUT":"IN", conntime);
- }
- /* destroy our local link queue */
- s = s_head.next;
- while (s != &s_head) {
- t = s;
- s = s->next;
- remque((struct qelem *)t);
- ast_free(t);
- }
- return CLI_SUCCESS;
- }
- }
-
- return CLI_FAILURE;
-}
-
-/*
-* reload vars
-*/
-static char *handle_cli_rpt_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- int n;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "rpt reload";
- e->usage =
- "Usage: rpt reload\n"
- " Reloads app_rpt running config parameters\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc > 2)
- return CLI_SHOWUSAGE;
-
- for (n = 0; n < nrpts; n++)
- rpt_vars[n].reload = 1;
-
- return CLI_SUCCESS;
-}
-
-/*
-* restart app_rpt
-*/
-static char *handle_cli_rpt_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- int i;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "rpt restart";
- e->usage =
- "Usage: rpt restart\n"
- " Restarts app_rpt\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc > 2)
- return CLI_SHOWUSAGE;
- for (i = 0; i < nrpts; i++) {
- if (rpt_vars[i].rxchannel)
- ast_softhangup(rpt_vars[i].rxchannel, AST_SOFTHANGUP_DEV);
- }
- return CLI_SUCCESS;
-}
-
-static int play_tone_pair(struct ast_channel *chan, int f1, int f2, int duration, int amplitude)
-{
- int res;
-
- if ((res = ast_tonepair_start(chan, f1, f2, duration, amplitude)))
- return res;
-
- while (chan->generatordata) {
- if (ast_safe_sleep(chan, 1))
- return -1;
- }
-
- return 0;
-}
-
-static int play_tone(struct ast_channel *chan, int freq, int duration, int amplitude)
-{
- return play_tone_pair(chan, freq, 0, duration, amplitude);
-}
-
-static int play_silence(struct ast_channel *chan, int duration)
-{
- return play_tone_pair(chan, 0, 0, duration, 0);
-}
-
-
-static int send_morse(struct ast_channel *chan, const char *string, int speed, int freq, int amplitude)
-{
-
- static struct morse_bits mbits[] = {
- {0, 0}, /* SPACE */
- {0, 0},
- {6, 18},/* " */
- {0, 0},
- {7, 72},/* $ */
- {0, 0},
- {0, 0},
- {6, 30},/* ' */
- {5, 13},/* ( */
- {6, 29},/* ) */
- {0, 0},
- {5, 10},/* + */
- {6, 51},/* , */
- {6, 33},/* - */
- {6, 42},/* . */
- {5, 9}, /* / */
- {5, 31},/* 0 */
- {5, 30},/* 1 */
- {5, 28},/* 2 */
- {5, 24},/* 3 */
- {5, 16},/* 4 */
- {5, 0}, /* 5 */
- {5, 1}, /* 6 */
- {5, 3}, /* 7 */
- {5, 7}, /* 8 */
- {5, 15},/* 9 */
- {6, 7}, /* : */
- {6, 21},/* ; */
- {0, 0},
- {5, 33},/* = */
- {0, 0},
- {6, 12},/* ? */
- {0, 0},
- {2, 2}, /* A */
- {4, 1}, /* B */
- {4, 5}, /* C */
- {3, 1}, /* D */
- {1, 0}, /* E */
- {4, 4}, /* F */
- {3, 3}, /* G */
- {4, 0}, /* H */
- {2, 0}, /* I */
- {4, 14},/* J */
- {3, 5}, /* K */
- {4, 2}, /* L */
- {2, 3}, /* M */
- {2, 1}, /* N */
- {3, 7}, /* O */
- {4, 6}, /* P */
- {4, 11},/* Q */
- {3, 2}, /* R */
- {3, 0}, /* S */
- {1, 1}, /* T */
- {3, 4}, /* U */
- {4, 8}, /* V */
- {3, 6}, /* W */
- {4, 9}, /* X */
- {4, 13},/* Y */
- {4, 3} /* Z */
- };
-
- int dottime;
- int dashtime;
- int intralettertime;
- int interlettertime;
- int interwordtime;
- int len, ddcomb;
- int res;
- int c;
- int i;
- int flags;
-
- res = 0;
-
- /* Approximate the dot time from the speed arg. */
-
- dottime = 900 / speed;
-
- /* Establish timing relationships */
-
- dashtime = 3 * dottime;
- intralettertime = dottime;
- interlettertime = dottime * 4 ;
- interwordtime = dottime * 7;
-
- for (; (*string) && (!res); string++) {
-
- c = *string;
-
- /* Convert lower case to upper case */
-
- if ((c >= 'a') && (c <= 'z'))
- c -= 0x20;
-
- /* Can't deal with any char code greater than Z, skip it */
-
- if (c > 'Z')
- continue;
-
- /* If space char, wait the inter word time */
-
- if (c == ' ') {
- if (!res)
- res = play_silence(chan, interwordtime);
- continue;
- }
-
- /* Subtract out control char offset to match our table */
-
- c -= 0x20;
-
- /* Get the character data */
-
- len = mbits[c].len;
- ddcomb = mbits[c].ddcomb;
-
- /* Send the character */
-
- for (; len ; len--) {
- if (!res)
- res = play_tone(chan, freq, (ddcomb & 1) ? dashtime : dottime, amplitude);
- if (!res)
- res = play_silence(chan, intralettertime);
- ddcomb >>= 1;
- }
-
- /* Wait the interletter time */
-
- if (!res)
- res = play_silence(chan, interlettertime - intralettertime);
- }
-
- /* Wait for all the frames to be sent */
-
- if (!res)
- res = ast_waitstream(chan, "");
- ast_stopstream(chan);
-
- /*
- * Wait for the zaptel driver to physically write the tone blocks to the hardware
- */
-
- for (i = 0; i < 20 ; i++) {
- flags = ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT;
- res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
- if (flags & ZT_IOMUX_WRITEEMPTY)
- break;
- if ( ast_safe_sleep(chan, 50)) {
- res = -1;
- break;
- }
- }
-
-
- return res;
-}
-
-static int send_tone_telemetry(struct ast_channel *chan, const char *tonestring)
-{
- char *stringp;
- char *tonesubset;
- int f1, f2;
- int duration;
- int amplitude;
- int res;
- int i;
- int flags;
-
- res = 0;
-
- stringp = ast_strdupa(tonestring);
-
- for (;tonestring;) {
- tonesubset = strsep(&stringp, ")");
- if (!tonesubset)
- break;
- if (sscanf(tonesubset, "(%d,%d,%d,%d", &f1, &f2, &duration, &amplitude) != 4)
- break;
- res = play_tone_pair(chan, f1, f2, duration, amplitude);
- if (res)
- break;
- }
- if (!res)
- res = play_tone_pair(chan, 0, 0, 100, 0); /* This is needed to ensure the last tone segment is timed correctly */
-
- if (!res)
- res = ast_waitstream(chan, "");
- ast_stopstream(chan);
-
- /*
- * Wait for the zaptel driver to physically write the tone blocks to the hardware
- */
-
- for (i = 0; i < 20 ; i++) {
- flags = ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT;
- res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
- if (flags & ZT_IOMUX_WRITEEMPTY)
- break;
- if (ast_safe_sleep(chan, 50)) {
- res = -1;
- break;
- }
- }
-
- return res;
-}
-
-
-static int sayfile(struct ast_channel *mychannel, const char *fname)
-{
- int res;
-
- res = ast_streamfile(mychannel, fname, mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- return res;
-}
-
-static int saycharstr(struct ast_channel *mychannel, char *str)
-{
- int res;
-
- res = ast_say_character_str(mychannel, str, NULL, mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- return res;
-}
-
-static int saynum(struct ast_channel *mychannel, int num)
-{
- int res;
- res = ast_say_number(mychannel, num, NULL, mychannel->language, NULL);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- return res;
-}
-
-static int saydigits(struct ast_channel *mychannel, int num)
-{
- int res;
- res = ast_say_digits(mychannel, num, NULL, mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- return res;
-}
-
-
-static int telem_any(struct rpt *myrpt, struct ast_channel *chan, const char *entry)
-{
- int res;
- char c;
-
- static int morsespeed;
- static int morsefreq;
- static int morseampl;
- static int morseidfreq = 0;
- static int morseidampl;
- static char mcat[] = MORSE;
-
- res = 0;
-
- if (!morseidfreq) { /* Get the morse parameters if not already loaded */
- morsespeed = retrieve_astcfgint(myrpt, mcat, "speed", 5, 20, 20);
- morsefreq = retrieve_astcfgint(myrpt, mcat, "frequency", 300, 3000, 800);
- morseampl = retrieve_astcfgint(myrpt, mcat, "amplitude", 200, 8192, 4096);
- morseidampl = retrieve_astcfgint(myrpt, mcat, "idamplitude", 200, 8192, 2048);
- morseidfreq = retrieve_astcfgint(myrpt, mcat, "idfrequency", 300, 3000, 330);
- }
-
- /* Is it a file, or a tone sequence? */
-
- if (entry[0] == '|') {
- c = entry[1];
- if ((c >= 'a') && (c <= 'z'))
- c -= 0x20;
-
- switch (c) {
- case 'I': /* Morse ID */
- res = send_morse(chan, entry + 2, morsespeed, morseidfreq, morseidampl);
- break;
- case 'M': /* Morse Message */
- res = send_morse(chan, entry + 2, morsespeed, morsefreq, morseampl);
- break;
- case 'T': /* Tone sequence */
- res = send_tone_telemetry(chan, entry + 2);
- break;
- default:
- res = -1;
- }
- } else
- res = sayfile(chan, entry); /* File */
- return res;
-}
-
-/*
-* This function looks up a telemetry name in the config file, and does a telemetry response as configured.
-*
-* 4 types of telemtry are handled: Morse ID, Morse Message, Tone Sequence, and a File containing a recording.
-*/
-
-static int telem_lookup(struct rpt *myrpt, struct ast_channel *chan, const char *node, const char *name)
-{
- int res = 0;
- int i;
- const char *entry = NULL;
- const char *telemetry;
-
- /* Retrieve the section name for telemetry from the node section */
- if ((telemetry = ast_variable_retrieve(myrpt->cfg, node, TELEMETRY)))
- entry = ast_variable_retrieve(myrpt->cfg, telemetry, name);
-
- /* Try to look up the telemetry name */
-
- if (!entry) {
- /* Telemetry name wasn't found in the config file, use the default */
- for (i = 0; i < sizeof(tele_defs) / sizeof(struct telem_defaults); i++) {
- if (!strcasecmp(tele_defs[i].name, name))
- entry = tele_defs[i].value;
- }
- }
- if (entry) {
- if (!ast_strlen_zero(entry))
- telem_any(myrpt, chan, entry);
- } else {
- res = -1;
- }
- return res;
-}
-
-/*
-* Retrieve a wait interval
-*/
-
-static int get_wait_interval(struct rpt *myrpt, int type)
-{
- int interval = 1000;
- const char *wait_times = ast_variable_retrieve(myrpt->cfg, myrpt->name, "wait_times");
-
- switch (type) {
- case DLY_TELEM:
- if (wait_times)
- interval = retrieve_astcfgint(myrpt, wait_times, "telemwait", 500, 5000, 1000);
- break;
- case DLY_ID:
- if (wait_times)
- interval = retrieve_astcfgint(myrpt, wait_times, "idwait", 250, 5000, 500);
- else
- interval = 500;
- break;
- case DLY_UNKEY:
- if (wait_times)
- interval = retrieve_astcfgint(myrpt, wait_times, "unkeywait", 500, 5000, 1000);
- break;
- case DLY_CALLTERM:
- if (wait_times)
- interval = retrieve_astcfgint(myrpt, wait_times, "calltermwait", 500, 5000, 1500);
- break;
- default:
- return 0;
- }
- return interval;
-}
-
-
-/*
-* Wait a configurable interval of time
-*/
-
-
-static void wait_interval(struct rpt *myrpt, int type, struct ast_channel *chan)
-{
- int interval;
- interval = get_wait_interval(myrpt, type);
- if (debug)
- ast_log(LOG_NOTICE, " Delay interval = %d\n", interval);
- if (interval)
- ast_safe_sleep(chan, interval);
- if (debug)
- ast_log(LOG_NOTICE, "Delay complete\n");
- return;
-}
-
-
-static void *rpt_tele_thread(void *this)
-{
- ZT_CONFINFO ci; /* conference info */
- int res = 0, haslink, hastx, hasremote, imdone = 0, unkeys_queued, x;
- struct rpt_tele *mytele = (struct rpt_tele *)this;
- struct rpt_tele *tlist;
- struct rpt *myrpt;
- struct rpt_link *l, *m, linkbase;
- struct ast_channel *mychannel;
- const char *p, *ct;
- struct timeval tv;
- struct ast_tm localtm;
-#ifdef APP_RPT_LOCK_DEBUG
- struct lockthread *t;
-#endif
-
- /* get a pointer to myrpt */
- myrpt = mytele->rpt;
-
- /* Snag copies of a few key myrpt variables */
- rpt_mutex_lock(&myrpt->lock);
- rpt_mutex_unlock(&myrpt->lock);
-
- /* allocate a pseudo-channel thru asterisk */
- mychannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
- if (!mychannel) {
- ast_log(LOG_WARNING, "rpt: unable to obtain pseudo channel\n");
- rpt_mutex_lock(&myrpt->lock);
- remque((struct qelem *)mytele);
- ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
- rpt_mutex_unlock(&myrpt->lock);
- ast_free(mytele);
- pthread_exit(NULL);
- }
- rpt_mutex_lock(&myrpt->lock);
- mytele->chan = mychannel; /* Save a copy of the channel so we can access it externally if need be */
- rpt_mutex_unlock(&myrpt->lock);
-
- /* make a conference for the tx */
- ci.chan = 0;
- /* If there's an ID queued, or tail message queued, */
- /* only connect the ID audio to the local tx conference so */
- /* linked systems can't hear it */
- ci.confno = (((mytele->mode == ID) || (mytele->mode == IDTALKOVER) || (mytele->mode == UNKEY) ||
- (mytele->mode == TAILMSG)) ?
- myrpt->txconf : myrpt->conf);
- ci.confmode = ZT_CONF_CONFANN;
- /* first put the channel on the conference in announce mode */
- if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- rpt_mutex_lock(&myrpt->lock);
- remque((struct qelem *)mytele);
- rpt_mutex_unlock(&myrpt->lock);
- ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
- ast_free(mytele);
- ast_hangup(mychannel);
- pthread_exit(NULL);
- }
- ast_stopstream(mychannel);
- switch (mytele->mode) {
- case ID:
- case ID1:
- /* wait a bit */
- wait_interval(myrpt, (mytele->mode == ID) ? DLY_ID : DLY_TELEM, mychannel);
- res = telem_any(myrpt, mychannel, myrpt->p.ident);
- imdone=1;
- break;
-
- case TAILMSG:
- res = ast_streamfile(mychannel, myrpt->p.tailmsg.msgs[myrpt->tailmessagen], mychannel->language);
- break;
-
- case IDTALKOVER:
- p = ast_variable_retrieve(myrpt->cfg, myrpt->name, "idtalkover");
- if (p)
- res = telem_any(myrpt, mychannel, p);
- imdone = 1;
- break;
- case PROC:
- /* wait a little bit longer */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = telem_lookup(myrpt, mychannel, myrpt->name, "patchup");
- if (res < 0) { /* Then default message */
- res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language);
- }
- break;
- case TERM:
- /* wait a little bit longer */
- wait_interval(myrpt, DLY_CALLTERM, mychannel);
- res = telem_lookup(myrpt, mychannel, myrpt->name, "patchdown");
- if (res < 0) { /* Then default message */
- res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language);
- }
- break;
- case COMPLETE:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = telem_lookup(myrpt, mychannel, myrpt->name, "functcomplete");
- break;
- case MACRO_NOTFOUND:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = ast_streamfile(mychannel, "rpt/macro_notfound", mychannel->language);
- break;
- case GOSUB_NOTFOUND:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = ast_streamfile(mychannel, "rpt/gosub_notfound", mychannel->language);
- break;
- case MACRO_BUSY:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = ast_streamfile(mychannel, "rpt/macro_busy", mychannel->language);
- break;
- case GOSUB_BUSY:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = ast_streamfile(mychannel, "rpt/gosub_busy", mychannel->language);
- break;
- case UNKEY:
- if (myrpt->patchnoct && myrpt->callmode) { /* If no CT during patch configured, then don't send one */
- imdone = 1;
- break;
- }
-
- /*
- * Reset the Unkey to CT timer
- */
-
- x = get_wait_interval(myrpt, DLY_UNKEY);
- rpt_mutex_lock(&myrpt->lock);
- myrpt->unkeytocttimer = x; /* Must be protected as it is changed below */
- rpt_mutex_unlock(&myrpt->lock);
-
- /*
- * If there's one already queued, don't do another
- */
-
- tlist = myrpt->tele.next;
- unkeys_queued = 0;
- if (tlist != &myrpt->tele) {
- rpt_mutex_lock(&myrpt->lock);
- while (tlist != &myrpt->tele) {
- if (tlist->mode == UNKEY)
- unkeys_queued++;
- tlist = tlist->next;
- }
- rpt_mutex_unlock(&myrpt->lock);
- }
- if (unkeys_queued > 1) {
- imdone = 1;
- break;
- }
-
- /* Wait for the telemetry timer to expire */
- /* Periodically check the timer since it can be re-initialized above */
- while (myrpt->unkeytocttimer) {
- int ctint;
- if (myrpt->unkeytocttimer > 100)
- ctint = 100;
- else
- ctint = myrpt->unkeytocttimer;
- ast_safe_sleep(mychannel, ctint);
- rpt_mutex_lock(&myrpt->lock);
- if (myrpt->unkeytocttimer < ctint)
- myrpt->unkeytocttimer = 0;
- else
- myrpt->unkeytocttimer -= ctint;
- rpt_mutex_unlock(&myrpt->lock);
- }
-
- /*
- * Now, the carrier on the rptr rx should be gone.
- * If it re-appeared, then forget about sending the CT
- */
- if (myrpt->keyed) {
- imdone = 1;
- break;
- }
-
- rpt_mutex_lock(&myrpt->lock); /* Update the kerchunk counters */
- myrpt->dailykerchunks++;
- myrpt->totalkerchunks++;
- rpt_mutex_unlock(&myrpt->lock);
-
- haslink = 0;
- hastx = 0;
- hasremote = 0;
- l = myrpt->links.next;
- if (l != &myrpt->links) {
- rpt_mutex_lock(&myrpt->lock);
- while (l != &myrpt->links) {
- if (l->name[0] == '0') {
- l = l->next;
- continue;
- }
- haslink = 1;
- if (l->mode) {
- hastx++;
- if (l->isremote)
- hasremote++;
- }
- l = l->next;
- }
- rpt_mutex_unlock(&myrpt->lock);
- }
- if (haslink) {
- res = telem_lookup(myrpt, mychannel, myrpt->name, (!hastx) ? "remotemon" : "remotetx");
- if (res)
- ast_log(LOG_WARNING, "telem_lookup:remotexx failed on %s\n", mychannel->name);
-
- /* if in remote cmd mode, indicate it */
- if (myrpt->cmdnode[0]) {
- ast_safe_sleep(mychannel, 200);
- res = telem_lookup(myrpt, mychannel, myrpt->name, "cmdmode");
- if (res)
- ast_log(LOG_WARNING, "telem_lookup:cmdmode failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- }
- } else if ((ct = ast_variable_retrieve(myrpt->cfg, myrpt->name, "unlinkedct"))) { /* Unlinked Courtesy Tone */
- res = telem_lookup(myrpt, mychannel, myrpt->name, ct);
- if (res)
- ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);
- }
- if (hasremote && (!myrpt->cmdnode[0])) {
- /* set for all to hear */
- ci.chan = 0;
- ci.confno = myrpt->conf;
- ci.confmode = ZT_CONF_CONFANN;
- /* first put the channel on the conference in announce mode */
- if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- rpt_mutex_lock(&myrpt->lock);
- remque((struct qelem *)mytele);
- rpt_mutex_unlock(&myrpt->lock);
- ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
- ast_free(mytele);
- ast_hangup(mychannel);
- pthread_exit(NULL);
- }
- if ((ct = ast_variable_retrieve(myrpt->cfg, myrpt->name, "remotect"))) { /* Unlinked Courtesy Tone */
- ast_safe_sleep(mychannel, 200);
- res = telem_lookup(myrpt, mychannel, myrpt->name, ct);
- if (res)
- ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);
- }
- }
-#ifdef _MDC_DECODE_H_
- if (myrpt->lastunit) {
- char mystr[10];
-
- ast_safe_sleep(mychannel, 200);
- /* set for all to hear */
- ci.chan = 0;
- ci.confno = myrpt->txconf;
- ci.confmode = ZT_CONF_CONFANN;
- /* first put the channel on the conference in announce mode */
- if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- rpt_mutex_lock(&myrpt->lock);
- remque((struct qelem *)mytele);
- rpt_mutex_unlock(&myrpt->lock);
- ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
- ast_free(mytele);
- ast_hangup(mychannel);
- pthread_exit(NULL);
- }
- snprintf(mystr, sizeof(mystr), "%04x", myrpt->lastunit);
- myrpt->lastunit = 0;
- ast_say_character_str(mychannel, mystr, NULL, mychannel->language);
- break;
- }
-#endif
- imdone = 1;
- break;
- case REMDISC:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- ast_say_character_str(mychannel, mytele->mylink.name, NULL, mychannel->language);
- res = ast_streamfile(mychannel, ((mytele->mylink.connected) ?
- "rpt/remote_disc" : "rpt/remote_busy"), mychannel->language);
- break;
- case REMALREADY:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = ast_streamfile(mychannel, "rpt/remote_already", mychannel->language);
- break;
- case REMNOTFOUND:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = ast_streamfile(mychannel, "rpt/remote_notfound", mychannel->language);
- break;
- case REMGO:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = ast_streamfile(mychannel, "rpt/remote_go", mychannel->language);
- break;
- case CONNECTED:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- ast_say_character_str(mychannel, mytele->mylink.name, NULL, mychannel->language);
- res = ast_streamfile(mychannel, "rpt/connected", mychannel->language);
- break;
- case CONNFAIL:
- res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- ast_say_character_str(mychannel, mytele->mylink.name, NULL, mychannel->language);
- res = ast_streamfile(mychannel, "rpt/connection_failed", mychannel->language);
- break;
- case STATUS:
- /* wait a little bit */
- wait_interval(myrpt, DLY_TELEM, mychannel);
- hastx = 0;
- linkbase.next = &linkbase;
- linkbase.prev = &linkbase;
- rpt_mutex_lock(&myrpt->lock);
- /* make our own list of links */
- l = myrpt->links.next;
- while (l != &myrpt->links) {
- if (l->name[0] == '0') {
- l = l->next;
- continue;
- }
- m = ast_malloc(sizeof(*m));
- if (!m) {
- ast_log(LOG_WARNING, "Cannot alloc memory on %s\n", mychannel->name);
- remque((struct qelem *)mytele);
- rpt_mutex_unlock(&myrpt->lock);
- ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
- ast_free(mytele);
- ast_hangup(mychannel);
- pthread_exit(NULL);
- }
- memcpy(m, l, sizeof(struct rpt_link));
- m->next = m->prev = NULL;
- insque((struct qelem *)m, (struct qelem *)linkbase.next);
- l = l->next;
- }
- rpt_mutex_unlock(&myrpt->lock);
- res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- ast_say_character_str(mychannel, myrpt->name, NULL, mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- if (myrpt->callmode) {
- hastx = 1;
- res = ast_streamfile(mychannel, "rpt/autopatch_on", mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- }
- l = linkbase.next;
- while (l != &linkbase) {
- hastx = 1;
- res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- ast_say_character_str(mychannel, l->name, NULL, mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- res = ast_streamfile(mychannel, ((l->mode) ?
- "rpt/tranceive" : "rpt/monitor"), mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- l = l->next;
- }
- if (!hastx) {
- res = ast_streamfile(mychannel, "rpt/repeat_only", mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- }
- /* destroy our local link queue */
- l = linkbase.next;
- while (l != &linkbase) {
- m = l;
- l = l->next;
- remque((struct qelem *)m);
- ast_free(m);
- }
- imdone = 1;
- break;
-
- case LASTNODEKEY: /* Identify last node which keyed us up */
- rpt_mutex_lock(&myrpt->lock);
- if (myrpt->lastnodewhichkeyedusup)
- p = ast_strdupa(myrpt->lastnodewhichkeyedusup); /* Make a local copy of the node name */
- else
- p = NULL;
- rpt_mutex_unlock(&myrpt->lock);
- if (!p) {
- imdone = 1; /* no node previously keyed us up, or the node which did has been disconnected */
- break;
- }
- wait_interval(myrpt, DLY_TELEM, mychannel);
- res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- ast_say_character_str(mychannel, p, NULL, mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- imdone = 1;
- break;
-
- case TIMEOUT:
- res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- ast_stopstream(mychannel);
- ast_say_character_str(mychannel, myrpt->name, NULL, mychannel->language);
- res = ast_streamfile(mychannel, "rpt/timeout", mychannel->language);
- break;
-
- case STATS_TIME:
- wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
- tv = ast_tvnow();
- ast_localtime(&tv, &localtm, NULL);
- /* Say the phase of the day is before the time */
- if ((localtm.tm_hour >= 0) && (localtm.tm_hour < 12))
- p = "rpt/goodmorning";
- else if ((localtm.tm_hour >= 12) && (localtm.tm_hour < 18))
- p = "rpt/goodafternoon";
- else
- p = "rpt/goodevening";
- if (sayfile(mychannel, p) == -1) {
- imdone = 1;
- break;
- }
- /* Say the time is ... */
- if (sayfile(mychannel, "rpt/thetimeis") == -1) {
- imdone = 1;
- break;
- }
- /* Say the time */
- res = ast_say_time(mychannel, tv.tv_sec, "", mychannel->language);
- if (!res)
- res = ast_waitstream(mychannel, "");
- ast_stopstream(mychannel);
- imdone = 1;
- break;
- case STATS_VERSION:
- wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
- /* Say "version" */
- if (sayfile(mychannel, "rpt/version") == -1) {
- imdone = 1;
- break;
- }
- if (!res) /* Say "X" */
- ast_say_number(mychannel, vmajor, "", mychannel->language, (char *) NULL);
- if (!res)
- res = ast_waitstream(mychannel, "");
- ast_stopstream(mychannel);
- if (saycharstr(mychannel, ".") == -1) {
- imdone = 1;
- break;
- }
- if (!res) /* Say "Y" */
- ast_say_number(mychannel, vminor, "", mychannel->language, (char *) NULL);
- if (!res) {
- res = ast_waitstream(mychannel, "");
- ast_stopstream(mychannel);
- } else
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- imdone = 1;
- break;
- case ARB_ALPHA:
- wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
- if (mytele->param)
- saycharstr(mychannel, mytele->param);
- imdone = 1;
- break;
- case REV_PATCH:
- wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
- if (mytele->param) {
- /* Parts of this section taken from app_parkandannounce */
- char *tpl_working, *tpl_current;
- char *tmp[100], *myparm;
- int looptemp=0, i = 0, dres = 0;
-
- tpl_working = ast_strdupa(mytele->param);
- myparm = strsep(&tpl_working, ",");
- tpl_current = strsep(&tpl_working, ":");
-
- while (tpl_current && looptemp < sizeof(tmp)) {
- tmp[looptemp] = tpl_current;
- looptemp++;
- tpl_current = strsep(&tpl_working, ":");
- }
-
- for (i = 0; i < looptemp; i++) {
- if (!strcmp(tmp[i], "PARKED")) {
- ast_say_digits(mychannel, atoi(myparm), "", mychannel->language);
- } else if (!strcmp(tmp[i], "NODE")) {
- ast_say_digits(mychannel, atoi(myrpt->name), "", mychannel->language);
- } else {
- dres = ast_streamfile(mychannel, tmp[i], mychannel->language);
- if (!dres) {
- dres = ast_waitstream(mychannel, "");
- } else {
- ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], mychannel->name);
- dres = 0;
- }
- }
- }
- }
- imdone = 1;
- break;
- case TEST_TONE:
- imdone = 1;
- myrpt->stopgen = 0;
- if ((res = ast_tonepair_start(mychannel, 1004.0, 0, 99999999, 7200.0)))
- break;
- while (mychannel->generatordata && (!myrpt->stopgen)) {
- if (ast_safe_sleep(mychannel, 1)) break;
- imdone = 1;
- }
- break;
- default:
- break;
- }
-
- myrpt->stopgen = 0;
- if (!imdone) {
- if (!res)
- res = ast_waitstream(mychannel, "");
- else {
- ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
- res = 0;
- }
- }
- ast_stopstream(mychannel);
- rpt_mutex_lock(&myrpt->lock);
- if (mytele->mode == TAILMSG) {
- if (!res) {
- myrpt->tailmessagen++;
- if (myrpt->tailmessagen >= myrpt->p.tailmsg.argc)
- myrpt->tailmessagen = 0;
- } else {
- myrpt->tmsgtimer = myrpt->p.tailsquashedtime;
- }
- }
- remque((struct qelem *)mytele);
- rpt_mutex_unlock(&myrpt->lock);
- ast_free(mytele);
- ast_hangup(mychannel);
-#ifdef APP_RPT_LOCK_DEBUG
- sleep(5);
- ast_mutex_lock(&locklock);
- t = get_lockthread(pthread_self());
- if (t)
- memset(t, 0, sizeof(struct lockthread));
- ast_mutex_unlock(&locklock);
-#endif
- pthread_exit(NULL);
-}
-
-static void rpt_telemetry(struct rpt *myrpt, int mode, void *data)
-{
- struct rpt_tele *tele;
- struct rpt_link *mylink = (struct rpt_link *) data;
- int res;
-
- tele = ast_calloc(1, sizeof(*tele));
- if (!tele) {
- ast_log(LOG_WARNING, "Unable to allocate memory\n");
- pthread_exit(NULL);
- }
- tele->rpt = myrpt;
- tele->mode = mode;
- rpt_mutex_lock(&myrpt->lock);
- if ((mode == CONNFAIL) || (mode == REMDISC) || (mode == CONNECTED)) {
- if (mylink) {
- memcpy(&tele->mylink, mylink, sizeof(struct rpt_link));
- }
- } else if ((mode == ARB_ALPHA) || (mode == REV_PATCH)) {
- ast_copy_string(tele->param, (char *) data, sizeof(tele->param));
- }
- insque((struct qelem *)tele, (struct qelem *)myrpt->tele.next);
- rpt_mutex_unlock(&myrpt->lock);
- res = ast_pthread_create_detached(&tele->threadid, NULL, rpt_tele_thread, (void *) tele);
- if (res != 0) {
- rpt_mutex_lock(&myrpt->lock);
- remque((struct qlem *) tele); /* We don't like stuck transmitters, remove it from the queue */
- rpt_mutex_unlock(&myrpt->lock);
- ast_log(LOG_WARNING, "Could not create telemetry thread: %s\n", strerror(res));
- }
- return;
-}
-
-static void *rpt_call(void *this)
-{
- ZT_CONFINFO ci; /* conference info */
- struct rpt *myrpt = (struct rpt *)this;
- int res;
- struct ast_frame wf;
- int stopped, congstarted, dialtimer, lastcidx, aborted;
- struct ast_channel *mychannel, *genchannel;
-
- myrpt->mydtmf = 0;
- /* allocate a pseudo-channel thru asterisk */
- mychannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
- if (!mychannel) {
- ast_log(LOG_ERROR, "rpt: unable to obtain pseudo channel\n");
- pthread_exit(NULL);
- }
- ci.chan = 0;
- ci.confno = myrpt->conf; /* use the pseudo conference */
- ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER
- | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER;
- /* first put the channel on the conference */
- if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- ast_hangup(mychannel);
- myrpt->callmode = 0;
- pthread_exit(NULL);
- }
- /* allocate a pseudo-channel thru asterisk */
- genchannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
- if (!genchannel) {
- ast_log(LOG_ERROR, "rpt: unable to obtain pseudo channel\n");
- ast_hangup(mychannel);
- pthread_exit(NULL);
- }
- ci.chan = 0;
- ci.confno = myrpt->conf;
- ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER
- | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER;
- /* first put the channel on the conference */
- if (ioctl(genchannel->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- ast_hangup(mychannel);
- ast_hangup(genchannel);
- myrpt->callmode = 0;
- pthread_exit(NULL);
- }
- if (myrpt->p.tonezone && (tone_zone_set_zone(mychannel->fds[0], myrpt->p.tonezone) == -1)) {
- ast_log(LOG_WARNING, "Unable to set tone zone %s\n", myrpt->p.tonezone);
- ast_hangup(mychannel);
- ast_hangup(genchannel);
- myrpt->callmode = 0;
- pthread_exit(NULL);
- }
- if (myrpt->p.tonezone && (tone_zone_set_zone(genchannel->fds[0], myrpt->p.tonezone) == -1)) {
- ast_log(LOG_WARNING, "Unable to set tone zone %s\n", myrpt->p.tonezone);
- ast_hangup(mychannel);
- ast_hangup(genchannel);
- myrpt->callmode = 0;
- pthread_exit(NULL);
- }
- /* start dialtone if patchquiet is 0. Special patch modes don't send dial tone */
- if ((!myrpt->patchquiet) && (tone_zone_play_tone(mychannel->fds[0], ZT_TONE_DIALTONE) < 0)) {
- ast_log(LOG_WARNING, "Cannot start dialtone\n");
- ast_hangup(mychannel);
- ast_hangup(genchannel);
- myrpt->callmode = 0;
- pthread_exit(NULL);
- }
- stopped = 0;
- congstarted = 0;
- dialtimer = 0;
- lastcidx = 0;
- aborted = 0;
-
- while ((myrpt->callmode == 1) || (myrpt->callmode == 4)) {
- if ((myrpt->patchdialtime) && (myrpt->callmode == 1) && (myrpt->cidx != lastcidx)) {
- dialtimer = 0;
- lastcidx = myrpt->cidx;
- }
-
- if ((myrpt->patchdialtime) && (dialtimer >= myrpt->patchdialtime)) {
- rpt_mutex_lock(&myrpt->lock);
- aborted = 1;
- myrpt->callmode = 0;
- rpt_mutex_unlock(&myrpt->lock);
- break;
- }
-
- if ((!myrpt->patchquiet) && (!stopped) && (myrpt->callmode == 1) && (myrpt->cidx > 0)) {
- stopped = 1;
- /* stop dial tone */
- tone_zone_play_tone(mychannel->fds[0], -1);
- }
- if (myrpt->callmode == 4) {
- if (!congstarted) {
- congstarted = 1;
- /* start congestion tone */
- tone_zone_play_tone(mychannel->fds[0], ZT_TONE_CONGESTION);
- }
- }
- res = ast_safe_sleep(mychannel, MSWAIT);
- if (res < 0) {
- ast_hangup(mychannel);
- ast_hangup(genchannel);
- rpt_mutex_lock(&myrpt->lock);
- myrpt->callmode = 0;
- rpt_mutex_unlock(&myrpt->lock);
- pthread_exit(NULL);
- }
- dialtimer += MSWAIT;
- }
- /* stop any tone generation */
- tone_zone_play_tone(mychannel->fds[0], -1);
- /* end if done */
- if (!myrpt->callmode) {
- ast_hangup(mychannel);
- ast_hangup(genchannel);
- rpt_mutex_lock(&myrpt->lock);
- myrpt->callmode = 0;
- rpt_mutex_unlock(&myrpt->lock);
- if ((!myrpt->patchquiet) && aborted)
- rpt_telemetry(myrpt, TERM, NULL);
- pthread_exit(NULL);
- }
-
- if (myrpt->p.ourcallerid && *myrpt->p.ourcallerid) {
- char *name, *loc, *instr;
- instr = ast_strdup(myrpt->p.ourcallerid);
- if (instr) {
- ast_callerid_parse(instr, &name, &loc);
- if (loc) {
- if (mychannel->cid.cid_num)
- ast_free(mychannel->cid.cid_num);
- mychannel->cid.cid_num = ast_strdup(loc);
- }
- if (name) {
- if (mychannel->cid.cid_name)
- ast_free(mychannel->cid.cid_name);
- mychannel->cid.cid_name = ast_strdup(name);
- }
- ast_free(instr);
- }
- }
-
- ast_copy_string(mychannel->exten, myrpt->exten, sizeof(mychannel->exten));
- ast_copy_string(mychannel->context, myrpt->patchcontext, sizeof(mychannel->context));
-
- if (myrpt->p.acctcode)
- ast_string_field_set(mychannel, accountcode, myrpt->p.acctcode);
- mychannel->priority = 1;
- ast_channel_undefer_dtmf(mychannel);
- if (ast_pbx_start(mychannel) < 0) {
- ast_log(LOG_WARNING, "Unable to start PBX!!\n");
- ast_hangup(mychannel);
- ast_hangup(genchannel);
- rpt_mutex_lock(&myrpt->lock);
- myrpt->callmode = 0;
- rpt_mutex_unlock(&myrpt->lock);
- pthread_exit(NULL);
- }
- usleep(10000);
- rpt_mutex_lock(&myrpt->lock);
- myrpt->callmode = 3;
- /* set appropriate conference for the pseudo */
- ci.chan = 0;
- ci.confno = myrpt->conf;
- ci.confmode = (myrpt->p.duplex == 2) ? ZT_CONF_CONFANNMON :
- (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
- /* first put the channel on the conference in announce mode */
- if (ioctl(myrpt->pchannel->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- ast_hangup(mychannel);
- ast_hangup(genchannel);
- myrpt->callmode = 0;
- pthread_exit(NULL);
- }
- while (myrpt->callmode) {
- if ((!mychannel->pbx) && (myrpt->callmode != 4)) {
- if (myrpt->patchfarenddisconnect) { /* If patch is setup for far end disconnect */
- myrpt->callmode = 0;
- if (!myrpt->patchquiet) {
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, TERM, NULL);
- rpt_mutex_lock(&myrpt->lock);
- }
- } else { /* Send congestion until patch is downed by command */
- myrpt->callmode = 4;
- rpt_mutex_unlock(&myrpt->lock);
- /* start congestion tone */
- tone_zone_play_tone(genchannel->fds[0], ZT_TONE_CONGESTION);
- rpt_mutex_lock(&myrpt->lock);
- }
- }
- if (myrpt->mydtmf) {
- wf.frametype = AST_FRAME_DTMF;
- wf.subclass = myrpt->mydtmf;
- wf.offset = 0;
- wf.mallocd = 0;
- wf.data = NULL;
- wf.datalen = 0;
- wf.samples = 0;
- rpt_mutex_unlock(&myrpt->lock);
- ast_write(genchannel, &wf);
- rpt_mutex_lock(&myrpt->lock);
- myrpt->mydtmf = 0;
- }
- rpt_mutex_unlock(&myrpt->lock);
- usleep(MSWAIT * 1000);
- rpt_mutex_lock(&myrpt->lock);
- }
- rpt_mutex_unlock(&myrpt->lock);
- tone_zone_play_tone(genchannel->fds[0], -1);
- if (mychannel->pbx)
- ast_softhangup(mychannel, AST_SOFTHANGUP_DEV);
- ast_hangup(genchannel);
- rpt_mutex_lock(&myrpt->lock);
- myrpt->callmode = 0;
- rpt_mutex_unlock(&myrpt->lock);
- /* set appropriate conference for the pseudo */
- ci.chan = 0;
- ci.confno = myrpt->conf;
- ci.confmode = ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4)) ? ZT_CONF_CONFANNMON :
- (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
- /* first put the channel on the conference in announce mode */
- if (ioctl(myrpt->pchannel->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- }
- pthread_exit(NULL);
-}
-
-static void send_link_dtmf(struct rpt *myrpt, char c)
-{
- char str[300];
- struct ast_frame wf;
- struct rpt_link *l;
-
- snprintf(str, sizeof(str), "D %s %s %d %c", myrpt->cmdnode, myrpt->name, ++(myrpt->dtmfidx), c);
- wf.frametype = AST_FRAME_TEXT;
- wf.subclass = 0;
- wf.offset = 0;
- wf.mallocd = 1;
- wf.datalen = strlen(str) + 1;
- wf.samples = 0;
- l = myrpt->links.next;
- /* first, see if our dude is there */
- while (l != &myrpt->links) {
- if (l->name[0] == '0') {
- l = l->next;
- continue;
- }
- /* if we found it, write it and were done */
- if (!strcmp(l->name, myrpt->cmdnode)) {
- wf.data = ast_strdup(str);
- if (l->chan)
- ast_write(l->chan, &wf);
- return;
- }
- l = l->next;
- }
- l = myrpt->links.next;
- /* if not, give it to everyone */
- while (l != &myrpt->links) {
- wf.data = ast_strdup(str);
- if (l->chan)
- ast_write(l->chan, &wf);
- l = l->next;
- }
- return;
-}
-
-/*
-* Internet linking function
-*/
-
-static int function_ilink(struct rpt *myrpt, char *param, char *digits, int command_source, struct rpt_link *mylink)
-{
- const char *val;
- char *s, *tele;
- char deststr[300] = "", modechange = 0;
- char digitbuf[MAXNODESTR];
- struct rpt_link *l;
- int reconnects = 0;
- ZT_CONFINFO ci; /* conference info */
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(s1);
- AST_APP_ARG(s2); /* XXX Never used. Scratch? XXX */
- );
-
- if (!param)
- return DC_ERROR;
-
- if (!myrpt->enable)
- return DC_ERROR;
-
- ast_copy_string(digitbuf, digits, sizeof(digitbuf));
- ast_debug(1, "@@@@ ilink param = %s, digitbuf = %s\n", S_OR(param, "(null)"), digitbuf);
-
- switch (myatoi(param)) {
- case 1: /* Link off */
- if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
- strcpy(digitbuf, myrpt->lastlinknode);
- val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
- if (!val) {
- if (strlen(digitbuf) >= myrpt->longestnode)
- return DC_ERROR;
- break;
- }
- rpt_mutex_lock(&myrpt->lock);
- l = myrpt->links.next;
- /* try to find this one in queue */
- while (l != &myrpt->links) {
- if (l->name[0] == '0') {
- l = l->next;
- continue;
- }
- /* if found matching string */
- if (!strcmp(l->name, digitbuf))
- break;
- l = l->next;
- }
- if (l != &myrpt->links) { /* if found */
- struct ast_frame wf;
- ast_copy_string(myrpt->lastlinknode, digitbuf, MAXNODESTR);
- l->retries = MAX_RETRIES + 1;
- l->disced = 1;
- rpt_mutex_unlock(&myrpt->lock);
- wf.frametype = AST_FRAME_TEXT;
- wf.subclass = 0;
- wf.offset = 0;
- wf.mallocd = 1;
- wf.datalen = strlen(discstr) + 1;
- wf.samples = 0;
- wf.data = ast_strdup(discstr);
- if (l->chan) {
- ast_write(l->chan, &wf);
- if (ast_safe_sleep(l->chan, 250) == -1)
- return DC_ERROR;
- ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
- }
- rpt_telemetry(myrpt, COMPLETE, NULL);
- return DC_COMPLETE;
- }
- rpt_mutex_unlock(&myrpt->lock);
- return DC_COMPLETE;
- case 2: /* Link Monitor */
- if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
- strcpy(digitbuf, myrpt->lastlinknode);
- val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
- if (!val) {
- if (strlen(digitbuf) >= myrpt->longestnode)
- return DC_ERROR;
- break;
- }
- s = ast_strdupa(val);
- AST_STANDARD_APP_ARGS(args, s);
- rpt_mutex_lock(&myrpt->lock);
- l = myrpt->links.next;
- /* try to find this one in queue */
- while (l != &myrpt->links) {
- if (l->name[0] == '0') {
- l = l->next;
- continue;
- }
- /* if found matching string */
- if (!strcmp(l->name, digitbuf))
- break;
- l = l->next;
- }
- /* if found */
- if (l != &myrpt->links) {
- /* if already in this mode, just ignore */
- if ((!l->mode) || (!l->chan)) {
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, REMALREADY, NULL);
- return DC_COMPLETE;
- }
- reconnects = l->reconnects;
- rpt_mutex_unlock(&myrpt->lock);
- if (l->chan)
- ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
- l->retries = MAX_RETRIES + 1;
- l->disced = 2;
- modechange = 1;
- } else
- rpt_mutex_unlock(&myrpt->lock);
- ast_copy_string(myrpt->lastlinknode, digitbuf, MAXNODESTR);
- /* establish call in monitor mode */
- l = ast_calloc(1, sizeof(*l));
- if (!l) {
- ast_log(LOG_WARNING, "Unable to malloc\n");
- return DC_ERROR;
- }
- snprintf(deststr, sizeof(deststr), "IAX2/%s", args.s1);
- tele = strchr(deststr, '/');
- if (!tele) {
- ast_log(LOG_ERROR, "link2:Dial number (%s) must be in format tech/number\n", deststr);
- return DC_ERROR;
- }
- *tele++ = 0;
- l->isremote = (s && ast_true(s));
- ast_copy_string(l->name, digitbuf, MAXNODESTR);
- l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele, NULL);
- if (modechange)
- l->connected = 1;
- if (l->chan) {
- ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
- ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
- l->chan->whentohangup = 0;
- l->chan->appl = "Apprpt";
- l->chan->data = "(Remote Rx)";
- ast_verb(3, "rpt (remote) initiating call to %s/%s on %s\n",
- deststr, tele, l->chan->name);
- if (l->chan->cid.cid_num)
- ast_free(l->chan->cid.cid_num);
- l->chan->cid.cid_num = ast_strdup(myrpt->name);
- ast_call(l->chan, tele, 0);
- } else {
- rpt_telemetry(myrpt, CONNFAIL, l);
- ast_free(l);
- ast_verb(3, "Unable to place call to %s/%s on %s\n",
- deststr, tele, l->chan->name);
- return DC_ERROR;
- }
- /* allocate a pseudo-channel thru asterisk */
- l->pchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
- if (!l->pchan) {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
- ast_hangup(l->chan);
- ast_free(l);
- return DC_ERROR;
- }
- ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR);
- ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR);
- /* make a conference for the pseudo-one */
- ci.chan = 0;
- ci.confno = myrpt->conf;
- ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
- /* first put the channel on the conference in proper mode */
- if (ioctl(l->pchan->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- ast_hangup(l->chan);
- ast_hangup(l->pchan);
- ast_free(l);
- return DC_ERROR;
- }
- rpt_mutex_lock(&myrpt->lock);
- l->reconnects = reconnects;
- /* insert at end of queue */
- insque((struct qelem *)l, (struct qelem *)myrpt->links.next);
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, COMPLETE, NULL);
- return DC_COMPLETE;
- case 3: /* Link transceive */
- if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
- strcpy(digitbuf, myrpt->lastlinknode);
- val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
- if (!val) {
- if (strlen(digitbuf) >= myrpt->longestnode)
- return DC_ERROR;
- break;
- }
- s = ast_strdupa(val);
- AST_STANDARD_APP_ARGS(args, s);
- rpt_mutex_lock(&myrpt->lock);
- l = myrpt->links.next;
- /* try to find this one in queue */
- while (l != &myrpt->links) {
- if (l->name[0] == '0') {
- l = l->next;
- continue;
- }
- /* if found matching string */
- if (!strcmp(l->name, digitbuf))
- break;
- l = l->next;
- }
- /* if found */
- if (l != &myrpt->links) {
- /* if already in this mode, just ignore */
- if ((l->mode) || (!l->chan)) {
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, REMALREADY, NULL);
- return DC_COMPLETE;
- }
- reconnects = l->reconnects;
- rpt_mutex_unlock(&myrpt->lock);
- if (l->chan)
- ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
- l->retries = MAX_RETRIES + 1;
- l->disced = 2;
- modechange = 1;
- } else
- rpt_mutex_unlock(&myrpt->lock);
- ast_copy_string(myrpt->lastlinknode, digitbuf, MAXNODESTR);
- /* establish call in tranceive mode */
- l = ast_calloc(1, sizeof(*l));
- if (!l) {
- ast_log(LOG_WARNING, "Unable to malloc\n");
- return DC_ERROR;
- }
- l->mode = 1;
- l->outbound = 1;
- ast_copy_string(l->name, digitbuf, MAXNODESTR);
- l->isremote = (s && ast_true(s));
- if (modechange)
- l->connected = 1;
- snprintf(deststr, sizeof(deststr), "IAX2/%s", args.s1);
- tele = strchr(deststr, '/');
- if (!tele) {
- ast_log(LOG_ERROR, "link3:Dial number (%s) must be in format tech/number\n", deststr);
- ast_free(l);
- return DC_ERROR;
- }
- *tele++ = 0;
- l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele, NULL);
- if (l->chan) {
- ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
- ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
- l->chan->whentohangup = 0;
- l->chan->appl = "Apprpt";
- l->chan->data = "(Remote Rx)";
- ast_verb(3, "rpt (remote) initiating call to %s/%s on %s\n",
- deststr, tele, l->chan->name);
- if (l->chan->cid.cid_num)
- ast_free(l->chan->cid.cid_num);
- l->chan->cid.cid_num = ast_strdup(myrpt->name);
- ast_call(l->chan, tele, 999);
- } else {
- rpt_telemetry(myrpt, CONNFAIL, l);
- ast_free(l);
- ast_verb(3, "Unable to place call to %s/%s on %s\n",
- deststr, tele, l->chan->name);
- return DC_ERROR;
- }
- /* allocate a pseudo-channel thru asterisk */
- l->pchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
- if (!l->pchan) {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
- ast_hangup(l->chan);
- ast_free(l);
- return DC_ERROR;
- }
- ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR);
- ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR);
- /* make a conference for the tx */
- ci.chan = 0;
- ci.confno = myrpt->conf;
- ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
- /* first put the channel on the conference in proper mode */
- if (ioctl(l->pchan->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- ast_hangup(l->chan);
- ast_hangup(l->pchan);
- ast_free(l);
- return DC_ERROR;
- }
- rpt_mutex_lock(&myrpt->lock);
- l->reconnects = reconnects;
- /* insert at end of queue */
- insque((struct qelem *)l, (struct qelem *)myrpt->links.next);
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, COMPLETE, NULL);
- return DC_COMPLETE;
- case 4: /* Enter Command Mode */
- /* if doesnt allow link cmd, or no links active, return */
- if (((command_source != SOURCE_RPT) &&
- (command_source != SOURCE_PHONE) &&
- (command_source != SOURCE_DPHONE)) ||
- (myrpt->links.next == &myrpt->links))
- return DC_COMPLETE;
- /* if already in cmd mode, or selected self, fughetabahtit */
- if ((myrpt->cmdnode[0]) || (!strcmp(myrpt->name, digitbuf))) {
- rpt_telemetry(myrpt, REMALREADY, NULL);
- return DC_COMPLETE;
- }
- if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
- strcpy(digitbuf, myrpt->lastlinknode);
- /* node must at least exist in list */
- val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
- if (!val) {
- if (strlen(digitbuf) >= myrpt->longestnode)
- return DC_ERROR;
- break;
- }
- rpt_mutex_lock(&myrpt->lock);
- strcpy(myrpt->lastlinknode, digitbuf);
- ast_copy_string(myrpt->cmdnode, digitbuf, sizeof(myrpt->cmdnode));
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, REMGO, NULL);
- return DC_COMPLETE;
- case 5: /* Status */
- rpt_telemetry(myrpt, STATUS, NULL);
- return DC_COMPLETE;
- case 6: /* All Links Off */
- l = myrpt->links.next;
- while (l != &myrpt->links) { /* This code is broke and needs to be changed to work with the reconnect kludge */
- if (l->chan)
- ast_softhangup(l->chan, AST_SOFTHANGUP_DEV); /* Hang 'em up */
- l = l->next;
- }
- rpt_telemetry(myrpt, COMPLETE, NULL);
- break;
- case 7: /* Identify last node which keyed us up */
- rpt_telemetry(myrpt, LASTNODEKEY, NULL);
- break;
- default:
- return DC_ERROR;
- }
-
- return DC_INDETERMINATE;
-}
-
-/*
-* Autopatch up
-*/
-
-static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
-{
- int i, index;
- char *value = NULL;
- AST_DECLARE_APP_ARGS(params,
- AST_APP_ARG(list)[20];
- );
-
- static char *keywords[] = {
- "context",
- "dialtime",
- "farenddisconnect",
- "noct",
- "quiet",
- NULL
- };
-
- if (!myrpt->enable)
- return DC_ERROR;
-
- ast_debug(1, "@@@@ Autopatch up\n");
-
- if (!myrpt->callmode) {
- /* Set defaults */
- myrpt->patchnoct = 0;
- myrpt->patchdialtime = 0;
- myrpt->patchfarenddisconnect = 0;
- myrpt->patchquiet = 0;
- ast_copy_string(myrpt->patchcontext, myrpt->p.ourcontext, sizeof(myrpt->patchcontext));
-
- if (param) {
- /* Process parameter list */
- char *tmp = ast_strdupa(param);
- AST_STANDARD_APP_ARGS(params, tmp);
- for (i = 0; i < params.argc; i++) {
- index = matchkeyword(params.list[i], &value, keywords);
- if (value)
- value = skipchars(value, "= ");
- switch (index) {
- case 1: /* context */
- ast_copy_string(myrpt->patchcontext, value, sizeof(myrpt->patchcontext)) ;
- break;
- case 2: /* dialtime */
- myrpt->patchdialtime = atoi(value);
- break;
- case 3: /* farenddisconnect */
- myrpt->patchfarenddisconnect = atoi(value);
- break;
- case 4: /* noct */
- myrpt->patchnoct = atoi(value);
- break;
- case 5: /* quiet */
- myrpt->patchquiet = atoi(value);
- break;
- default:
- break;
- }
- }
- }
- }
-
- rpt_mutex_lock(&myrpt->lock);
-
- /* if on call, force * into current audio stream */
-
- if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) {
- myrpt->mydtmf = myrpt->p.funcchar;
- }
- if (myrpt->callmode) {
- rpt_mutex_unlock(&myrpt->lock);
- return DC_COMPLETE;
- }
- myrpt->callmode = 1;
- myrpt->cidx = 0;
- myrpt->exten[myrpt->cidx] = 0;
- rpt_mutex_unlock(&myrpt->lock);
- ast_pthread_create_detached(&myrpt->rpt_call_thread, NULL, rpt_call, (void *) myrpt);
- return DC_COMPLETE;
-}
-
-/*
-* Autopatch down
-*/
-
-static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
-{
- if (!myrpt->enable)
- return DC_ERROR;
-
- ast_debug(1, "@@@@ Autopatch down\n");
-
- rpt_mutex_lock(&myrpt->lock);
-
- if (!myrpt->callmode) {
- rpt_mutex_unlock(&myrpt->lock);
- return DC_COMPLETE;
- }
-
- myrpt->callmode = 0;
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, TERM, NULL);
- return DC_COMPLETE;
-}
-
-/*
-* Status
-*/
-
-static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
-{
-
- if (!param)
- return DC_ERROR;
-
- if (!myrpt->enable)
- return DC_ERROR;
-
- ast_debug(1, "@@@@ status param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
-
- switch (myatoi(param)) {
- case 1: /* System ID */
- rpt_telemetry(myrpt, ID1, NULL);
- return DC_COMPLETE;
- case 2: /* System Time */
- rpt_telemetry(myrpt, STATS_TIME, NULL);
- return DC_COMPLETE;
- case 3: /* app_rpt.c version */
- rpt_telemetry(myrpt, STATS_VERSION, NULL);
- default:
- return DC_ERROR;
- }
-
- /* Never reached */
- return DC_INDETERMINATE;
-}
-
-/*
-* Macro-oni (without Salami)
-*/
-
-static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
-{
-
- const char *val;
- int i;
- struct ast_channel *mychannel;
-
- if ((!myrpt->remote) && (!myrpt->enable))
- return DC_ERROR;
-
- ast_debug(1, "@@@@ macro-oni param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
-
- mychannel = myrpt->remchannel;
-
- if (ast_strlen_zero(digitbuf)) /* needs 1 digit */
- return DC_INDETERMINATE;
-
- for (i = 0; i < digitbuf[i]; i++) {
- if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
- return DC_ERROR;
- }
-
- if (*digitbuf == '0')
- val = myrpt->p.startupmacro;
- else
- val = ast_variable_retrieve(myrpt->cfg, myrpt->p.macro, digitbuf);
- /* param was 1 for local buf */
- if (!val) {
- rpt_telemetry(myrpt, MACRO_NOTFOUND, NULL);
- return DC_COMPLETE;
- }
- rpt_mutex_lock(&myrpt->lock);
- if ((sizeof(myrpt->macrobuf) - strlen(myrpt->macrobuf)) < strlen(val)) {
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, MACRO_BUSY, NULL);
- return DC_ERROR;
- }
- myrpt->macrotimer = MACROTIME;
- strncat(myrpt->macrobuf, val, sizeof(myrpt->macrobuf) - 1);
- rpt_mutex_unlock(&myrpt->lock);
- return DC_COMPLETE;
-}
-
-/*
-* Gosub
-*/
-
-static int function_gosub(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
-{
-
- const char *val;
- int i;
- struct ast_channel *mychannel;
-
- if ((!myrpt->remote) && (!myrpt->enable))
- return DC_ERROR;
-
- if (debug)
- ast_log(LOG_DEBUG, "@@@@ gosub param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
-
- mychannel = myrpt->remchannel;
-
- if (ast_strlen_zero(digitbuf)) /* needs 1 digit */
- return DC_INDETERMINATE;
-
- for (i = 0; i < digitbuf[i]; i++) {
- if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
- return DC_ERROR;
- }
-
- if (*digitbuf == '0')
- val = myrpt->p.startupgosub;
- else
- val = ast_variable_retrieve(myrpt->cfg, myrpt->p.gosub, digitbuf);
- /* param was 1 for local buf */
- if (!val) {
- rpt_telemetry(myrpt, GOSUB_NOTFOUND, NULL);
- return DC_COMPLETE;
- }
- rpt_mutex_lock(&myrpt->lock);
- if ((sizeof(myrpt->gosubbuf) - strlen(myrpt->gosubbuf)) < strlen(val)) {
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, GOSUB_BUSY, NULL);
- return DC_ERROR;
- }
- myrpt->gosubtimer = GOSUBTIME;
- strncat(myrpt->gosubbuf, val, sizeof(myrpt->gosubbuf) - 1);
- rpt_mutex_unlock(&myrpt->lock);
- return DC_COMPLETE;
-}
-
-/*
-* COP - Control operator
-*/
-
-static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
-{
- if (!param)
- return DC_ERROR;
-
- switch(myatoi(param)) {
- case 1: /* System reset */
- ast_cli_command(STDERR_FILENO, "restart now"); /* A little less drastic than what was previously here. */
- return DC_COMPLETE;
- case 2:
- myrpt->enable = 1;
- rpt_telemetry(myrpt, ARB_ALPHA, (void *) "RPTENA");
- return DC_COMPLETE;
- case 3:
- myrpt->enable = 0;
- return DC_COMPLETE;
- case 4: /* test tone on */
- rpt_telemetry(myrpt, TEST_TONE, NULL);
- return DC_COMPLETE;
- case 5: /* Disgorge variables to log for debug purposes */
- myrpt->disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */
- return DC_COMPLETE;
- case 6: /* Simulate COR being activated (phone only) */
- if (command_source != SOURCE_PHONE)
- return DC_INDETERMINATE;
- return DC_DOKEY;
- }
- return DC_INDETERMINATE;
-}
-
-/*
-* Collect digits one by one until something matches
-*/
-
-static int collect_function_digits(struct rpt *myrpt, char *digits, int command_source, struct rpt_link *mylink)
-{
- int i;
- char *stringp, *functiondigits;
- char function_table_name[30] = "";
- struct ast_variable *vp;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(action);
- AST_APP_ARG(param);
- );
-
- ast_debug(1, "@@@@ Digits collected: %s, source: %d\n", digits, command_source);
-
- if (command_source == SOURCE_DPHONE) {
- if (!myrpt->p.dphone_functions)
- return DC_INDETERMINATE;
- ast_copy_string(function_table_name, myrpt->p.dphone_functions, sizeof(function_table_name));
- } else if (command_source == SOURCE_PHONE) {
- if (!myrpt->p.phone_functions)
- return DC_INDETERMINATE;
- ast_copy_string(function_table_name, myrpt->p.phone_functions, sizeof(function_table_name));
- } else if (command_source == SOURCE_LNK)
- ast_copy_string(function_table_name, myrpt->p.link_functions, sizeof(function_table_name));
- else
- ast_copy_string(function_table_name, myrpt->p.functions, sizeof(function_table_name));
-
- for (vp = ast_variable_browse(myrpt->cfg, function_table_name); vp; vp = vp->next) {
- if (!strncasecmp(vp->name, digits, strlen(vp->name)))
- break;
- }
- if (!vp) {
- int n;
-
- n = myrpt->longestfunc;
- if (command_source == SOURCE_LNK)
- n = myrpt->link_longestfunc;
- else if (command_source == SOURCE_PHONE)
- n = myrpt->phone_longestfunc;
- else if (command_source == SOURCE_DPHONE)
- n = myrpt->dphone_longestfunc;
-
- if (strlen(digits) >= n)
- return DC_ERROR;
- else
- return DC_INDETERMINATE;
- }
-
- /* Found a match, retrieve value part and parse */
- stringp = ast_strdupa(vp->value);
- AST_STANDARD_APP_ARGS(args, stringp);
-
- ast_debug(1, "@@@@ action: %s, param = %s\n", args.action, S_OR(args.param, "(null)"));
- /* Look up the action */
- for (i = 0; i < (sizeof(function_table) / sizeof(struct function_table_tag)); i++) {
- if (!strncasecmp(args.action, function_table[i].action, strlen(args.action)))
- break;
- }
- ast_debug(1, "@@@@ table index i = %d\n", i);
- if (i == (sizeof(function_table) / sizeof(struct function_table_tag))) {
- /* Error, action not in table */
- return DC_ERROR;
- }
- if (function_table[i].function == NULL) {
- /* Error, function undefined */
- ast_debug(1, "@@@@ NULL for action: %s\n", args.action);
- return DC_ERROR;
- }
- functiondigits = digits + strlen(vp->name);
- return (*function_table[i].function)(myrpt, args.param, functiondigits, command_source, mylink);
-}
-
-
-static void handle_link_data(struct rpt *myrpt, struct rpt_link *mylink, char *str)
-{
- char cmd[300] = "", dest[300], src[300], c;
- int seq, res;
- struct rpt_link *l;
- struct ast_frame wf;
-
- wf.frametype = AST_FRAME_TEXT;
- wf.subclass = 0;
- wf.offset = 0;
- wf.mallocd = 1;
- wf.datalen = strlen(str) + 1;
- wf.samples = 0;
- if (!strcmp(str, discstr)) {
- mylink->disced = 1;
- mylink->retries = MAX_RETRIES + 1;
- ast_softhangup(mylink->chan, AST_SOFTHANGUP_DEV);
- return;
- }
- if (sscanf(str, "%s %s %s %d %c", cmd, dest, src, &seq, &c) != 5) {
- ast_log(LOG_WARNING, "Unable to parse link string %s\n", str);
- return;
- }
- if (strcmp(cmd, "D")) {
- ast_log(LOG_WARNING, "Unable to parse link string %s\n", str);
- return;
- }
-
- if (dest[0] == '0') {
- strcpy(dest, myrpt->name);
- }
-
- /* if not for me, redistribute to all links */
- if (strcmp(dest, myrpt->name)) {
- l = myrpt->links.next;
- /* see if this is one in list */
- while (l != &myrpt->links) {
- if (l->name[0] == '0') {
- l = l->next;
- continue;
- }
- /* dont send back from where it came */
- if ((l == mylink) || (!strcmp(l->name, mylink->name))) {
- l = l->next;
- continue;
- }
- /* if it is, send it and we're done */
- if (!strcmp(l->name, dest)) {
- /* send, but not to src */
- if (strcmp(l->name, src)) {
- wf.data = ast_strdup(str);
- if (l->chan)
- ast_write(l->chan, &wf);
- }
- return;
- }
- l = l->next;
- }
- l = myrpt->links.next;
- /* otherwise, send it to all of em */
- while (l != &myrpt->links) {
- if (l->name[0] == '0') {
- l = l->next;
- continue;
- }
- /* dont send back from where it came */
- if ((l == mylink) || (!strcmp(l->name, mylink->name))) {
- l = l->next;
- continue;
- }
- /* send, but not to src */
- if (strcmp(l->name, src)) {
- wf.data = ast_strdup(str);
- if (l->chan)
- ast_write(l->chan, &wf);
- }
- l = l->next;
- }
- return;
- }
- rpt_mutex_lock(&myrpt->lock);
- if (c == myrpt->p.endchar)
- myrpt->stopgen = 1;
- if (myrpt->callmode == 1) {
- myrpt->exten[myrpt->cidx++] = c;
- myrpt->exten[myrpt->cidx] = 0;
- /* if this exists */
- if (ast_exists_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
- myrpt->callmode = 2;
- if (!myrpt->patchquiet) {
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, PROC, NULL);
- rpt_mutex_lock(&myrpt->lock);
- }
- }
- /* if can continue, do so */
- if (!ast_canmatch_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
- /* call has failed, inform user */
- myrpt->callmode = 4;
- }
- }
- if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) {
- myrpt->mydtmf = c;
- }
- if (c == myrpt->p.funcchar) {
- myrpt->rem_dtmfidx = 0;
- myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
- time(&myrpt->rem_dtmf_time);
- rpt_mutex_unlock(&myrpt->lock);
- return;
- } else if ((c != myrpt->p.endchar) && (myrpt->rem_dtmfidx >= 0)) {
- time(&myrpt->rem_dtmf_time);
- if (myrpt->rem_dtmfidx < MAXDTMF) {
- myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c;
- myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
-
- rpt_mutex_unlock(&myrpt->lock);
- ast_copy_string(cmd, myrpt->rem_dtmfbuf, sizeof(cmd));
- res = collect_function_digits(myrpt, cmd, SOURCE_LNK, mylink);
- rpt_mutex_lock(&myrpt->lock);
-
- switch (res) {
- case DC_INDETERMINATE:
- break;
- case DC_REQ_FLUSH:
- myrpt->rem_dtmfidx = 0;
- myrpt->rem_dtmfbuf[0] = 0;
- break;
- case DC_COMPLETE:
- myrpt->totalexecdcommands++;
- myrpt->dailyexecdcommands++;
- ast_copy_string(myrpt->lastdtmfcommand, cmd, MAXDTMF);
- myrpt->lastdtmfcommand[MAXDTMF-1] = '\0';
- myrpt->rem_dtmfbuf[0] = 0;
- myrpt->rem_dtmfidx = -1;
- myrpt->rem_dtmf_time = 0;
- break;
- case DC_ERROR:
- default:
- myrpt->rem_dtmfbuf[0] = 0;
- myrpt->rem_dtmfidx = -1;
- myrpt->rem_dtmf_time = 0;
- break;
- }
- }
-
- }
- rpt_mutex_unlock(&myrpt->lock);
- return;
-}
-
-static void handle_link_phone_dtmf(struct rpt *myrpt, struct rpt_link *mylink, char c)
-{
- char cmd[300];
- int res;
-
- rpt_mutex_lock(&myrpt->lock);
- if (c == myrpt->p.endchar) {
- if (mylink->lastrx) {
- mylink->lastrx = 0;
- rpt_mutex_unlock(&myrpt->lock);
- return;
- }
- myrpt->stopgen = 1;
- if (myrpt->cmdnode[0]) {
- myrpt->cmdnode[0] = 0;
- myrpt->dtmfidx = -1;
- myrpt->dtmfbuf[0] = 0;
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, COMPLETE, NULL);
- return;
- }
- }
- if (myrpt->cmdnode[0]) {
- rpt_mutex_unlock(&myrpt->lock);
- send_link_dtmf(myrpt, c);
- return;
- }
- if (myrpt->callmode == 1) {
- myrpt->exten[myrpt->cidx++] = c;
- myrpt->exten[myrpt->cidx] = 0;
- /* if this exists */
- if (ast_exists_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
- myrpt->callmode = 2;
- if (!myrpt->patchquiet) {
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, PROC, NULL);
- rpt_mutex_lock(&myrpt->lock);
- }
- }
- /* if can continue, do so */
- if (!ast_canmatch_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
- /* call has failed, inform user */
- myrpt->callmode = 4;
- }
- }
- if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) {
- myrpt->mydtmf = c;
- }
- if (c == myrpt->p.funcchar) {
- myrpt->rem_dtmfidx = 0;
- myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
- time(&myrpt->rem_dtmf_time);
- rpt_mutex_unlock(&myrpt->lock);
- return;
- } else if ((c != myrpt->p.endchar) && (myrpt->rem_dtmfidx >= 0)) {
- time(&myrpt->rem_dtmf_time);
- if (myrpt->rem_dtmfidx < MAXDTMF) {
- myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c;
- myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
-
- rpt_mutex_unlock(&myrpt->lock);
- ast_copy_string(cmd, myrpt->rem_dtmfbuf, sizeof(cmd));
- switch(mylink->phonemode) {
- case 1:
- res = collect_function_digits(myrpt, cmd,
- SOURCE_PHONE, mylink);
- break;
- case 2:
- res = collect_function_digits(myrpt, cmd,
- SOURCE_DPHONE, mylink);
- break;
- default:
- res = collect_function_digits(myrpt, cmd,
- SOURCE_LNK, mylink);
- break;
- }
-
- rpt_mutex_lock(&myrpt->lock);
-
- switch(res) {
- case DC_INDETERMINATE:
- break;
- case DC_DOKEY:
- mylink->lastrx = 1;
- break;
- case DC_REQ_FLUSH:
- myrpt->rem_dtmfidx = 0;
- myrpt->rem_dtmfbuf[0] = 0;
- break;
- case DC_COMPLETE:
- myrpt->totalexecdcommands++;
- myrpt->dailyexecdcommands++;
- ast_copy_string(myrpt->lastdtmfcommand, cmd, MAXDTMF);
- myrpt->rem_dtmfbuf[0] = 0;
- myrpt->rem_dtmfidx = -1;
- myrpt->rem_dtmf_time = 0;
- break;
- case DC_ERROR:
- default:
- myrpt->rem_dtmfbuf[0] = 0;
- myrpt->rem_dtmfidx = -1;
- myrpt->rem_dtmf_time = 0;
- break;
- }
- }
-
- }
- rpt_mutex_unlock(&myrpt->lock);
- return;
-}
-
-/* Doug Hall RBI-1 serial data definitions:
- *
- * Byte 0: Expansion external outputs
- * Byte 1:
- * Bits 0-3 are BAND as follows:
- * Bits 4-5 are POWER bits as follows:
- * 00 - Low Power
- * 01 - Hi Power
- * 02 - Med Power
- * Bits 6-7 are always set
- * Byte 2:
- * Bits 0-3 MHZ in BCD format
- * Bits 4-5 are offset as follows:
- * 00 - minus
- * 01 - plus
- * 02 - simplex
- * 03 - minus minus (whatever that is)
- * Bit 6 is the 0/5 KHZ bit
- * Bit 7 is always set
- * Byte 3:
- * Bits 0-3 are 10 KHZ in BCD format
- * Bits 4-7 are 100 KHZ in BCD format
- * Byte 4: PL Tone code and encode/decode enable bits
- * Bits 0-5 are PL tone code (comspec binary codes)
- * Bit 6 is encode enable/disable
- * Bit 7 is decode enable/disable
- */
-
-/* take the frequency from the 10 mhz digits (and up) and convert it
- to a band number */
-
-static int rbi_mhztoband(char *str)
-{
- int i;
-
- i = atoi(str) / 10; /* get the 10's of mhz */
- switch (i) {
- case 2:
- return 10;
- case 5:
- return 11;
- case 14:
- return 2;
- case 22:
- return 3;
- case 44:
- return 4;
- case 124:
- return 0;
- case 125:
- return 1;
- case 126:
- return 8;
- case 127:
- return 5;
- case 128:
- return 6;
- case 129:
- return 7;
- default:
- break;
- }
- return -1;
-}
-
-/* take a PL frequency and turn it into a code */
-static int rbi_pltocode(char *str)
-{
- int i;
- char *s;
-
- s = strchr(str, '.');
- i = 0;
- if (s)
- i = atoi(s + 1);
- i += atoi(str) * 10;
- switch(i) {
- case 670:
- return 0;
- case 719:
- return 1;
- case 744:
- return 2;
- case 770:
- return 3;
- case 797:
- return 4;
- case 825:
- return 5;
- case 854:
- return 6;
- case 885:
- return 7;
- case 915:
- return 8;
- case 948:
- return 9;
- case 974:
- return 10;
- case 1000:
- return 11;
- case 1035:
- return 12;
- case 1072:
- return 13;
- case 1109:
- return 14;
- case 1148:
- return 15;
- case 1188:
- return 16;
- case 1230:
- return 17;
- case 1273:
- return 18;
- case 1318:
- return 19;
- case 1365:
- return 20;
- case 1413:
- return 21;
- case 1462:
- return 22;
- case 1514:
- return 23;
- case 1567:
- return 24;
- case 1622:
- return 25;
- case 1679:
- return 26;
- case 1738:
- return 27;
- case 1799:
- return 28;
- case 1862:
- return 29;
- case 1928:
- return 30;
- case 2035:
- return 31;
- case 2107:
- return 32;
- case 2181:
- return 33;
- case 2257:
- return 34;
- case 2336:
- return 35;
- case 2418:
- return 36;
- case 2503:
- return 37;
- }
- return -1;
-}
-
-/*
-* Shift out a formatted serial bit stream
-*/
-
-static void rbi_out_parallel(struct rpt *myrpt, unsigned char *data)
-{
- int i, j;
- unsigned char od, d;
-
- for (i = 0; i < 5; i++) {
- od = *data++;
- for (j = 0; j < 8; j++) {
- d = od & 1;
- outb(d, myrpt->p.iobase);
- usleep(15);
- od >>= 1;
- outb(d | 2, myrpt->p.iobase);
- usleep(30);
- outb(d, myrpt->p.iobase);
- usleep(10);
- }
- }
- /* >= 50 us */
- usleep(50);
-}
-
-static void rbi_out(struct rpt *myrpt, unsigned char *data)
-{
- struct zt_radio_param r = { 0, };
-
- r.radpar = ZT_RADPAR_REMMODE;
- r.data = ZT_RADPAR_REM_RBI1;
- /* if setparam ioctl fails, its probably not a pciradio card */
- if (ioctl(myrpt->rxchannel->fds[0], ZT_RADIO_SETPARAM, &r) == -1) {
- rbi_out_parallel(myrpt, data);
- return;
- }
- r.radpar = ZT_RADPAR_REMCOMMAND;
- memcpy(&r.data, data, 5);
- if (ioctl(myrpt->rxchannel->fds[0], ZT_RADIO_SETPARAM, &r) == -1) {
- ast_log(LOG_WARNING, "Cannot send RBI command for channel %s\n", myrpt->rxchannel->name);
- return;
- }
-}
-
-static int serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int txbytes, char *rxbuf, int rxmaxbytes, int asciiflag)
-{
- int i;
- struct zt_radio_param prm;
-
- char *buf = alloca(30 + txbytes * 3);
- int len;
- ast_copy_string(buf, "String output was: ", 30 + txbytes * 3);
- len = strlen(buf);
- for (i = 0; i < txbytes; i++)
- len += snprintf(buf + len, 30 + txbytes * 3 - len, "%02X ", (unsigned char) txbuf[i]);
- strcat(buf + len, "\n");
- ast_debug(1, "%s", buf);
-
- prm.radpar = ZT_RADPAR_REMMODE;
- if (asciiflag)
- prm.data = ZT_RADPAR_REM_SERIAL_ASCII;
- else
- prm.data = ZT_RADPAR_REM_SERIAL;
- if (ioctl(myrpt->rxchannel->fds[0], ZT_RADIO_SETPARAM, &prm) == -1)
- return -1;
- prm.radpar = ZT_RADPAR_REMCOMMAND;
- prm.data = rxmaxbytes;
- memcpy(prm.buf, txbuf, txbytes);
- prm.index = txbytes;
- if (ioctl(myrpt->rxchannel->fds[0], ZT_RADIO_SETPARAM, &prm) == -1)
- return -1;
- if (rxbuf) {
- *rxbuf = 0;
- memcpy(rxbuf, prm.buf, prm.index);
- }
- return(prm.index);
-}
-
-static int setrbi(struct rpt *myrpt)
-{
- char tmp[MAXREMSTR] = "", *s;
- unsigned char rbicmd[5];
- int band, txoffset = 0, txpower = 0, txpl;
-
- /* must be a remote system */
- if (!myrpt->remote)
- return(0);
- /* must have rbi hardware */
- if (strncmp(myrpt->remote, remote_rig_rbi, 3))
- return(0);
- ast_copy_string(tmp, myrpt->freq, sizeof(tmp));
- s = strchr(tmp, '.');
- /* if no decimal, is invalid */
-
- if (s == NULL) {
- ast_debug(1, "@@@@ Frequency needs a decimal\n");
- return -1;
- }
-
- *s++ = 0;
- if (strlen(tmp) < 2) {
- ast_debug(1, "@@@@ Bad MHz digits: %s\n", tmp);
- return -1;
- }
-
- if (strlen(s) < 3) {
- ast_debug(1, "@@@@ Bad KHz digits: %s\n", s);
- return -1;
- }
-
- if ((s[2] != '0') && (s[2] != '5')) {
- ast_debug(1, "@@@@ KHz must end in 0 or 5: %c\n", s[2]);
- return -1;
- }
-
- band = rbi_mhztoband(tmp);
- if (band == -1) {
- ast_debug(1, "@@@@ Bad Band: %s\n", tmp);
- return -1;
- }
-
- txpl = rbi_pltocode(myrpt->txpl);
-
- if (txpl == -1) {
- ast_debug(1, "@@@@ Bad TX PL: %s\n", myrpt->txpl);
- return -1;
- }
-
-
- switch (myrpt->offset) {
- case REM_MINUS:
- txoffset = 0;
- break;
- case REM_PLUS:
- txoffset = 0x10;
- break;
- case REM_SIMPLEX:
- txoffset = 0x20;
- break;
- }
- switch(myrpt->powerlevel) {
- case REM_LOWPWR:
- txpower = 0;
- break;
- case REM_MEDPWR:
- txpower = 0x20;
- break;
- case REM_HIPWR:
- txpower = 0x10;
- break;
- }
- rbicmd[0] = 0;
- rbicmd[1] = band | txpower | 0xc0;
- rbicmd[2] = (*(s - 2) - '0') | txoffset | 0x80;
- if (s[2] == '5')
- rbicmd[2] |= 0x40;
- rbicmd[3] = ((*s - '0') << 4) + (s[1] - '0');
- rbicmd[4] = txpl;
- if (myrpt->txplon)
- rbicmd[4] |= 0x40;
- if (myrpt->rxplon)
- rbicmd[4] |= 0x80;
- rbi_out(myrpt, rbicmd);
- return 0;
-}
-
-
-/* Check for valid rbi frequency */
-/* Hard coded limits now, configurable later, maybe? */
-
-static int check_freq_rbi(int m, int d, int *defmode)
-{
- int dflmd = REM_MODE_FM;
-
- if (m == 50) { /* 6 meters */
- if (d < 10100)
- return -1;
- } else if ((m >= 51) && ( m < 54)) {
- /* nada */
- } else if (m == 144) { /* 2 meters */
- if (d < 10100)
- return -1;
- } else if ((m >= 145) && (m < 148)) {
- /* nada */
- } else if ((m >= 222) && (m < 225)) { /* 1.25 meters */
- /* nada */
- } else if ((m >= 430) && (m < 450)) { /* 70 centimeters */
- /* nada */
- } else if ((m >= 1240) && (m < 1300)) { /* 23 centimeters */
- /* nada */
- } else
- return -1;
-
- if (defmode)
- *defmode = dflmd;
-
- return 0;
-}
-
-static int split_decimal(char *input, int *ints, int *decs, int places)
-{
- double input2 = 0.0;
- long long modifier = (long long)pow(10.0, (double)places);
- if (sscanf(input, "%lf", &input2) == 1) {
- long long input3 = input2 * modifier;
- *ints = input3 / modifier;
- *decs = input3 % modifier;
- return 0;
- } else
- return -1;
-}
-
-/*
-* Split frequency into mhz and decimals
-*/
-
-#define split_freq(mhz, decimal, freq) split_decimal(freq, mhz, decimal, 5)
-
-/*
-* Split ctcss frequency into hertz and decimal
-*/
-
-#define split_ctcss_freq(hertz, decimal, freq) split_decimal(freq, hertz, decimal, 1)
-
-/*
-* FT-897 I/O handlers
-*/
-
-/* Check to see that the frequency is valid */
-/* Hard coded limits now, configurable later, maybe? */
-
-static int check_freq_ft897(int m, int d, int *defmode)
-{
- int dflmd = REM_MODE_FM;
-
- if (m == 1) { /* 160 meters */
- dflmd = REM_MODE_LSB;
- if (d < 80001)
- return -1;
- } else if (m == 3) { /* 80 meters */
- dflmd = REM_MODE_LSB;
- if (d < 75001)
- return -1;
- } else if (m == 7) { /* 40 meters */
- dflmd = REM_MODE_LSB;
- if ((d < 15001) || (d > 29999))
- return -1;
- } else if (m == 14) { /* 20 meters */
- dflmd = REM_MODE_USB;
- if ((d < 15001) || (d > 34999))
- return -1;
- } else if (m == 18) { /* 17 meters */
- dflmd = REM_MODE_USB;
- if ((d < 11001) || (d > 16797))
- return -1;
- } else if (m == 21) { /* 15 meters */
- dflmd = REM_MODE_USB;
- if ((d < 20001) || (d > 44999))
- return -1;
- } else if (m == 24) { /* 12 meters */
- dflmd = REM_MODE_USB;
- if ((d < 93001) || (d > 98999))
- return -1;
- } else if (m == 28) { /* 10 meters */
- dflmd = REM_MODE_USB;
- if (d < 30001)
- return -1;
- } else if (m == 29) {
- if (d >= 51000)
- dflmd = REM_MODE_FM;
- else
- dflmd = REM_MODE_USB;
- if (d > 69999)
- return -1;
- } else if (m == 50) { /* 6 meters */
- if (d < 10100)
- return -1;
- if (d >= 30000)
- dflmd = REM_MODE_FM;
- else
- dflmd = REM_MODE_USB;
- } else if ((m >= 51) && ( m < 54)) {
- dflmd = REM_MODE_FM;
- } else if (m == 144) { /* 2 meters */
- if (d < 10100)
- return -1;
- if (d >= 30000)
- dflmd = REM_MODE_FM;
- else
- dflmd = REM_MODE_USB;
- } else if ((m >= 145) && (m < 148)) {
- dflmd = REM_MODE_FM;
- } else if ((m >= 430) && (m < 450)) { /* 70 centimeters */
- if (m < 438)
- dflmd = REM_MODE_USB;
- else
- dflmd = REM_MODE_FM;
- ;
- } else
- return -1;
-
- if (defmode)
- *defmode = dflmd;
-
- return 0;
-}
-
-/*
-* Set a new frequency for the FT897
-*/
-
-static int set_freq_ft897(struct rpt *myrpt, char *newfreq)
-{
- unsigned char cmdstr[5];
- int fd, m, d;
-
- fd = 0;
- ast_debug(1, "New frequency: %s\n", newfreq);
-
- if (split_freq(&m, &d, newfreq))
- return -1;
-
- /* The FT-897 likes packed BCD frequencies */
-
- cmdstr[0] = ((m / 100) << 4) + ((m % 100) / 10); /* 100MHz 10Mhz */
- cmdstr[1] = ((m % 10) << 4) + (d / 10000); /* 1MHz 100KHz */
- cmdstr[2] = (((d % 10000) / 1000) << 4) + ((d % 1000) / 100); /* 10KHz 1KHz */
- cmdstr[3] = (((d % 100) / 10) << 4) + (d % 10); /* 100Hz 10Hz */
- cmdstr[4] = 0x01; /* command */
-
- return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
-}
-
-/* ft-897 simple commands */
-
-static int simple_command_ft897(struct rpt *myrpt, char command)
-{
- unsigned char cmdstr[5] = { 0, 0, 0, 0, command };
-
- return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
-}
-
-/* ft-897 offset */
-
-static int set_offset_ft897(struct rpt *myrpt, char offset)
-{
- unsigned char cmdstr[5] = "";
-
- switch (offset) {
- case REM_SIMPLEX:
- cmdstr[0] = 0x89;
- break;
- case REM_MINUS:
- cmdstr[0] = 0x09;
- break;
- case REM_PLUS:
- cmdstr[0] = 0x49;
- break;
- default:
- return -1;
- }
-
- cmdstr[4] = 0x09;
-
- return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
-}
-
-/* ft-897 mode */
-
-static int set_mode_ft897(struct rpt *myrpt, char newmode)
-{
- unsigned char cmdstr[5] = { 0, 0, 0, 0, 0x07 };
-
- switch (newmode) {
- case REM_MODE_FM:
- cmdstr[0] = 0x08;
- break;
- case REM_MODE_USB:
- cmdstr[0] = 0x01;
- break;
- case REM_MODE_LSB:
- cmdstr[0] = 0x00;
- break;
- case REM_MODE_AM:
- cmdstr[0] = 0x04;
- break;
- default:
- return -1;
- }
-
- return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
-}
-
-/* Set tone encode and decode modes */
-
-static int set_ctcss_mode_ft897(struct rpt *myrpt, char txplon, char rxplon)
-{
- unsigned char cmdstr[5] = { 0, 0, 0, 0, 0x0A };
-
- if (rxplon && txplon)
- cmdstr[0] = 0x2A; /* Encode and Decode */
- else if (!rxplon && txplon)
- cmdstr[0] = 0x4A; /* Encode only */
- else if (rxplon && !txplon)
- cmdstr[0] = 0x3A; /* Encode only */
- else
- cmdstr[0] = 0x8A; /* OFF */
-
- return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
-}
-
-
-/* Set transmit and receive ctcss tone frequencies */
-
-static int set_ctcss_freq_ft897(struct rpt *myrpt, char *txtone, char *rxtone)
-{
- unsigned char cmdstr[5] = { 0, 0, 0, 0, 0x0B };
- int hertz, decimal;
-
- if (split_ctcss_freq(&hertz, &decimal, txtone))
- return -1;
-
- cmdstr[0] = ((hertz / 100) << 4) + (hertz % 100) / 10;
- cmdstr[1] = ((hertz % 10) << 4) + (decimal % 10);
-
- if (rxtone) {
- if (split_ctcss_freq(&hertz, &decimal, rxtone))
- return -1;
-
- cmdstr[2] = ((hertz / 100) << 4) + (hertz % 100)/ 10;
- cmdstr[3] = ((hertz % 10) << 4) + (decimal % 10);
- }
-
- return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
-}
-
-
-
-static int set_ft897(struct rpt *myrpt)
-{
- int res;
-
- ast_debug(1, "@@@@ lock on\n");
-
- res = simple_command_ft897(myrpt, 0x00); /* LOCK on */
-
- ast_debug(1, "@@@@ ptt off\n");
-
- if (!res)
- res = simple_command_ft897(myrpt, 0x88); /* PTT off */
-
- ast_debug(1, "Modulation mode\n");
-
- if (!res)
- res = set_mode_ft897(myrpt, myrpt->remmode); /* Modulation mode */
-
- ast_debug(1, "Split off\n");
-
- if (!res)
- simple_command_ft897(myrpt, 0x82); /* Split off */
-
- ast_debug(1, "Frequency\n");
-
- if (!res)
- res = set_freq_ft897(myrpt, myrpt->freq); /* Frequency */
- if ((myrpt->remmode == REM_MODE_FM)) {
- ast_debug(1, "Offset\n");
- if (!res)
- res = set_offset_ft897(myrpt, myrpt->offset); /* Offset if FM */
- if ((!res)&&(myrpt->rxplon || myrpt->txplon)) {
- ast_debug(1, "CTCSS tone freqs.\n");
- res = set_ctcss_freq_ft897(myrpt, myrpt->txpl, myrpt->rxpl); /* CTCSS freqs if CTCSS is enabled */
- }
- if (!res) {
- ast_debug(1, "CTCSS mode\n");
- res = set_ctcss_mode_ft897(myrpt, myrpt->txplon, myrpt->rxplon); /* CTCSS mode */
- }
- }
- if ((myrpt->remmode == REM_MODE_USB)||(myrpt->remmode == REM_MODE_LSB)) {
- ast_debug(1, "Clarifier off\n");
- simple_command_ft897(myrpt, 0x85); /* Clarifier off if LSB or USB */
- }
- return res;
-}
-
-static int closerem_ft897(struct rpt *myrpt)
-{
- simple_command_ft897(myrpt, 0x88); /* PTT off */
- return 0;
-}
-
-/*
-* Bump frequency up or down by a small amount
-* Return 0 if the new frequnecy is valid, or -1 if invalid
-* Interval is in Hz, resolution is 10Hz
-*/
-
-static int multimode_bump_freq_ft897(struct rpt *myrpt, int interval)
-{
- int m, d;
-
- ast_debug(1, "Before bump: %s\n", myrpt->freq);
-
- if (split_freq(&m, &d, myrpt->freq))
- return -1;
-
- d += (interval / 10); /* 10Hz resolution */
- if (d < 0) {
- m--;
- d += 100000;
- } else if (d >= 100000) {
- m++;
- d -= 100000;
- }
-
- if (check_freq_ft897(m, d, NULL)) {
- ast_debug(1, "Bump freq invalid\n");
- return -1;
- }
-
- snprintf(myrpt->freq, MAXREMSTR, "%d.%05d", m, d);
- ast_debug(1, "After bump: %s\n", myrpt->freq);
-
- return set_freq_ft897(myrpt, myrpt->freq);
-}
-
-
-
-/*
-* Dispatch to correct I/O handler
-*/
-
-static int setrem(struct rpt *myrpt)
-{
- return 0; /* XXX BROKEN!! */
- if (!strcmp(myrpt->remote, remote_rig_ft897))
- return set_ft897(myrpt);
- else if (!strcmp(myrpt->remote, remote_rig_rbi))
- return setrbi(myrpt);
- else
- return -1;
-}
-
-static int closerem(struct rpt *myrpt)
-{
- return 0; /* XXX BROKEN!! */
- if (!strcmp(myrpt->remote, remote_rig_ft897))
- return closerem_ft897(myrpt);
- else
- return 0;
-}
-
-/*
-* Dispatch to correct frequency checker
-*/
-
-static int check_freq(struct rpt *myrpt, int m, int d, int *defmode)
-{
- if (!strcmp(myrpt->remote, remote_rig_ft897))
- return check_freq_ft897(m, d, defmode);
- else if (!strcmp(myrpt->remote, remote_rig_rbi))
- return check_freq_rbi(m, d, defmode);
- else
- return -1;
-}
-
-/*
-* Return 1 if rig is multimode capable
-*/
-
-static int multimode_capable(struct rpt *myrpt)
-{
- if (!strcmp(myrpt->remote, remote_rig_ft897))
- return 1;
- return 0;
-}
-
-/*
-* Dispatch to correct frequency bumping function
-*/
-
-static int multimode_bump_freq(struct rpt *myrpt, int interval)
-{
- if (!strcmp(myrpt->remote, remote_rig_ft897))
- return multimode_bump_freq_ft897(myrpt, interval);
- else
- return -1;
-}
-
-
-/*
-* Queue announcment that scan has been stopped
-*/
-
-static void stop_scan(struct rpt *myrpt, int flag)
-{
- myrpt->hfscanmode = 0;
- myrpt->hfscanstatus = ((flag) ? -2 : -1);
-}
-
-/*
-* This is called periodically when in scan mode
-*/
-
-static int service_scan(struct rpt *myrpt)
-{
- int res, interval, mhz, decimals;
- char k10=0, k100=0;
-
- switch (myrpt->hfscanmode) {
-
- case HF_SCAN_DOWN_SLOW:
- interval = -10; /* 100Hz /sec */
- break;
-
- case HF_SCAN_DOWN_QUICK:
- interval = -50; /* 500Hz /sec */
- break;
-
- case HF_SCAN_DOWN_FAST:
- interval = -200; /* 2KHz /sec */
- break;
-
- case HF_SCAN_UP_SLOW:
- interval = 10; /* 100Hz /sec */
- break;
-
- case HF_SCAN_UP_QUICK:
- interval = 50; /* 500 Hz/sec */
- break;
-
- case HF_SCAN_UP_FAST:
- interval = 200; /* 2KHz /sec */
- break;
-
- default:
- myrpt->hfscanmode = 0; /* Huh? */
- return -1;
- }
-
- res = split_freq(&mhz, &decimals, myrpt->freq);
-
- if (!res) {
- k100 = decimals / 10000;
- k10 = (decimals / 1000) % 10;
- res = multimode_bump_freq(myrpt, interval);
- }
-
- if (!res)
- res = split_freq(&mhz, &decimals, myrpt->freq);
-
- if (res) {
- stop_scan(myrpt, 1);
- return -1;
- }
-
- /* Announce 10KHz boundaries */
- if (k10 != (decimals / 1000) % 10) {
- int myhund = (interval < 0) ? k100 : decimals / 10000;
- int myten = (interval < 0) ? k10 : (decimals / 1000) % 10;
- myrpt->hfscanstatus = (myten == 0) ? (myhund) * 100 : (myten) * 10;
- }
- return res;
-
-}
-
-
-static int rmt_telem_start(struct rpt *myrpt, struct ast_channel *chan, int delay)
-{
- myrpt->remotetx = 0;
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
- if (!myrpt->remoterx)
- ast_indicate(chan, AST_CONTROL_RADIO_KEY);
- if (ast_safe_sleep(chan, delay) == -1)
- return -1;
- return 0;
-}
-
-
-static int rmt_telem_finish(struct rpt *myrpt, struct ast_channel *chan)
-{
- struct zt_params par;
-
- if (ioctl(myrpt->txchannel->fds[0], ZT_GET_PARAMS, &par) == -1) {
- return -1;
-
- }
- if (!par.rxisoffhook) {
- ast_indicate(myrpt->remchannel, AST_CONTROL_RADIO_UNKEY);
- myrpt->remoterx = 0;
- } else {
- myrpt->remoterx = 1;
- }
- return 0;
-}
-
-
-static int rmt_sayfile(struct rpt *myrpt, struct ast_channel *chan, int delay, char *filename)
-{
- int res;
-
- res = rmt_telem_start(myrpt, chan, delay);
-
- if (!res)
- res = sayfile(chan, filename);
-
- if (!res)
- res = rmt_telem_finish(myrpt, chan);
- return res;
-}
-
-static int rmt_saycharstr(struct rpt *myrpt, struct ast_channel *chan, int delay, char *charstr)
-{
- int res;
-
- res = rmt_telem_start(myrpt, chan, delay);
-
- if (!res)
- res = saycharstr(chan, charstr);
-
- if (!res)
- res = rmt_telem_finish(myrpt, chan);
- return res;
-}
-
-/*
-* Remote base function
-*/
-
-static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
-{
- char *s, *modestr;
- const char *val;
- int i, j, ht, k, l, ls2, res, offset, offsave, modesave, defmode = 0;
- char multimode = 0;
- char oc;
- char tmp[20], freq[20] = "", savestr[20] = "";
- int mhz = 0, decimals = 0;
- struct ast_channel *mychannel;
- AST_DECLARE_APP_ARGS(args1,
- AST_APP_ARG(freq);
- AST_APP_ARG(xpl);
- AST_APP_ARG(mode);
- );
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(s1);
- AST_APP_ARG(s2);
- );
-
- if ((!param) || (command_source == SOURCE_RPT) || (command_source == SOURCE_LNK))
- return DC_ERROR;
-
- multimode = multimode_capable(myrpt);
- mychannel = myrpt->remchannel;
-
- switch (myatoi(param)) {
- case 1: /* retrieve memory */
- if (strlen(digitbuf) < 2) /* needs 2 digits */
- break;
-
- for (i = 0 ; i < 2 ; i++) {
- if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
- return DC_ERROR;
- }
-
- val = ast_variable_retrieve(myrpt->cfg, myrpt->p.memory, digitbuf);
- if (!val) {
- if (ast_safe_sleep(mychannel, 1000) == -1)
- return DC_ERROR;
- sayfile(mychannel, "rpt/memory_notfound");
- return DC_COMPLETE;
- }
- s = ast_strdupa(val);
- AST_STANDARD_APP_ARGS(args1, s);
- if (args1.argc < 3)
- return DC_ERROR;
- ast_copy_string(myrpt->freq, args1.freq, sizeof(myrpt->freq));
- ast_copy_string(myrpt->rxpl, args1.xpl, sizeof(myrpt->rxpl));
- ast_copy_string(myrpt->txpl, args1.xpl, sizeof(myrpt->rxpl));
- myrpt->remmode = REM_MODE_FM;
- myrpt->offset = REM_SIMPLEX;
- myrpt->powerlevel = REM_MEDPWR;
- myrpt->txplon = myrpt->rxplon = 0;
- modestr = args1.mode;
- while (*modestr) {
- switch (*modestr++) {
- case 'A':
- case 'a':
- strcpy(myrpt->rxpl, "100.0");
- strcpy(myrpt->txpl, "100.0");
- myrpt->remmode = REM_MODE_AM;
- break;
-
- case 'B':
- case 'b':
- strcpy(myrpt->rxpl, "100.0");
- strcpy(myrpt->txpl, "100.0");
- myrpt->remmode = REM_MODE_LSB;
- break;
-
- case 'F':
- myrpt->remmode = REM_MODE_FM;
- break;
-
- case 'L':
- case 'l':
- myrpt->powerlevel = REM_LOWPWR;
- break;
- case 'H':
- case 'h':
- myrpt->powerlevel = REM_HIPWR;
- break;
-
- case 'M':
- case 'm':
- myrpt->powerlevel = REM_MEDPWR;
- break;
-
- case '-':
- myrpt->offset = REM_MINUS;
- break;
-
- case '+':
- myrpt->offset = REM_PLUS;
- break;
-
- case 'S':
- case 's':
- myrpt->offset = REM_SIMPLEX;
- break;
-
- case 'T':
- case 't':
- myrpt->txplon = 1;
- break;
-
- case 'R':
- case 'r':
- myrpt->rxplon = 1;
- break;
-
- case 'U':
- case 'u':
- strcpy(myrpt->rxpl, "100.0");
- strcpy(myrpt->txpl, "100.0");
- myrpt->remmode = REM_MODE_USB;
- break;
- }
- }
-
- if (setrem(myrpt) == -1)
- return DC_ERROR;
-
- return DC_COMPLETE;
-
- case 2: /* set freq and offset */
- for (i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++) { /* look for M+*K+*O or M+*H+* depending on mode */
- if (digitbuf[i] == '*') {
- j++;
- continue;
- }
- if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
- goto invalid_freq;
- else {
- if (j == 0)
- l++; /* # of digits before first * */
- if (j == 1)
- k++; /* # of digits after first * */
- }
- }
-
- i = strlen(digitbuf) - 1;
- if (multimode) {
- if ((j > 2) || (l > 3) || (k > 6))
- goto invalid_freq; /* &^@#! */
- } else {
- if ((j > 2) || (l > 4) || (k > 3))
- goto invalid_freq; /* &^@#! */
- }
-
- /* Wait for M+*K+* */
-
- if (j < 2)
- break; /* Not yet */
-
- /* We have a frequency */
-
- s = ast_strdupa(digitbuf);
- AST_NONSTANDARD_APP_ARGS(args, s, '*');
- ls2 = strlen(args.s2);
-
- switch (ls2) { /* Allow partial entry of khz and hz digits for laziness support */
- case 1:
- ht = 0;
- k = 100 * atoi(args.s2);
- break;
- case 2:
- ht = 0;
- k = 10 * atoi(args.s2);
- break;
- case 3:
- if (!multimode) {
- if ((args.s2[2] != '0') && (args.s2[2] != '5'))
- goto invalid_freq;
- }
- ht = 0;
- k = atoi(args.s2);
- break;
- case 4:
- k = atoi(args.s2) / 10;
- ht = 10 * (atoi(args.s2 + (ls2 - 1)));
- break;
- case 5:
- k = atoi(args.s2) / 100;
- ht = (atoi(args.s2 + (ls2 - 2)));
- break;
- default:
- goto invalid_freq;
- }
-
- /* Check frequency for validity and establish a default mode */
-
- snprintf(freq, sizeof(freq), "%s.%03d%02d", args.s1, k, ht);
- ast_debug(1, "New frequency: %s\n", freq);
-
- split_freq(&mhz, &decimals, freq);
-
- if (check_freq(myrpt, mhz, decimals, &defmode)) /* Check to see if frequency entered is legit */
- goto invalid_freq;
-
- if ((defmode == REM_MODE_FM) && (digitbuf[i] == '*')) /* If FM, user must enter and additional offset digit */
- break; /* Not yet */
-
- offset = REM_SIMPLEX; /* Assume simplex */
-
- if (defmode == REM_MODE_FM) {
- oc = *s; /* Pick off offset */
- if (oc) {
- switch (oc) {
- case '1':
- offset = REM_MINUS;
- break;
- case '2':
- offset = REM_SIMPLEX;
- break;
- case '3':
- offset = REM_PLUS;
- break;
- default:
- goto invalid_freq;
- }
- }
- }
- offsave = myrpt->offset;
- modesave = myrpt->remmode;
- ast_copy_string(savestr, myrpt->freq, sizeof(savestr));
- ast_copy_string(myrpt->freq, freq, sizeof(myrpt->freq));
- myrpt->offset = offset;
- myrpt->remmode = defmode;
-
- if (setrem(myrpt) == -1) {
- myrpt->offset = offsave;
- myrpt->remmode = modesave;
- ast_copy_string(myrpt->freq, savestr, sizeof(myrpt->freq));
- goto invalid_freq;
- }
-
- return DC_COMPLETE;
-
-invalid_freq:
- rmt_sayfile(myrpt, mychannel, 1000, "rpt/invalid-freq");
-
- return DC_ERROR;
-
- case 3: /* set rx PL tone */
-
- for (i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++) { /* look for N+*N */
- if (digitbuf[i] == '*') {
- j++;
- continue;
- }
- if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
- return DC_ERROR;
- else {
- if (j)
- l++;
- else
- k++;
- }
- }
- if ((j > 1) || (k > 3) || (l > 1))
- return DC_ERROR; /* &$@^! */
- i = strlen(digitbuf) - 1;
- if ((j != 1) || (k < 2)|| (l != 1))
- break; /* Not yet */
- ast_debug(1, "PL digits entered %s\n", digitbuf);
-
- ast_copy_string(tmp, digitbuf, sizeof(tmp));
- /* see if we have at least 1 */
- s = strchr(tmp, '*');
- if (s)
- *s = '.';
- ast_copy_string(savestr, myrpt->rxpl, sizeof(savestr));
- ast_copy_string(myrpt->rxpl, tmp, sizeof(myrpt->rxpl));
-
- if (setrem(myrpt) == -1) {
- ast_copy_string(myrpt->rxpl, savestr, sizeof(myrpt->rxpl));
- return DC_ERROR;
- }
-
- return DC_COMPLETE;
- case 4: /* set tx PL tone */
- for (i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++) { /* look for N+*N */
- if (digitbuf[i] == '*') {
- j++;
- continue;
- }
- if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
- return DC_ERROR;
- else {
- if (j)
- l++;
- else
- k++;
- }
- }
- if ((j > 1) || (k > 3) || (l > 1))
- return DC_ERROR; /* &$@^! */
- i = strlen(digitbuf) - 1;
- if ((j != 1) || (k < 2)|| (l != 1))
- break; /* Not yet */
- ast_debug(1, "PL digits entered %s\n", digitbuf);
-
- ast_copy_string(tmp, digitbuf, sizeof(tmp));
- /* see if we have at least 1 */
- s = strchr(tmp, '*');
- if (s)
- *s = '.';
- ast_copy_string(savestr, myrpt->txpl, sizeof(savestr));
- ast_copy_string(myrpt->txpl, tmp, sizeof(myrpt->txpl));
-
- if (setrem(myrpt) == -1) {
- ast_copy_string(myrpt->txpl, savestr, sizeof(myrpt->txpl));
- return DC_ERROR;
- }
-
- return DC_COMPLETE;
-
- case 6: /* MODE (FM,USB,LSB,AM) */
- if (strlen(digitbuf) < 1)
- break;
-
- if (!multimode)
- return DC_ERROR; /* Multimode radios only */
-
- switch (*digitbuf) {
- case '1':
- split_freq(&mhz, &decimals, myrpt->freq);
- if (mhz < 29) /* No FM allowed below 29MHz! */
- return DC_ERROR;
- myrpt->remmode = REM_MODE_FM;
- res = rmt_saycharstr(myrpt, mychannel, 1000, "FM");
- break;
-
- case '2':
- myrpt->remmode = REM_MODE_USB;
- res = rmt_saycharstr(myrpt, mychannel, 1000, "USB");
- break;
-
- case '3':
- myrpt->remmode = REM_MODE_LSB;
- res = rmt_saycharstr(myrpt, mychannel, 1000, "LSB");
- break;
-
- case '4':
- myrpt->remmode = REM_MODE_AM;
- res = rmt_saycharstr(myrpt, mychannel, 1000, "AM");
- break;
-
- default:
- return DC_ERROR;
- }
- if (res)
- return DC_ERROR;
-
- if (setrem(myrpt))
- return DC_ERROR;
- return DC_COMPLETE;
-
- case 100: /* other stuff */
- case 101:
- case 102:
- case 103:
- case 104:
- case 105:
- case 106:
- res = rmt_telem_start(myrpt, mychannel, 1000);
- switch (myatoi(param)) { /* Quick commands requiring a setrem call */
- case 100: /* RX PL Off */
- myrpt->rxplon = 0;
- if (!res)
- res = sayfile(mychannel, "rpt/rxpl");
- if (!res)
- sayfile(mychannel, "rpt/off");
- break;
-
- case 101: /* RX PL On */
- myrpt->rxplon = 1;
- if (!res)
- res = sayfile(mychannel, "rpt/rxpl");
- if (!res)
- sayfile(mychannel, "rpt/on");
- break;
-
- case 102: /* TX PL Off */
- myrpt->txplon = 0;
- if (!res)
- res = sayfile(mychannel, "rpt/txpl");
- if (!res)
- sayfile(mychannel, "rpt/off");
- break;
-
- case 103: /* TX PL On */
- myrpt->txplon = 1;
- if (!res)
- res = sayfile(mychannel, "rpt/txpl");
- if (!res)
- sayfile(mychannel, "rpt/on");
- break;
-
- case 104: /* Low Power */
- myrpt->powerlevel = REM_LOWPWR;
- if (!res)
- res = sayfile(mychannel, "rpt/lopwr");
- break;
-
- case 105: /* Medium Power */
- myrpt->powerlevel = REM_MEDPWR;
- if (!res)
- res = sayfile(mychannel, "rpt/medpwr");
- break;
-
- case 106: /* Hi Power */
- myrpt->powerlevel = REM_HIPWR;
- if (!res)
- res = sayfile(mychannel, "rpt/hipwr");
- break;
-
- default:
- if (!res)
- rmt_telem_finish(myrpt, mychannel);
- return DC_ERROR;
- }
- if (!res)
- res = rmt_telem_finish(myrpt, mychannel);
- if (res)
- return DC_ERROR;
-
- if (setrem(myrpt) == -1)
- return DC_ERROR;
- return DC_COMPLETE;
-
- case 107: /* Bump down 20Hz */
- multimode_bump_freq(myrpt, -20);
- return DC_COMPLETE;
-
- case 108: /* Bump down 100Hz */
- multimode_bump_freq(myrpt, -100);
- return DC_COMPLETE;
-
- case 109: /* Bump down 500Hz */
- multimode_bump_freq(myrpt, -500);
- return DC_COMPLETE;
-
- case 110: /* Bump up 20Hz */
- multimode_bump_freq(myrpt, 20);
- return DC_COMPLETE;
-
- case 111: /* Bump up 100Hz */
- multimode_bump_freq(myrpt, 100);
- return DC_COMPLETE;
-
- case 112: /* Bump up 500Hz */
- multimode_bump_freq(myrpt, 500);
- return DC_COMPLETE;
-
- case 113:
- case 114:
- case 115:
- case 116:
- case 117:
- case 118:
- myrpt->remotetx = 0;
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
- if (!myrpt->remoterx)
- ast_indicate(mychannel, AST_CONTROL_RADIO_KEY);
- if (ast_safe_sleep(mychannel, 1000) == -1)
- return DC_ERROR;
-
- switch (myatoi(param)) {
- case 113: /* Scan down slow */
- res = sayfile(mychannel, "rpt/down");
- if (!res)
- res = sayfile(mychannel, "rpt/slow");
- if (!res) {
- myrpt->scantimer = REM_SCANTIME;
- myrpt->hfscanmode = HF_SCAN_DOWN_SLOW;
- }
- break;
-
- case 114: /* Scan down quick */
- res = sayfile(mychannel, "rpt/down");
- if (!res)
- res = sayfile(mychannel, "rpt/quick");
- if (!res) {
- myrpt->scantimer = REM_SCANTIME;
- myrpt->hfscanmode = HF_SCAN_DOWN_QUICK;
- }
- break;
-
- case 115: /* Scan down fast */
- res = sayfile(mychannel, "rpt/down");
- if (!res)
- res = sayfile(mychannel, "rpt/fast");
- if (!res) {
- myrpt->scantimer = REM_SCANTIME;
- myrpt->hfscanmode = HF_SCAN_DOWN_FAST;
- }
- break;
-
- case 116: /* Scan up slow */
- res = sayfile(mychannel, "rpt/up");
- if (!res)
- res = sayfile(mychannel, "rpt/slow");
- if (!res) {
- myrpt->scantimer = REM_SCANTIME;
- myrpt->hfscanmode = HF_SCAN_UP_SLOW;
- }
- break;
-
- case 117: /* Scan up quick */
- res = sayfile(mychannel, "rpt/up");
- if (!res)
- res = sayfile(mychannel, "rpt/quick");
- if (!res) {
- myrpt->scantimer = REM_SCANTIME;
- myrpt->hfscanmode = HF_SCAN_UP_QUICK;
- }
- break;
-
- case 118: /* Scan up fast */
- res = sayfile(mychannel, "rpt/up");
- if (!res)
- res = sayfile(mychannel, "rpt/fast");
- if (!res) {
- myrpt->scantimer = REM_SCANTIME;
- myrpt->hfscanmode = HF_SCAN_UP_FAST;
- }
- break;
- }
- rmt_telem_finish(myrpt, mychannel);
- return DC_COMPLETE;
-
-
- case 119: /* Tune Request */
- myrpt->tunerequest = 1;
- return DC_COMPLETE;
-
- case 5: /* Long Status */
- case 140: /* Short Status */
- res = rmt_telem_start(myrpt, mychannel, 1000);
-
- res = sayfile(mychannel, "rpt/node");
- if (!res)
- res = saycharstr(mychannel, myrpt->name);
- if (!res)
- res = sayfile(mychannel, "rpt/frequency");
- if (!res)
- res = split_freq(&mhz, &decimals, myrpt->freq);
- if (!res) {
- if (mhz < 100)
- res = saynum(mychannel, mhz);
- else
- res = saydigits(mychannel, mhz);
- }
- if (!res)
- res = sayfile(mychannel, "letters/dot");
- if (!res)
- res = saydigits(mychannel, decimals);
-
- if (res) {
- rmt_telem_finish(myrpt, mychannel);
- return DC_ERROR;
- }
- if (myrpt->remmode == REM_MODE_FM) { /* Mode FM? */
- switch (myrpt->offset) {
- case REM_MINUS:
- res = sayfile(mychannel, "rpt/minus");
- break;
-
- case REM_SIMPLEX:
- res = sayfile(mychannel, "rpt/simplex");
- break;
-
- case REM_PLUS:
- res = sayfile(mychannel, "rpt/plus");
- break;
-
- default:
- return DC_ERROR;
-
- }
- } else { /* Must be USB, LSB, or AM */
- switch (myrpt->remmode) {
- case REM_MODE_USB:
- res = saycharstr(mychannel, "USB");
- break;
- case REM_MODE_LSB:
- res = saycharstr(mychannel, "LSB");
- break;
- case REM_MODE_AM:
- res = saycharstr(mychannel, "AM");
- break;
- default:
- return DC_ERROR;
- }
- }
-
- if (res == -1) {
- rmt_telem_finish(myrpt, mychannel);
- return DC_ERROR;
- }
-
- if (myatoi(param) == 140) { /* Short status? */
- if (!res)
- res = rmt_telem_finish(myrpt, mychannel);
- if (res)
- return DC_ERROR;
- return DC_COMPLETE;
- }
-
- switch (myrpt->powerlevel) {
- case REM_LOWPWR:
- res = sayfile(mychannel, "rpt/lopwr") ;
- break;
- case REM_MEDPWR:
- res = sayfile(mychannel, "rpt/medpwr");
- break;
- case REM_HIPWR:
- res = sayfile(mychannel, "rpt/hipwr");
- break;
- }
- if (res || (sayfile(mychannel, "rpt/rxpl") == -1) ||
- (sayfile(mychannel, "rpt/frequency") == -1) ||
- (saycharstr(mychannel, myrpt->rxpl) == -1) ||
- (sayfile(mychannel, "rpt/txpl") == -1) ||
- (sayfile(mychannel, "rpt/frequency") == -1) ||
- (saycharstr(mychannel, myrpt->txpl) == -1) ||
- (sayfile(mychannel, "rpt/txpl") == -1) ||
- (sayfile(mychannel, ((myrpt->txplon) ? "rpt/on" : "rpt/off")) == -1) ||
- (sayfile(mychannel, "rpt/rxpl") == -1) ||
- (sayfile(mychannel, ((myrpt->rxplon) ? "rpt/on" : "rpt/off")) == -1))
- {
- rmt_telem_finish(myrpt, mychannel);
- return DC_ERROR;
- }
- if (!res)
- res = rmt_telem_finish(myrpt, mychannel);
- if (res)
- return DC_ERROR;
-
- return DC_COMPLETE;
- default:
- return DC_ERROR;
- }
-
- return DC_INDETERMINATE;
-}
-
-static int handle_remote_dtmf_digit(struct rpt *myrpt, char c, char *keyed, int phonemode)
-{
- time_t now;
- int ret, res = 0, src;
-
- /* Stop scan mode if in scan mode */
- if (myrpt->hfscanmode) {
- stop_scan(myrpt, 0);
- return 0;
- }
-
- time(&now);
- /* if timed-out */
- if ((myrpt->dtmf_time_rem + DTMF_TIMEOUT) < now) {
- myrpt->dtmfidx = -1;
- myrpt->dtmfbuf[0] = 0;
- myrpt->dtmf_time_rem = 0;
- }
- /* if decode not active */
- if (myrpt->dtmfidx == -1) {
- /* if not lead-in digit, dont worry */
- if (c != myrpt->p.funcchar)
- return 0;
- myrpt->dtmfidx = 0;
- myrpt->dtmfbuf[0] = 0;
- myrpt->dtmf_time_rem = now;
- return 0;
- }
- /* if too many in buffer, start over */
- if (myrpt->dtmfidx >= MAXDTMF) {
- myrpt->dtmfidx = 0;
- myrpt->dtmfbuf[0] = 0;
- myrpt->dtmf_time_rem = now;
- }
- if (c == myrpt->p.funcchar) {
- /* if star at beginning, or 2 together, erase buffer */
- if ((myrpt->dtmfidx < 1) || (myrpt->dtmfbuf[myrpt->dtmfidx - 1] == myrpt->p.funcchar)) {
- myrpt->dtmfidx = 0;
- myrpt->dtmfbuf[0] = 0;
- myrpt->dtmf_time_rem = now;
- return 0;
- }
- }
- myrpt->dtmfbuf[myrpt->dtmfidx++] = c;
- myrpt->dtmfbuf[myrpt->dtmfidx] = 0;
- myrpt->dtmf_time_rem = now;
-
-
- src = SOURCE_RMT;
- if (phonemode > 1)
- src = SOURCE_DPHONE;
- else if (phonemode)
- src = SOURCE_PHONE;
- ret = collect_function_digits(myrpt, myrpt->dtmfbuf, src, NULL);
-
- switch(ret) {
- case DC_INDETERMINATE:
- res = 0;
- break;
- case DC_DOKEY:
- if (keyed)
- *keyed = 1;
- res = 0;
- break;
- case DC_REQ_FLUSH:
- myrpt->dtmfidx = 0;
- myrpt->dtmfbuf[0] = 0;
- res = 0;
- break;
- case DC_COMPLETE:
- myrpt->totalexecdcommands++;
- myrpt->dailyexecdcommands++;
- ast_copy_string(myrpt->lastdtmfcommand, myrpt->dtmfbuf, MAXDTMF);
- myrpt->dtmfbuf[0] = 0;
- myrpt->dtmfidx = -1;
- myrpt->dtmf_time_rem = 0;
- res = 1;
- break;
- case DC_ERROR:
- default:
- myrpt->dtmfbuf[0] = 0;
- myrpt->dtmfidx = -1;
- myrpt->dtmf_time_rem = 0;
- res = 0;
- }
-
- return res;
-}
-
-static int handle_remote_data(struct rpt *myrpt, char *str)
-{
- char cmd[300], dest[300], src[300], c;
- int seq, res;
-
- if (!strcmp(str, discstr))
- return 0;
- if (sscanf(str, "%s %s %s %d %c", cmd, dest, src, &seq, &c) != 5) {
- ast_log(LOG_WARNING, "Unable to parse link string %s\n", str);
- return 0;
- }
- if (strcmp(cmd, "D")) {
- ast_log(LOG_WARNING, "Unable to parse link string %s\n", str);
- return 0;
- }
- /* if not for me, ignore */
- if (strcmp(dest, myrpt->name))
- return 0;
- res = handle_remote_dtmf_digit(myrpt, c, NULL, 0);
- if (res != 1)
- return res;
- myrpt->remotetx = 0;
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
- if (!myrpt->remoterx) {
- ast_indicate(myrpt->remchannel, AST_CONTROL_RADIO_KEY);
- }
- if (ast_safe_sleep(myrpt->remchannel, 1000) == -1)
- return -1;
- res = telem_lookup(myrpt, myrpt->remchannel, myrpt->name, "functcomplete");
- rmt_telem_finish(myrpt, myrpt->remchannel);
- return res;
-}
-
-static int handle_remote_phone_dtmf(struct rpt *myrpt, char c, char *keyed, int phonemode)
-{
- int res;
-
- if (keyed && *keyed && (c == myrpt->p.endchar)) {
- *keyed = 0;
- return DC_INDETERMINATE;
- }
-
- res = handle_remote_dtmf_digit(myrpt, c, keyed, phonemode);
- if (res != 1)
- return res;
- myrpt->remotetx = 0;
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
- if (!myrpt->remoterx) {
- ast_indicate(myrpt->remchannel, AST_CONTROL_RADIO_KEY);
- }
- if (ast_safe_sleep(myrpt->remchannel, 1000) == -1)
- return -1;
- res = telem_lookup(myrpt, myrpt->remchannel, myrpt->name, "functcomplete");
- rmt_telem_finish(myrpt, myrpt->remchannel);
- return res;
-}
-
-static int attempt_reconnect(struct rpt *myrpt, struct rpt_link *l)
-{
- const char *val;
- char *s, *tele;
- char deststr[300] = "";
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(channel);
- AST_APP_ARG(extra);
- );
-
- val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, l->name);
- if (!val) {
- ast_log(LOG_ERROR, "attempt_reconnect: cannot find node %s\n", l->name);
- return -1;
- }
-
- rpt_mutex_lock(&myrpt->lock);
- /* remove from queue */
- remque((struct qelem *) l);
- rpt_mutex_unlock(&myrpt->lock);
- s = ast_strdupa(val);
- AST_STANDARD_APP_ARGS(args, s);
-
- /* XXX This section doesn't make any sense. Why not just use args.channel? XXX */
- snprintf(deststr, sizeof(deststr), "IAX2/%s", args.channel);
- tele = strchr(deststr, '/');
- if (!tele) {
- ast_log(LOG_ERROR, "attempt_reconnect:Dial number (%s) must be in format tech/number\n", deststr);
- return -1;
- }
- *tele++ = 0;
- l->elaptime = 0;
- l->connecttime = 0;
- l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele, NULL);
- if (l->chan) {
- ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
- ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
- l->chan->whentohangup = 0;
- l->chan->appl = "Apprpt";
- l->chan->data = "(Remote Rx)";
- ast_verb(3, "rpt (attempt_reconnect) initiating call to %s/%s on %s\n",
- deststr, tele, l->chan->name);
- if (l->chan->cid.cid_num)
- ast_free(l->chan->cid.cid_num);
- l->chan->cid.cid_num = ast_strdup(myrpt->name);
- ast_call(l->chan, tele, 999);
-
- } else {
- ast_verb(3, "Unable to place call to %s/%s on %s\n",
- deststr, tele, l->chan->name);
- return -1;
- }
- rpt_mutex_lock(&myrpt->lock);
- /* put back in queue */
- insque((struct qelem *)l, (struct qelem *)myrpt->links.next);
- rpt_mutex_unlock(&myrpt->lock);
- ast_log(LOG_NOTICE, "Reconnect Attempt to %s in process\n", l->name);
- return 0;
-}
-
-/* 0 return=continue, 1 return = break, -1 return = error */
-static void local_dtmf_helper(struct rpt *myrpt, char c)
-{
- int res;
- char cmd[MAXDTMF+1] = "";
-
- if (c == myrpt->p.endchar) {
- /* if in simple mode, kill autopatch */
- if (myrpt->p.simple && myrpt->callmode) {
- rpt_mutex_lock(&myrpt->lock);
- myrpt->callmode = 0;
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, TERM, NULL);
- return;
- }
- rpt_mutex_lock(&myrpt->lock);
- myrpt->stopgen = 1;
- if (myrpt->cmdnode[0]) {
- myrpt->cmdnode[0] = 0;
- myrpt->dtmfidx = -1;
- myrpt->dtmfbuf[0] = 0;
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, COMPLETE, NULL);
- } else
- rpt_mutex_unlock(&myrpt->lock);
- return;
- }
- rpt_mutex_lock(&myrpt->lock);
- if (myrpt->cmdnode[0]) {
- rpt_mutex_unlock(&myrpt->lock);
- send_link_dtmf(myrpt, c);
- return;
- }
- if (!myrpt->p.simple) {
- if (c == myrpt->p.funcchar) {
- myrpt->dtmfidx = 0;
- myrpt->dtmfbuf[myrpt->dtmfidx] = 0;
- rpt_mutex_unlock(&myrpt->lock);
- time(&myrpt->dtmf_time);
- return;
- } else if ((c != myrpt->p.endchar) && (myrpt->dtmfidx >= 0)) {
- time(&myrpt->dtmf_time);
-
- if (myrpt->dtmfidx < MAXDTMF) {
- myrpt->dtmfbuf[myrpt->dtmfidx++] = c;
- myrpt->dtmfbuf[myrpt->dtmfidx] = 0;
-
- ast_copy_string(cmd, myrpt->dtmfbuf, sizeof(cmd));
-
- rpt_mutex_unlock(&myrpt->lock);
- res = collect_function_digits(myrpt, cmd, SOURCE_RPT, NULL);
- rpt_mutex_lock(&myrpt->lock);
- switch(res) {
- case DC_INDETERMINATE:
- break;
- case DC_REQ_FLUSH:
- myrpt->dtmfidx = 0;
- myrpt->dtmfbuf[0] = 0;
- break;
- case DC_COMPLETE:
- myrpt->totalexecdcommands++;
- myrpt->dailyexecdcommands++;
- ast_copy_string(myrpt->lastdtmfcommand, cmd, MAXDTMF);
- myrpt->dtmfbuf[0] = 0;
- myrpt->dtmfidx = -1;
- myrpt->dtmf_time = 0;
- break;
-
- case DC_ERROR:
- default:
- myrpt->dtmfbuf[0] = 0;
- myrpt->dtmfidx = -1;
- myrpt->dtmf_time = 0;
- break;
- }
- if (res != DC_INDETERMINATE) {
- rpt_mutex_unlock(&myrpt->lock);
- return;
- }
- }
- }
- } else /* if simple */ {
- if ((!myrpt->callmode) && (c == myrpt->p.funcchar)) {
- myrpt->callmode = 1;
- myrpt->patchnoct = 0;
- myrpt->patchquiet = 0;
- myrpt->patchfarenddisconnect = 0;
- myrpt->patchdialtime = 0;
- ast_copy_string(myrpt->patchcontext, myrpt->p.ourcontext, sizeof(myrpt->patchcontext));
- myrpt->cidx = 0;
- myrpt->exten[myrpt->cidx] = 0;
- rpt_mutex_unlock(&myrpt->lock);
- ast_pthread_create_detached(&myrpt->rpt_call_thread, NULL, rpt_call, (void *)myrpt);
- return;
- }
- }
- if (myrpt->callmode == 1) {
- myrpt->exten[myrpt->cidx++] = c;
- myrpt->exten[myrpt->cidx] = 0;
- /* if this exists */
- if (ast_exists_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
- myrpt->callmode = 2;
- rpt_mutex_unlock(&myrpt->lock);
- if (!myrpt->patchquiet)
- rpt_telemetry(myrpt, PROC, NULL);
- return;
- }
- /* if can continue, do so */
- if (!ast_canmatch_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
- /* call has failed, inform user */
- myrpt->callmode = 4;
- }
- rpt_mutex_unlock(&myrpt->lock);
- return;
- }
- if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) {
- myrpt->mydtmf = c;
- }
- rpt_mutex_unlock(&myrpt->lock);
- return;
-}
-
-
-/* place an ID event in the telemetry queue */
-
-static void queue_id(struct rpt *myrpt)
-{
- myrpt->mustid = myrpt->tailid = 0;
- myrpt->idtimer = myrpt->p.idtime; /* Reset our ID timer */
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, ID, NULL);
- rpt_mutex_lock(&myrpt->lock);
-}
-
-/* Scheduler */
-
-static void do_scheduler(struct rpt *myrpt)
-{
- int res;
- struct ast_tm tmnow;
-
- memcpy(&myrpt->lasttv, &myrpt->curtv, sizeof(struct timeval));
-
- if ( (res = gettimeofday(&myrpt->curtv, NULL)) < 0)
- ast_log(LOG_NOTICE, "Scheduler gettime of day returned: %s\n", strerror(res));
-
- /* Try to get close to a 1 second resolution */
-
- if (myrpt->lasttv.tv_sec == myrpt->curtv.tv_sec)
- return;
-
- ast_localtime(&myrpt->curtv, &tmnow, NULL);
-
- /* If midnight, then reset all daily statistics */
-
- if ((tmnow.tm_hour == 0) && (tmnow.tm_min == 0) && (tmnow.tm_sec == 0)) {
- myrpt->dailykeyups = 0;
- myrpt->dailytxtime = 0;
- myrpt->dailykerchunks = 0;
- myrpt->dailyexecdcommands = 0;
- }
-}
-
-
-/* single thread with one file (request) to dial */
-static void *rpt(void *this)
-{
- struct rpt *myrpt = (struct rpt *)this;
- char *tele, c;
- const char *idtalkover;
- int ms = MSWAIT, i, lasttx=0, val, remrx=0, identqueued, othertelemqueued, tailmessagequeued, ctqueued;
- struct ast_channel *who;
- ZT_CONFINFO ci; /* conference info */
- time_t t;
- struct rpt_link *l, *m;
- struct rpt_tele *telem;
- char tmpstr[300];
-
- rpt_mutex_lock(&myrpt->lock);
-
- telem = myrpt->tele.next;
- while (telem != &myrpt->tele) {
- ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV);
- telem = telem->next;
- }
- rpt_mutex_unlock(&myrpt->lock);
- /* find our index, and load the vars initially */
- for (i = 0; i < nrpts; i++) {
- if (&rpt_vars[i] == myrpt) {
- load_rpt_vars(i, 0);
- break;
- }
- }
- rpt_mutex_lock(&myrpt->lock);
- ast_copy_string(tmpstr, myrpt->rxchanname, sizeof(tmpstr));
- tele = strchr(tmpstr, '/');
- if (!tele) {
- ast_log(LOG_ERROR, "rpt:Rxchannel Dial number (%s) must be in format tech/number\n", myrpt->rxchanname);
- rpt_mutex_unlock(&myrpt->lock);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- *tele++ = 0;
- myrpt->rxchannel = ast_request(tmpstr, AST_FORMAT_SLINEAR, tele, NULL);
- if (myrpt->rxchannel) {
- if (myrpt->rxchannel->_state == AST_STATE_BUSY) {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Rx channel\n");
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->rxchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- ast_set_read_format(myrpt->rxchannel, AST_FORMAT_SLINEAR);
- ast_set_write_format(myrpt->rxchannel, AST_FORMAT_SLINEAR);
- myrpt->rxchannel->whentohangup = 0;
- myrpt->rxchannel->appl = "Apprpt";
- myrpt->rxchannel->data = "(Repeater Rx)";
- ast_verb(3, "rpt (Rx) initiating call to %s/%s on %s\n",
- tmpstr, tele, myrpt->rxchannel->name);
- ast_call(myrpt->rxchannel, tele, 999);
- if (myrpt->rxchannel->_state != AST_STATE_UP) {
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->rxchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- } else {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Rx channel\n");
- rpt_mutex_unlock(&myrpt->lock);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- if (myrpt->txchanname) {
- ast_copy_string(tmpstr, myrpt->txchanname, sizeof(tmpstr));
- tele = strchr(tmpstr, '/');
- if (!tele) {
- ast_log(LOG_ERROR, "rpt:Txchannel Dial number (%s) must be in format tech/number\n", myrpt->txchanname);
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->rxchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- *tele++ = 0;
- myrpt->txchannel = ast_request(tmpstr, AST_FORMAT_SLINEAR, tele, NULL);
- if (myrpt->txchannel) {
- if (myrpt->txchannel->_state == AST_STATE_BUSY) {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Tx channel\n");
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->txchannel);
- ast_hangup(myrpt->rxchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- ast_set_read_format(myrpt->txchannel, AST_FORMAT_SLINEAR);
- ast_set_write_format(myrpt->txchannel, AST_FORMAT_SLINEAR);
- myrpt->txchannel->whentohangup = 0;
- myrpt->txchannel->appl = "Apprpt";
- myrpt->txchannel->data = "(Repeater Tx)";
- ast_verb(3, "rpt (Tx) initiating call to %s/%s on %s\n",
- tmpstr, tele, myrpt->txchannel->name);
- ast_call(myrpt->txchannel, tele, 999);
- if (myrpt->rxchannel->_state != AST_STATE_UP) {
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->rxchannel);
- ast_hangup(myrpt->txchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- } else {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Tx channel\n");
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->rxchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- } else {
- myrpt->txchannel = myrpt->rxchannel;
- }
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_KEY);
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
- /* allocate a pseudo-channel thru asterisk */
- myrpt->pchannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
- if (!myrpt->pchannel) {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
- rpt_mutex_unlock(&myrpt->lock);
- if (myrpt->txchannel != myrpt->rxchannel)
- ast_hangup(myrpt->txchannel);
- ast_hangup(myrpt->rxchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- /* make a conference for the tx */
- ci.chan = 0;
- ci.confno = -1; /* make a new conf */
- ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
- /* first put the channel on the conference in proper mode */
- if (ioctl(myrpt->txchannel->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->pchannel);
- if (myrpt->txchannel != myrpt->rxchannel)
- ast_hangup(myrpt->txchannel);
- ast_hangup(myrpt->rxchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- /* save tx conference number */
- myrpt->txconf = ci.confno;
- /* make a conference for the pseudo */
- ci.chan = 0;
- ci.confno = -1; /* make a new conf */
- ci.confmode = ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4)) ? ZT_CONF_CONFANNMON :
- (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
- /* first put the channel on the conference in announce mode */
- if (ioctl(myrpt->pchannel->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->pchannel);
- if (myrpt->txchannel != myrpt->rxchannel)
- ast_hangup(myrpt->txchannel);
- ast_hangup(myrpt->rxchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- /* save pseudo channel conference number */
- myrpt->conf = ci.confno;
- /* allocate a pseudo-channel thru asterisk */
- myrpt->txpchannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
- if (!myrpt->txpchannel) {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->pchannel);
- if (myrpt->txchannel != myrpt->rxchannel)
- ast_hangup(myrpt->txchannel);
- ast_hangup(myrpt->rxchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- /* make a conference for the tx */
- ci.chan = 0;
- ci.confno = myrpt->txconf;
- ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER ;
- /* first put the channel on the conference in proper mode */
- if (ioctl(myrpt->txpchannel->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->txpchannel);
- ast_hangup(myrpt->pchannel);
- if (myrpt->txchannel != myrpt->rxchannel)
- ast_hangup(myrpt->txchannel);
- ast_hangup(myrpt->rxchannel);
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- }
- /* Now, the idea here is to copy from the physical rx channel buffer
- into the pseudo tx buffer, and from the pseudo rx buffer into the
- tx channel buffer */
- myrpt->links.next = &myrpt->links;
- myrpt->links.prev = &myrpt->links;
- myrpt->tailtimer = 0;
- myrpt->totimer = 0;
- myrpt->tmsgtimer = myrpt->p.tailmessagetime;
- myrpt->idtimer = myrpt->p.politeid;
- myrpt->mustid = myrpt->tailid = 0;
- myrpt->callmode = 0;
- myrpt->tounkeyed = 0;
- myrpt->tonotify = 0;
- myrpt->retxtimer = 0;
- myrpt->skedtimer = 0;
- myrpt->tailevent = 0;
- lasttx = 0;
- myrpt->keyed = 0;
- idtalkover = ast_variable_retrieve(myrpt->cfg, myrpt->name, "idtalkover");
- myrpt->dtmfidx = -1;
- myrpt->dtmfbuf[0] = 0;
- myrpt->rem_dtmfidx = -1;
- myrpt->rem_dtmfbuf[0] = 0;
- myrpt->dtmf_time = 0;
- myrpt->rem_dtmf_time = 0;
- myrpt->enable = 1;
- myrpt->disgorgetime = 0;
- myrpt->lastnodewhichkeyedusup[0] = '\0';
- myrpt->dailytxtime = 0;
- myrpt->totaltxtime = 0;
- myrpt->dailykeyups = 0;
- myrpt->totalkeyups = 0;
- myrpt->dailykerchunks = 0;
- myrpt->totalkerchunks = 0;
- myrpt->dailyexecdcommands = 0;
- myrpt->totalexecdcommands = 0;
- myrpt->timeouts = 0;
- myrpt->exten[0] = '\0';
- myrpt->lastdtmfcommand[0] = '\0';
- if (myrpt->p.startupmacro) {
- snprintf(myrpt->macrobuf, sizeof(myrpt->macrobuf), "PPPP%s", myrpt->p.startupmacro);
- }
- if (myrpt->p.startupgosub) {
- snprintf(myrpt->gosubbuf, sizeof(myrpt->gosubbuf), "PPPP%s", myrpt->p.startupgosub);
- }
- rpt_mutex_unlock(&myrpt->lock);
- val = 0;
- ast_channel_setoption(myrpt->rxchannel, AST_OPTION_TONE_VERIFY, &val, sizeof(char), 0);
- val = 1;
- ast_channel_setoption(myrpt->rxchannel, AST_OPTION_RELAXDTMF, &val, sizeof(char), 0);
- while (ms >= 0) {
- struct ast_frame *f;
- struct ast_channel *cs[300];
- int totx=0, elap=0, n, toexit = 0;
-
- /* DEBUG Dump */
- if ((myrpt->disgorgetime) && (time(NULL) >= myrpt->disgorgetime)) {
- struct rpt_link *zl;
- struct rpt_tele *zt;
-
- myrpt->disgorgetime = 0;
- ast_log(LOG_NOTICE, "********** Variable Dump Start (app_rpt) **********\n");
- ast_log(LOG_NOTICE, "totx = %d\n", totx);
- ast_log(LOG_NOTICE, "remrx = %d\n", remrx);
- ast_log(LOG_NOTICE, "lasttx = %d\n", lasttx);
- ast_log(LOG_NOTICE, "elap = %d\n", elap);
- ast_log(LOG_NOTICE, "toexit = %d\n", toexit);
-
- ast_log(LOG_NOTICE, "myrpt->keyed = %d\n", myrpt->keyed);
- ast_log(LOG_NOTICE, "myrpt->localtx = %d\n", myrpt->localtx);
- ast_log(LOG_NOTICE, "myrpt->callmode = %d\n", myrpt->callmode);
- ast_log(LOG_NOTICE, "myrpt->enable = %d\n", myrpt->enable);
- ast_log(LOG_NOTICE, "myrpt->mustid = %d\n", myrpt->mustid);
- ast_log(LOG_NOTICE, "myrpt->tounkeyed = %d\n", myrpt->tounkeyed);
- ast_log(LOG_NOTICE, "myrpt->tonotify = %d\n", myrpt->tonotify);
- ast_log(LOG_NOTICE, "myrpt->retxtimer = %ld\n", myrpt->retxtimer);
- ast_log(LOG_NOTICE, "myrpt->totimer = %d\n", myrpt->totimer);
- ast_log(LOG_NOTICE, "myrpt->tailtimer = %d\n", myrpt->tailtimer);
- ast_log(LOG_NOTICE, "myrpt->tailevent = %d\n", myrpt->tailevent);
-
- zl = myrpt->links.next;
- while (zl != &myrpt->links) {
- ast_log(LOG_NOTICE, "*** Link Name: %s ***\n", zl->name);
- ast_log(LOG_NOTICE, " link->lasttx %d\n", zl->lasttx);
- ast_log(LOG_NOTICE, " link->lastrx %d\n", zl->lastrx);
- ast_log(LOG_NOTICE, " link->connected %d\n", zl->connected);
- ast_log(LOG_NOTICE, " link->hasconnected %d\n", zl->hasconnected);
- ast_log(LOG_NOTICE, " link->outbound %d\n", zl->outbound);
- ast_log(LOG_NOTICE, " link->disced %d\n", zl->disced);
- ast_log(LOG_NOTICE, " link->killme %d\n", zl->killme);
- ast_log(LOG_NOTICE, " link->disctime %ld\n", zl->disctime);
- ast_log(LOG_NOTICE, " link->retrytimer %ld\n", zl->retrytimer);
- ast_log(LOG_NOTICE, " link->retries = %d\n", zl->retries);
- ast_log(LOG_NOTICE, " link->reconnects = %d\n", zl->reconnects);
- zl = zl->next;
- }
-
- zt = myrpt->tele.next;
- if (zt != &myrpt->tele)
- ast_log(LOG_NOTICE, "*** Telemetry Queue ***\n");
- while (zt != &myrpt->tele) {
- ast_log(LOG_NOTICE, " Telemetry mode: %d\n", zt->mode);
- zt = zt->next;
- }
- ast_log(LOG_NOTICE, "******* Variable Dump End (app_rpt) *******\n");
- }
-
- if (myrpt->reload) {
- struct rpt_tele *telem;
-
- rpt_mutex_lock(&myrpt->lock);
- telem = myrpt->tele.next;
- while (telem != &myrpt->tele) {
- ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV);
- telem = telem->next;
- }
- myrpt->reload = 0;
- rpt_mutex_unlock(&myrpt->lock);
- usleep(10000);
- /* find our index, and load the vars */
- for (i = 0; i < nrpts; i++) {
- if (&rpt_vars[i] == myrpt) {
- load_rpt_vars(i, 0);
- break;
- }
- }
- }
-
- rpt_mutex_lock(&myrpt->lock);
- if (ast_check_hangup(myrpt->rxchannel)) break;
- if (ast_check_hangup(myrpt->txchannel)) break;
- if (ast_check_hangup(myrpt->pchannel)) break;
- if (ast_check_hangup(myrpt->txpchannel)) break;
-
- /* Update local tx with keyed if not parsing a command */
- myrpt->localtx = myrpt->keyed && (myrpt->dtmfidx == -1) && (!myrpt->cmdnode[0]);
- /* If someone's connected, and they're transmitting from their end to us, set remrx true */
- l = myrpt->links.next;
- remrx = 0;
- while (l != &myrpt->links) {
- if (l->lastrx) {
- remrx = 1;
- if (l->name[0] != '0') /* Ignore '0' nodes */
- strcpy(myrpt->lastnodewhichkeyedusup, l->name); /* Note the node which is doing the key up */
- }
- l = l->next;
- }
- /* Create a "must_id" flag for the cleanup ID */
- myrpt->mustid |= (myrpt->idtimer) && (myrpt->keyed || remrx) ;
- /* Build a fresh totx from myrpt->keyed and autopatch activated */
- totx = myrpt->callmode;
- /* If full duplex, add local tx to totx */
- if (myrpt->p.duplex > 1) totx = totx || myrpt->localtx;
- /* Traverse the telemetry list to see what's queued */
- identqueued = 0;
- othertelemqueued = 0;
- tailmessagequeued = 0;
- ctqueued = 0;
- telem = myrpt->tele.next;
- while (telem != &myrpt->tele) {
- if ((telem->mode == ID) || (telem->mode == IDTALKOVER)) {
- identqueued = 1; /* Identification telemetry */
- } else if (telem->mode == TAILMSG) {
- tailmessagequeued = 1; /* Tail message telemetry */
- } else {
- if (telem->mode != UNKEY)
- othertelemqueued = 1; /* Other telemetry */
- else
- ctqueued = 1; /* Courtesy tone telemetry */
- }
- telem = telem->next;
- }
-
- /* Add in any "other" telemetry, if 3/4 or full duplex */
- if (myrpt->p.duplex > 0)
- totx = totx || othertelemqueued;
- /* Update external (to links) transmitter PTT state with everything but ID, CT, and tailmessage telemetry */
- myrpt->exttx = totx;
- /* If half or 3/4 duplex, add localtx to external link tx */
- if (myrpt->p.duplex < 2)
- myrpt->exttx = myrpt->exttx || myrpt->localtx;
- /* Add in ID telemetry to local transmitter */
- totx = totx || remrx;
- /* If 3/4 or full duplex, add in ident and CT telemetry */
- if (myrpt->p.duplex > 0)
- totx = totx || identqueued || ctqueued;
- /* Reset time out timer variables if there is no activity */
- if (!totx) {
- myrpt->totimer = myrpt->p.totime;
- myrpt->tounkeyed = 0;
- myrpt->tonotify = 0;
- } else
- myrpt->tailtimer = myrpt->p.hangtime; /* Initialize tail timer */
- /* Disable the local transmitter if we are timed out */
- totx = totx && myrpt->totimer;
- /* if timed-out and not said already, say it */
- if ((!myrpt->totimer) && (!myrpt->tonotify)) {
- myrpt->tonotify = 1;
- myrpt->timeouts++;
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, TIMEOUT, NULL);
- rpt_mutex_lock(&myrpt->lock);
- }
-
- /* If unkey and re-key, reset time out timer */
- if ((!totx) && (!myrpt->totimer) && (!myrpt->tounkeyed) && (!myrpt->keyed)) {
- myrpt->tounkeyed = 1;
- }
- if ((!totx) && (!myrpt->totimer) && myrpt->tounkeyed && myrpt->keyed) {
- myrpt->totimer = myrpt->p.totime;
- myrpt->tounkeyed = 0;
- myrpt->tonotify = 0;
- rpt_mutex_unlock(&myrpt->lock);
- continue;
- }
- /* if timed-out and in circuit busy after call */
- if ((!totx) && (!myrpt->totimer) && (myrpt->callmode == 4)) {
- myrpt->callmode = 0;
- }
- /* get rid of tail if timed out */
- if (!myrpt->totimer)
- myrpt->tailtimer = 0;
- /* if not timed-out, add in tail */
- if (myrpt->totimer)
- totx = totx || myrpt->tailtimer;
- /* If user or links key up or are keyed up over standard ID, switch to talkover ID, if one is defined */
- /* If tail message, kill the message if someone keys up over it */
- if ((myrpt->keyed || remrx) && ((identqueued && idtalkover) || (tailmessagequeued))) {
- int hasid = 0, hastalkover = 0;
-
- telem = myrpt->tele.next;
- while (telem != &myrpt->tele) {
- if (telem->mode == ID) {
- if (telem->chan)
- ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV); /* Whoosh! */
- hasid = 1;
- }
- if (telem->mode == TAILMSG) {
- if (telem->chan)
- ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV); /* Whoosh! */
- }
- if (telem->mode == IDTALKOVER)
- hastalkover = 1;
- telem = telem->next;
- }
- rpt_mutex_unlock(&myrpt->lock);
- if (hasid && (!hastalkover))
- rpt_telemetry(myrpt, IDTALKOVER, NULL); /* Start Talkover ID */
- rpt_mutex_lock(&myrpt->lock);
- }
- /* Try to be polite */
- /* If the repeater has been inactive for longer than the ID time, do an initial ID in the tail*/
- /* If within 30 seconds of the time to ID, try do it in the tail */
- /* else if at ID time limit, do it right over the top of them */
- /* Lastly, if the repeater has been keyed, and the ID timer is expired, do a clean up ID */
- if (myrpt->mustid && (!myrpt->idtimer))
- queue_id(myrpt);
-
- if ((totx && (!myrpt->exttx) &&
- (myrpt->idtimer <= myrpt->p.politeid) && myrpt->tailtimer)) {
- myrpt->tailid = 1;
- }
-
- /* If tail timer expires, then check for tail messages */
-
- if (myrpt->tailevent) {
- myrpt->tailevent = 0;
- if (myrpt->tailid) {
- totx = 1;
- queue_id(myrpt);
- }
- else if ((myrpt->p.tailmsg.msgs[0]) &&
- (myrpt->p.tailmessagetime) && (myrpt->tmsgtimer == 0)) {
- totx = 1;
- myrpt->tmsgtimer = myrpt->p.tailmessagetime;
- rpt_mutex_unlock(&myrpt->lock);
- rpt_telemetry(myrpt, TAILMSG, NULL);
- rpt_mutex_lock(&myrpt->lock);
- }
- }
-
- /* Main TX control */
-
- /* let telemetry transmit anyway (regardless of timeout) */
- if (myrpt->p.duplex > 0)
- totx = totx || (myrpt->tele.next != &myrpt->tele);
- if (totx && (!lasttx)) {
- lasttx = 1;
- myrpt->dailykeyups++;
- myrpt->totalkeyups++;
- rpt_mutex_unlock(&myrpt->lock);
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_KEY);
- rpt_mutex_lock(&myrpt->lock);
- }
- totx = totx && myrpt->enable;
- if ((!totx) && lasttx) {
- lasttx = 0;
- rpt_mutex_unlock(&myrpt->lock);
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
- rpt_mutex_lock(&myrpt->lock);
- }
- time(&t);
- /* if DTMF timeout */
- if ((!myrpt->cmdnode[0]) && (myrpt->dtmfidx >= 0) && ((myrpt->dtmf_time + DTMF_TIMEOUT) < t)) {
- myrpt->dtmfidx = -1;
- myrpt->dtmfbuf[0] = 0;
- }
- /* if remote DTMF timeout */
- if ((myrpt->rem_dtmfidx >= 0) && ((myrpt->rem_dtmf_time + DTMF_TIMEOUT) < t)) {
- myrpt->rem_dtmfidx = -1;
- myrpt->rem_dtmfbuf[0] = 0;
- }
-
- /* Reconnect */
-
- l = myrpt->links.next;
- while (l != &myrpt->links) {
- if (l->killme) {
- /* remove from queue */
- remque((struct qelem *) l);
- if (!strcmp(myrpt->cmdnode, l->name))
- myrpt->cmdnode[0] = 0;
- rpt_mutex_unlock(&myrpt->lock);
- /* hang-up on call to device */
- if (l->chan)
- ast_hangup(l->chan);
- ast_hangup(l->pchan);
- ast_free(l);
- rpt_mutex_lock(&myrpt->lock);
- /* re-start link traversal */
- l = myrpt->links.next;
- continue;
- }
- l = l->next;
- }
- n = 0;
- cs[n++] = myrpt->rxchannel;
- cs[n++] = myrpt->pchannel;
- cs[n++] = myrpt->txpchannel;
- if (myrpt->txchannel != myrpt->rxchannel)
- cs[n++] = myrpt->txchannel;
- l = myrpt->links.next;
- while (l != &myrpt->links) {
- if ((!l->killme) && (!l->disctime) && l->chan) {
- cs[n++] = l->chan;
- cs[n++] = l->pchan;
- }
- l = l->next;
- }
- rpt_mutex_unlock(&myrpt->lock);
- ms = MSWAIT;
- who = ast_waitfor_n(cs, n, &ms);
- if (who == NULL)
- ms = 0;
- elap = MSWAIT - ms;
- rpt_mutex_lock(&myrpt->lock);
- l = myrpt->links.next;
- while (l != &myrpt->links) {
- if (!l->lasttx) {
- if ((l->retxtimer += elap) >= REDUNDANT_TX_TIME) {
- l->retxtimer = 0;
- if (l->chan)
- ast_indicate(l->chan, AST_CONTROL_RADIO_UNKEY);
- }
- } else
- l->retxtimer = 0;
- if (l->disctime) { /* Disconnect timer active on a channel ? */
- l->disctime -= elap;
- if (l->disctime <= 0) /* Disconnect timer expired on inbound channel ? */
- l->disctime = 0; /* Yep */
- }
-
- if (l->retrytimer) {
- l->retrytimer -= elap;
- if (l->retrytimer < 0)
- l->retrytimer = 0;
- }
-
- /* Tally connect time */
- l->connecttime += elap;
-
- /* ignore non-timing channels */
- if (l->elaptime < 0) {
- l = l->next;
- continue;
- }
- l->elaptime += elap;
- /* if connection has taken too long */
- if ((l->elaptime > MAXCONNECTTIME) &&
- ((!l->chan) || (l->chan->_state != AST_STATE_UP))) {
- l->elaptime = 0;
- rpt_mutex_unlock(&myrpt->lock);
- if (l->chan)
- ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- if ((!l->chan) && (!l->retrytimer) && l->outbound &&
- (l->retries++ < MAX_RETRIES) && (l->hasconnected)) {
- if (l->chan)
- ast_hangup(l->chan);
- rpt_mutex_unlock(&myrpt->lock);
- if ((l->name[0] != '0') && (!l->isremote)) {
- l->retrytimer = MAX_RETRIES + 1;
- } else {
- if (attempt_reconnect(myrpt, l) == -1) {
- l->retrytimer = RETRY_TIMER_MS;
- }
- }
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- if ((!l->chan) && (!l->retrytimer) && l->outbound && (l->retries >= MAX_RETRIES)) {
- /* remove from queue */
- remque((struct qelem *) l);
- if (!strcmp(myrpt->cmdnode, l->name))
- myrpt->cmdnode[0] = 0;
- rpt_mutex_unlock(&myrpt->lock);
- if (l->name[0] != '0') {
- if (!l->hasconnected)
- rpt_telemetry(myrpt, CONNFAIL, l);
- else
- rpt_telemetry(myrpt, REMDISC, l);
- }
- /* hang-up on call to device */
- ast_hangup(l->pchan);
- ast_free(l);
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- if ((!l->chan) && (!l->disctime) && (!l->outbound)) {
- /* remove from queue */
- remque((struct qelem *) l);
- if (!strcmp(myrpt->cmdnode, l->name))
- myrpt->cmdnode[0] = 0;
- rpt_mutex_unlock(&myrpt->lock);
- if (l->name[0] != '0') {
- rpt_telemetry(myrpt, REMDISC, l);
- }
- /* hang-up on call to device */
- ast_hangup(l->pchan);
- ast_free(l);
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- l = l->next;
- }
- if (totx) {
- myrpt->dailytxtime += elap;
- myrpt->totaltxtime += elap;
- }
- i = myrpt->tailtimer;
- if (myrpt->tailtimer)
- myrpt->tailtimer -= elap;
- if (myrpt->tailtimer < 0)
- myrpt->tailtimer = 0;
- if ((i) && (myrpt->tailtimer == 0))
- myrpt->tailevent = 1;
- if (myrpt->totimer)
- myrpt->totimer -= elap;
- if (myrpt->totimer < 0)
- myrpt->totimer = 0;
- if (myrpt->idtimer)
- myrpt->idtimer -= elap;
- if (myrpt->idtimer < 0)
- myrpt->idtimer = 0;
- if (myrpt->tmsgtimer)
- myrpt->tmsgtimer -= elap;
- if (myrpt->tmsgtimer < 0)
- myrpt->tmsgtimer = 0;
- /* do macro timers */
- if (myrpt->macrotimer)
- myrpt->macrotimer -= elap;
- if (myrpt->macrotimer < 0)
- myrpt->macrotimer = 0;
- /* do gosub timers */
- if (myrpt->gosubtimer)
- myrpt->gosubtimer -= elap;
- if (myrpt->gosubtimer < 0)
- myrpt->gosubtimer = 0;
- /* Execute scheduler appx. every 2 tenths of a second */
- if (myrpt->skedtimer <= 0) {
- myrpt->skedtimer = 200;
- do_scheduler(myrpt);
- } else
- myrpt->skedtimer -= elap;
- if (!ms) {
- rpt_mutex_unlock(&myrpt->lock);
- continue;
- }
- c = myrpt->macrobuf[0];
- if (c && (!myrpt->macrotimer)) {
- myrpt->macrotimer = MACROTIME;
- memmove(myrpt->macrobuf, myrpt->macrobuf + 1, sizeof(myrpt->macrobuf) - 1);
- if ((c == 'p') || (c == 'P'))
- myrpt->macrotimer = MACROPTIME;
- rpt_mutex_unlock(&myrpt->lock);
- local_dtmf_helper(myrpt, c);
- } else
- rpt_mutex_unlock(&myrpt->lock);
- c = myrpt->gosubbuf[0];
- if (c && (!myrpt->gosubtimer)) {
- myrpt->gosubtimer = GOSUBTIME;
- memmove(myrpt->gosubbuf, myrpt->gosubbuf + 1, sizeof(myrpt->gosubbuf) - 1);
- if ((c == 'p') || (c == 'P'))
- myrpt->gosubtimer = GOSUBPTIME;
- rpt_mutex_unlock(&myrpt->lock);
- local_dtmf_helper(myrpt, c);
- } else
- rpt_mutex_unlock(&myrpt->lock);
- if (who == myrpt->rxchannel) { /* if it was a read from rx */
- f = ast_read(myrpt->rxchannel);
- if (!f) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- break;
- }
- if (f->frametype == AST_FRAME_VOICE) {
-#ifdef _MDC_DECODE_H_
- unsigned char ubuf[2560];
- short *sp;
- int n;
-#endif
-
- if (!myrpt->localtx) {
- memset(f->data, 0, f->datalen);
- }
-
-#ifdef _MDC_DECODE_H_
- sp = (short *) f->data;
- /* convert block to unsigned char */
- for (n = 0; n < f->datalen / 2; n++) {
- ubuf[n] = (*sp++ >> 8) + 128;
- }
- n = mdc_decoder_process_samples(myrpt->mdc, ubuf, f->datalen / 2);
- if (n == 1) {
- unsigned char op, arg;
- unsigned short unitID;
-
- mdc_decoder_get_packet(myrpt->mdc, &op, &arg, &unitID);
- if (debug > 2) {
- ast_log(LOG_NOTICE, "Got (single-length) packet:\n");
- ast_log(LOG_NOTICE, "op: %02x, arg: %02x, UnitID: %04x\n", op & 255, arg & 255, unitID);
- }
- if ((op == 1) && (arg == 0)) {
- myrpt->lastunit = unitID;
- }
- }
- if ((debug > 2) && (i == 2)) {
- unsigned char op, arg, ex1, ex2, ex3, ex4;
- unsigned short unitID;
-
- mdc_decoder_get_double_packet(myrpt->mdc, &op, &arg, &unitID, &ex1, &ex2, &ex3, &ex4);
- ast_log(LOG_NOTICE, "Got (double-length) packet:\n");
- ast_log(LOG_NOTICE, "op: %02x, arg: %02x, UnitID: %04x\n", op & 255, arg & 255, unitID);
- ast_log(LOG_NOTICE, "ex1: %02x, ex2: %02x, ex3: %02x, ex4: %02x\n",
- ex1 & 255, ex2 & 255, ex3 & 255, ex4 & 255);
- }
-#endif
-#ifdef __RPT_NOTCH
- /* apply inbound filters, if any */
- rpt_filter(myrpt, f->data, f->datalen / 2);
-#endif
- ast_write(myrpt->pchannel, f);
- } else if (f->frametype == AST_FRAME_DTMF) {
- c = (char) f->subclass; /* get DTMF char */
- ast_frfree(f);
- if (!myrpt->keyed)
- continue;
- local_dtmf_helper(myrpt, c);
- continue;
- } else if (f->frametype == AST_FRAME_CONTROL) {
- if (f->subclass == AST_CONTROL_HANGUP) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- ast_frfree(f);
- break;
- }
- /* if RX key */
- if (f->subclass == AST_CONTROL_RADIO_KEY) {
- if ((!lasttx) || (myrpt->p.duplex > 1)) {
- ast_debug(8, "@@@@ rx key\n");
- myrpt->keyed = 1;
- }
- }
- /* if RX un-key */
- if (f->subclass == AST_CONTROL_RADIO_UNKEY) {
- if ((!lasttx) || (myrpt->p.duplex > 1)) {
- ast_debug(8, "@@@@ rx un-key\n");
- if (myrpt->keyed) {
- rpt_telemetry(myrpt, UNKEY, NULL);
- }
- myrpt->keyed = 0;
- }
- }
- }
- ast_frfree(f);
- continue;
- }
- if (who == myrpt->pchannel) { /* if it was a read from pseudo */
- f = ast_read(myrpt->pchannel);
- if (!f) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- break;
- }
- if (f->frametype == AST_FRAME_VOICE) {
- ast_write(myrpt->txpchannel, f);
- }
- if (f->frametype == AST_FRAME_CONTROL) {
- if (f->subclass == AST_CONTROL_HANGUP) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- ast_frfree(f);
- break;
- }
- }
- ast_frfree(f);
- continue;
- }
- if (who == myrpt->txchannel) { /* if it was a read from tx */
- f = ast_read(myrpt->txchannel);
- if (!f) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- break;
- }
- if (f->frametype == AST_FRAME_CONTROL) {
- if (f->subclass == AST_CONTROL_HANGUP) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- ast_frfree(f);
- break;
- }
- }
- ast_frfree(f);
- continue;
- }
- toexit = 0;
- rpt_mutex_lock(&myrpt->lock);
- l = myrpt->links.next;
- while (l != &myrpt->links) {
- if (l->disctime) {
- l = l->next;
- continue;
- }
- if (who == l->chan) { /* if it was a read from rx */
- remrx = 0;
- /* see if any other links are receiving */
- m = myrpt->links.next;
- while (m != &myrpt->links) {
- /* if not us, count it */
- if ((m != l) && (m->lastrx))
- remrx = 1;
- m = m->next;
- }
- rpt_mutex_unlock(&myrpt->lock);
- totx = (((l->isremote) ? myrpt->localtx :
- myrpt->exttx) || remrx) && l->mode;
- if (l->chan && (l->lasttx != totx)) {
- if (totx) {
- ast_indicate(l->chan, AST_CONTROL_RADIO_KEY);
- } else {
- ast_indicate(l->chan, AST_CONTROL_RADIO_UNKEY);
- }
- }
- l->lasttx = totx;
- f = ast_read(l->chan);
- if (!f) {
- if ((!l->disced) && (!l->outbound)) {
- if ((l->name[0] == '0') || l->isremote)
- l->disctime = 1;
- else
- l->disctime = DISC_TIME;
- rpt_mutex_lock(&myrpt->lock);
- ast_hangup(l->chan);
- l->chan = 0;
- break;
- }
-
- if (l->retrytimer) {
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- if (l->outbound && (l->retries++ < MAX_RETRIES) && (l->hasconnected)) {
- rpt_mutex_lock(&myrpt->lock);
- ast_hangup(l->chan);
- l->chan = 0;
- rpt_mutex_unlock(&myrpt->lock);
- if (attempt_reconnect(myrpt, l) == -1) {
- l->retrytimer = RETRY_TIMER_MS;
- }
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- rpt_mutex_lock(&myrpt->lock);
- /* remove from queue */
- remque((struct qelem *) l);
- if (!strcmp(myrpt->cmdnode, l->name))
- myrpt->cmdnode[0] = 0;
- rpt_mutex_unlock(&myrpt->lock);
- if (!l->hasconnected)
- rpt_telemetry(myrpt, CONNFAIL, l);
- else if (l->disced != 2)
- rpt_telemetry(myrpt, REMDISC, l);
- /* hang-up on call to device */
- ast_hangup(l->chan);
- ast_hangup(l->pchan);
- ast_free(l);
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- if (f->frametype == AST_FRAME_VOICE) {
- if (!l->lastrx) {
- memset(f->data, 0, f->datalen);
- }
- ast_write(l->pchan, f);
- }
- if (f->frametype == AST_FRAME_TEXT) {
- handle_link_data(myrpt, l, f->data);
- }
- if (f->frametype == AST_FRAME_DTMF) {
- handle_link_phone_dtmf(myrpt, l, f->subclass);
- }
- if (f->frametype == AST_FRAME_CONTROL) {
- if (f->subclass == AST_CONTROL_ANSWER) {
- char lconnected = l->connected;
- l->connected = 1;
- l->hasconnected = 1;
- l->elaptime = -1;
- l->retries = 0;
- if (!lconnected)
- rpt_telemetry(myrpt, CONNECTED, l);
- else
- l->reconnects++;
- }
- /* if RX key */
- if (f->subclass == AST_CONTROL_RADIO_KEY) {
- ast_debug(8, "@@@@ rx key\n");
- l->lastrx = 1;
- }
- /* if RX un-key */
- if (f->subclass == AST_CONTROL_RADIO_UNKEY) {
- ast_debug(8, "@@@@ rx un-key\n");
- l->lastrx = 0;
- }
- if (f->subclass == AST_CONTROL_HANGUP) {
- ast_frfree(f);
- if ((!l->outbound) && (!l->disced)) {
- if ((l->name[0] == '0') || l->isremote)
- l->disctime = 1;
- else
- l->disctime = DISC_TIME;
- rpt_mutex_lock(&myrpt->lock);
- ast_hangup(l->chan);
- l->chan = 0;
- break;
- }
- if (l->retrytimer) {
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- if (l->outbound && (l->retries++ < MAX_RETRIES) && (l->hasconnected)) {
- rpt_mutex_lock(&myrpt->lock);
- ast_hangup(l->chan);
- l->chan = 0;
- rpt_mutex_unlock(&myrpt->lock);
- if (attempt_reconnect(myrpt, l) == -1) {
- l->retrytimer = RETRY_TIMER_MS;
- }
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- rpt_mutex_lock(&myrpt->lock);
- /* remove from queue */
- remque((struct qelem *) l);
- if (!strcmp(myrpt->cmdnode, l->name))
- myrpt->cmdnode[0] = 0;
- rpt_mutex_unlock(&myrpt->lock);
- if (!l->hasconnected)
- rpt_telemetry(myrpt, CONNFAIL, l);
- else if (l->disced != 2)
- rpt_telemetry(myrpt, REMDISC, l);
- /* hang-up on call to device */
- ast_hangup(l->chan);
- ast_hangup(l->pchan);
- ast_free(l);
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- }
- ast_frfree(f);
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- if (who == l->pchan) {
- rpt_mutex_unlock(&myrpt->lock);
- f = ast_read(l->pchan);
- if (!f) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- toexit = 1;
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- if (f->frametype == AST_FRAME_VOICE) {
- if (l->chan)
- ast_write(l->chan, f);
- }
- if (f->frametype == AST_FRAME_CONTROL) {
- if (f->subclass == AST_CONTROL_HANGUP) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- ast_frfree(f);
- toexit = 1;
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- }
- ast_frfree(f);
- rpt_mutex_lock(&myrpt->lock);
- break;
- }
- l = l->next;
- }
- rpt_mutex_unlock(&myrpt->lock);
- if (toexit)
- break;
- if (who == myrpt->txpchannel) { /* if it was a read from remote tx */
- f = ast_read(myrpt->txpchannel);
- if (!f) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- break;
- }
- if (f->frametype == AST_FRAME_CONTROL) {
- if (f->subclass == AST_CONTROL_HANGUP) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- ast_frfree(f);
- break;
- }
- }
- ast_frfree(f);
- continue;
- }
- }
- usleep(100000);
- ast_hangup(myrpt->pchannel);
- ast_hangup(myrpt->txpchannel);
- if (myrpt->txchannel != myrpt->rxchannel)
- ast_hangup(myrpt->txchannel);
- ast_hangup(myrpt->rxchannel);
- rpt_mutex_lock(&myrpt->lock);
- l = myrpt->links.next;
- while (l != &myrpt->links) {
- struct rpt_link *ll = l;
- /* remove from queue */
- remque((struct qelem *) l);
- /* hang-up on call to device */
- if (l->chan)
- ast_hangup(l->chan);
- ast_hangup(l->pchan);
- l = l->next;
- ast_free(ll);
- }
- rpt_mutex_unlock(&myrpt->lock);
- ast_debug(1, "@@@@ rpt:Hung up channel\n");
- myrpt->rpt_thread = AST_PTHREADT_STOP;
- pthread_exit(NULL);
- return NULL;
-}
-
-
-static void *rpt_master(void *config)
-{
- int i, n;
- struct ast_config *cfg;
- char *this;
- const char *val;
-
- /* go thru all the specified repeaters */
- this = NULL;
- n = 0;
- rpt_vars[n].cfg = config;
- cfg = rpt_vars[n].cfg;
- if (!cfg) {
- ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n");
- pthread_exit(NULL);
- }
- while ((this = ast_category_browse(cfg, this)) != NULL) {
- for (i = 0; i < strlen(this); i++) {
- if ((this[i] < '0') || (this[i] > '9'))
- break;
- }
- if (i != strlen(this))
- continue; /* Not a node defn */
- memset(&rpt_vars[n], 0, sizeof(rpt_vars[n]));
- rpt_vars[n].name = ast_strdup(this);
- val = ast_variable_retrieve(cfg, this, "rxchannel");
- if (val)
- rpt_vars[n].rxchanname = ast_strdup(val);
- val = ast_variable_retrieve(cfg, this, "txchannel");
- if (val)
- rpt_vars[n].txchanname = ast_strdup(val);
- val = ast_variable_retrieve(cfg, this, "remote");
- if (val)
- rpt_vars[n].remote = ast_strdup(val);
- ast_mutex_init(&rpt_vars[n].lock);
- rpt_vars[n].tele.next = &rpt_vars[n].tele;
- rpt_vars[n].tele.prev = &rpt_vars[n].tele;
- rpt_vars[n].rpt_thread = AST_PTHREADT_NULL;
- rpt_vars[n].tailmessagen = 0;
-#ifdef _MDC_DECODE_H_
- rpt_vars[n].mdc = mdc_decoder_new(8000);
-#endif
- n++;
- }
- nrpts = n;
- ast_config_destroy(cfg);
-
- /* start em all */
- for (i = 0; i < n; i++) {
- load_rpt_vars(i, 1);
-
- /* if is a remote, dont start one for it */
- if (rpt_vars[i].remote) {
- ast_copy_string(rpt_vars[i].freq, "146.580", sizeof(rpt_vars[i].freq));
- ast_copy_string(rpt_vars[i].rxpl, "100.0", sizeof(rpt_vars[i].rxpl));
- ast_copy_string(rpt_vars[i].txpl, "100.0", sizeof(rpt_vars[i].txpl));
- rpt_vars[i].remmode = REM_MODE_FM;
- rpt_vars[i].offset = REM_SIMPLEX;
- rpt_vars[i].powerlevel = REM_MEDPWR;
- continue;
- }
- if (!rpt_vars[i].p.ident) {
- ast_log(LOG_WARNING, "Did not specify ident for node %s\n", rpt_vars[i].name);
- ast_config_destroy(cfg);
- pthread_exit(NULL);
- }
- ast_pthread_create_detached(&rpt_vars[i].rpt_thread, NULL, rpt, (void *) &rpt_vars[i]);
- }
- usleep(500000);
- for (;;) {
- /* Now monitor each thread, and restart it if necessary */
- for (i = 0; i < n; i++) {
- int rv;
- if (rpt_vars[i].remote)
- continue;
- if (rpt_vars[i].rpt_thread == AST_PTHREADT_STOP)
- rv = -1;
- else
- rv = pthread_kill(rpt_vars[i].rpt_thread, 0);
- if (rv) {
- if (time(NULL) - rpt_vars[i].lastthreadrestarttime <= 15) {
- if (rpt_vars[i].threadrestarts >= 5) {
- ast_log(LOG_ERROR, "Continual RPT thread restarts, killing Asterisk\n");
- ast_cli_command(STDERR_FILENO, "restart now");
- } else {
- ast_log(LOG_NOTICE, "RPT thread restarted on %s\n", rpt_vars[i].name);
- rpt_vars[i].threadrestarts++;
- }
- } else
- rpt_vars[i].threadrestarts = 0;
-
- rpt_vars[i].lastthreadrestarttime = time(NULL);
- ast_pthread_create_detached(&rpt_vars[i].rpt_thread, NULL, rpt, (void *) &rpt_vars[i]);
- ast_log(LOG_WARNING, "rpt_thread restarted on node %s\n", rpt_vars[i].name);
- }
-
- }
- usleep(2000000);
- }
- ast_config_destroy(cfg);
- pthread_exit(NULL);
-}
-
-static int rpt_exec(struct ast_channel *chan, void *data)
-{
- int res = -1, i, rem_totx, n, phone_mode = 0;
- char *tmp, keyed = 0;
- char *options = NULL, *tele, c;
- struct rpt *myrpt;
- struct ast_frame *f;
- struct ast_channel *who;
- struct ast_channel *cs[20];
- struct rpt_link *l;
- ZT_CONFINFO ci; /* conference info */
- ZT_PARAMS par;
- int ms, elap;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(node);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "Rpt requires an argument (system node)\n");
- return -1;
- }
- tmp = ast_strdupa((char *)data);
- AST_STANDARD_APP_ARGS(args, tmp);
- myrpt = NULL;
- /* see if we can find our specified one */
- for (i = 0; i < nrpts; i++) {
- /* if name matches, assign it and exit loop */
- if (!strcmp(args.node, rpt_vars[i].name)) {
- myrpt = &rpt_vars[i];
- break;
- }
- }
- if (myrpt == NULL) {
- ast_log(LOG_WARNING, "Cannot find specified system node %s\n", args.node);
- return -1;
- }
-
- /* if not phone access, must be an IAX connection */
- if (options && ((*options == 'P') || (*options == 'D') || (*options == 'R'))) {
- phone_mode = 1;
- if (*options == 'D')
- phone_mode = 2;
- ast_set_callerid(chan, "0", "app_rpt user", "0");
- } else {
- if (strncmp(chan->name, "IAX2", 4)) {
- ast_log(LOG_WARNING, "We only accept links via IAX2!!\n");
- return -1;
- }
- }
- if (*args.options == 'R') {
- /* Parts of this section taken from app_parkandannounce */
- int m, lot, timeout = 0;
- char tmp[256];
- char *s;
- AST_DECLARE_APP_ARGS(optionarg,
- AST_APP_ARG(template);
- AST_APP_ARG(timeout);
- AST_APP_ARG(return_context);
- );
-
- rpt_mutex_lock(&myrpt->lock);
- m = myrpt->callmode;
- rpt_mutex_unlock(&myrpt->lock);
-
- if ((!myrpt->p.nobusyout) && m) {
- if (chan->_state != AST_STATE_UP) {
- ast_indicate(chan, AST_CONTROL_BUSY);
- }
- while (ast_safe_sleep(chan, 10000) != -1) {
- /* This used to be a busy loop. It's probably better to yield the processor here. */
- usleep(1);
- }
- return -1;
- }
-
- if (chan->_state != AST_STATE_UP) {
- ast_answer(chan);
- }
-
- s = ast_strdupa(options);
- AST_STANDARD_APP_ARGS(optionarg, s);
- if (optionarg.argc == 0 || ast_strlen_zero(optionarg.template)) {
- ast_log(LOG_WARNING, "An announce template must be defined\n");
- return -1;
- }
-
- if (optionarg.argc >= 2) {
- timeout = atoi(optionarg.timeout) * 1000;
- }
-
- if (!ast_strlen_zero(optionarg.return_context)) {
- if (ast_parseable_goto(chan, optionarg.return_context)) {
- ast_verb(3, "Warning: Return Context Invalid, call will return to default|s\n");
- }
- }
-
- /* we are using masq_park here to protect * from touching the channel once we park it. If the channel comes out of timeout
- before we are done announcing and the channel is messed with, Kablooeee. So we use Masq to prevent this. */
-
- ast_masq_park_call(chan, NULL, timeout, &lot);
- ast_verb(3, "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, optionarg.return_context);
-
- snprintf(tmp, sizeof(tmp), "%d,%s", lot, optionarg.template + 1);
- rpt_telemetry(myrpt, REV_PATCH, tmp);
-
- return 0;
- }
-
- if (!options) {
- struct ast_hostent ahp;
- struct hostent *hp;
- struct in_addr ia;
- char hisip[100], nodeip[100];
- const char *val;
- char *s, *s1, *s2;
-
- /* look at callerid to see what node this comes from */
- if (!chan->cid.cid_num) { /* if doesn't have caller id */
- ast_log(LOG_WARNING, "Doesn't have callerid on %s\n", args.node);
- return -1;
- }
-
- /* get his IP from IAX2 module */
- pbx_substitute_variables_helper(chan, "${IAXPEER(CURRENTCHANNEL)}", hisip, sizeof(hisip) - 1);
- if (ast_strlen_zero(hisip)) {
- ast_log(LOG_WARNING, "Link IP address cannot be determined!!\n");
- return -1;
- }
-
- if (!strcmp(myrpt->name, chan->cid.cid_num)) {
- ast_log(LOG_WARNING, "Trying to link to self!!\n");
- return -1;
- }
-
- if (*(chan->cid.cid_num) < '1') {
- ast_log(LOG_WARNING, "Node %s Invalid for connection here!!\n", chan->cid.cid_num);
- return -1;
- }
-
- /* look for his reported node string */
- val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, chan->cid.cid_num);
- if (!val) {
- ast_log(LOG_WARNING, "Reported node %s cannot be found!!\n", chan->cid.cid_num);
- return -1;
- }
- ast_copy_string(tmp, val, sizeof(tmp));
- s = tmp;
- s1 = strsep(&s, ",");
- s2 = strsep(&s, ",");
- if (!s2) {
- ast_log(LOG_WARNING, "Reported node %s not in correct format!!\n", chan->cid.cid_num);
- return -1;
- }
- if (strcmp(s2, "NONE")) {
- hp = ast_gethostbyname(s2, &ahp);
- if (!hp) {
- ast_log(LOG_WARNING, "Reported node %s, name %s cannot be found!!\n", chan->cid.cid_num, s2);
- return -1;
- }
- memcpy(&ia, hp->h_addr, sizeof(in_addr_t));
- ast_copy_string(nodeip, ast_inet_ntoa(ia), sizeof(nodeip));
- if (strcmp(hisip, nodeip)) {
- char *s3 = strchr(s1, '@');
- if (s3)
- s1 = s3 + 1;
- s3 = strchr(s1, '/');
- if (s3)
- *s3 = 0;
- hp = ast_gethostbyname(s1, &ahp);
- if (!hp) {
- ast_log(LOG_WARNING, "Reported node %s, name %s cannot be found!!\n", chan->cid.cid_num, s1);
- return -1;
- }
- memcpy(&ia, hp->h_addr, sizeof(in_addr_t));
- ast_copy_string(nodeip, ast_inet_ntoa(ia), sizeof(nodeip));
- if (strcmp(hisip, nodeip)) {
- ast_log(LOG_WARNING, "Node %s IP %s does not match link IP %s!!\n", chan->cid.cid_num, nodeip, hisip);
- return -1;
- }
- }
- }
- }
-
- /* if is not a remote */
- if (!myrpt->remote) {
- int reconnects = 0;
-
- /* look at callerid to see what node this comes from */
- if (!chan->cid.cid_num) { /* if doesn't have caller id */
- ast_log(LOG_WARNING, "Doesnt have callerid on %s\n", args.node);
- return -1;
- }
-
- if (!strcmp(myrpt->name, chan->cid.cid_num)) {
- ast_log(LOG_WARNING, "Trying to link to self!!\n");
- return -1;
- }
- rpt_mutex_lock(&myrpt->lock);
- l = myrpt->links.next;
- /* try to find this one in queue */
- while (l != &myrpt->links) {
- if (l->name[0] == '0') {
- l = l->next;
- continue;
- }
- /* if found matching string */
- if (!strcmp(l->name, chan->cid.cid_num))
- break;
- l = l->next;
- }
- /* if found */
- if (l != &myrpt->links) {
- l->killme = 1;
- l->retries = MAX_RETRIES + 1;
- l->disced = 2;
- reconnects = l->reconnects;
- reconnects++;
- rpt_mutex_unlock(&myrpt->lock);
- usleep(500000);
- } else
- rpt_mutex_unlock(&myrpt->lock);
- /* establish call in tranceive mode */
- l = ast_calloc(1, sizeof(*l));
- if (!l) {
- ast_log(LOG_WARNING, "Unable to malloc\n");
- pthread_exit(NULL);
- }
- l->mode = 1;
- ast_copy_string(l->name, chan->cid.cid_num, sizeof(l->name));
- l->isremote = 0;
- l->chan = chan;
- l->connected = 1;
- l->hasconnected = 1;
- l->reconnects = reconnects;
- l->phonemode = phone_mode;
- ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
- ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
- /* allocate a pseudo-channel thru asterisk */
- l->pchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
- if (!l->pchan) {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
- pthread_exit(NULL);
- }
- ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR);
- ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR);
- /* make a conference for the tx */
- ci.chan = 0;
- ci.confno = myrpt->conf;
- ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
- /* first put the channel on the conference in proper mode */
- if (ioctl(l->pchan->fds[0], ZT_SETCONF, &ci) == -1) {
- ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
- pthread_exit(NULL);
- }
- rpt_mutex_lock(&myrpt->lock);
- if (phone_mode > 1)
- l->lastrx = 1;
- /* insert at end of queue */
- insque((struct qelem *)l, (struct qelem *)myrpt->links.next);
- rpt_mutex_unlock(&myrpt->lock);
- if (chan->_state != AST_STATE_UP) {
- ast_answer(chan);
- }
- return AST_PBX_KEEPALIVE;
- }
- rpt_mutex_lock(&myrpt->lock);
- /* if remote, error if anyone else already linked */
- if (myrpt->remoteon) {
- rpt_mutex_unlock(&myrpt->lock);
- usleep(500000);
- if (myrpt->remoteon) {
- ast_log(LOG_WARNING, "Trying to use busy link on %s\n", args.node);
- return -1;
- }
- rpt_mutex_lock(&myrpt->lock);
- }
- myrpt->remoteon = 1;
- if (ioperm(myrpt->p.iobase, 1, 1) == -1) {
- rpt_mutex_unlock(&myrpt->lock);
- ast_log(LOG_WARNING, "Cant get io permission on IO port %x hex\n", myrpt->p.iobase);
- return -1;
- }
- rpt_mutex_unlock(&myrpt->lock);
- /* find our index, and load the vars initially */
- for (i = 0; i < nrpts; i++) {
- if (&rpt_vars[i] == myrpt) {
- load_rpt_vars(i, 0);
- break;
- }
- }
- rpt_mutex_lock(&myrpt->lock);
- tele = strchr(myrpt->rxchanname, '/');
- if (!tele) {
- ast_log(LOG_ERROR, "rpt:Dial number must be in format tech/number\n");
- rpt_mutex_unlock(&myrpt->lock);
- pthread_exit(NULL);
- }
- *tele++ = 0;
- myrpt->rxchannel = ast_request(myrpt->rxchanname, AST_FORMAT_SLINEAR, tele, NULL);
- if (myrpt->rxchannel) {
- ast_set_read_format(myrpt->rxchannel, AST_FORMAT_SLINEAR);
- ast_set_write_format(myrpt->rxchannel, AST_FORMAT_SLINEAR);
- myrpt->rxchannel->whentohangup = 0;
- myrpt->rxchannel->appl = "Apprpt";
- myrpt->rxchannel->data = "(Link Rx)";
- ast_verb(3, "rpt (Rx) initiating call to %s/%s on %s\n",
- myrpt->rxchanname, tele, myrpt->rxchannel->name);
- rpt_mutex_unlock(&myrpt->lock);
- ast_call(myrpt->rxchannel, tele, 999);
- rpt_mutex_lock(&myrpt->lock);
- } else {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Rx channel\n");
- rpt_mutex_unlock(&myrpt->lock);
- pthread_exit(NULL);
- }
- *--tele = '/';
- if (myrpt->txchanname) {
- tele = strchr(myrpt->txchanname, '/');
- if (!tele) {
- ast_log(LOG_ERROR, "rpt:Dial number must be in format tech/number\n");
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->rxchannel);
- pthread_exit(NULL);
- }
- *tele++ = 0;
- myrpt->txchannel = ast_request(myrpt->txchanname, AST_FORMAT_SLINEAR, tele, NULL);
- if (myrpt->txchannel) {
- ast_set_read_format(myrpt->txchannel, AST_FORMAT_SLINEAR);
- ast_set_write_format(myrpt->txchannel, AST_FORMAT_SLINEAR);
- myrpt->txchannel->whentohangup = 0;
- myrpt->txchannel->appl = "Apprpt";
- myrpt->txchannel->data = "(Link Tx)";
- ast_verb(3, "rpt (Tx) initiating call to %s/%s on %s\n",
- myrpt->txchanname, tele, myrpt->txchannel->name);
- rpt_mutex_unlock(&myrpt->lock);
- ast_call(myrpt->txchannel, tele, 999);
- rpt_mutex_lock(&myrpt->lock);
- } else {
- ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Tx channel\n");
- rpt_mutex_unlock(&myrpt->lock);
- ast_hangup(myrpt->rxchannel);
- pthread_exit(NULL);
- }
- *--tele = '/';
- } else {
- myrpt->txchannel = myrpt->rxchannel;
- }
- myrpt->remoterx = 0;
- myrpt->remotetx = 0;
- myrpt->retxtimer = 0;
- myrpt->remoteon = 1;
- myrpt->dtmfidx = -1;
- myrpt->dtmfbuf[0] = 0;
- myrpt->dtmf_time_rem = 0;
- myrpt->hfscanmode = 0;
- myrpt->hfscanstatus = 0;
- if (myrpt->p.startupmacro) {
- myrpt->remchannel = chan; /* Save copy of channel */
- snprintf(myrpt->macrobuf, sizeof(myrpt->macrobuf), "PPPP%s", myrpt->p.startupmacro);
- }
- if (myrpt->p.startupgosub) {
- myrpt->remchannel = chan; /* Save copy of channel */
- snprintf(myrpt->gosubbuf, sizeof(myrpt->gosubbuf), "PPPP%s", myrpt->p.startupgosub);
- }
- myrpt->reload = 0;
- rpt_mutex_unlock(&myrpt->lock);
- setrem(myrpt);
- ast_set_write_format(chan, AST_FORMAT_SLINEAR);
- ast_set_read_format(chan, AST_FORMAT_SLINEAR);
- /* if we are on 2w loop and are a remote, turn EC on */
- if (myrpt->remote && (myrpt->rxchannel == myrpt->txchannel)) {
- i = 128;
- ioctl(myrpt->rxchannel->fds[0], ZT_ECHOCANCEL, &i);
- }
- if (chan->_state != AST_STATE_UP) {
- ast_answer(chan);
- }
-
- if (ioctl(myrpt->txchannel->fds[0], ZT_GET_PARAMS, &par) != -1) {
- if (par.rxisoffhook) {
- ast_indicate(chan, AST_CONTROL_RADIO_KEY);
- myrpt->remoterx = 1;
- }
- }
- n = 0;
- cs[n++] = chan;
- cs[n++] = myrpt->rxchannel;
- if (myrpt->rxchannel != myrpt->txchannel)
- cs[n++] = myrpt->txchannel;
- for (;;) {
- if (ast_check_hangup(chan))
- break;
- if (ast_check_hangup(myrpt->rxchannel))
- break;
- if (myrpt->reload) {
- myrpt->reload = 0;
- rpt_mutex_unlock(&myrpt->lock);
- /* find our index, and load the vars */
- for (i = 0; i < nrpts; i++) {
- if (&rpt_vars[i] == myrpt) {
- load_rpt_vars(i, 0);
- break;
- }
- }
- rpt_mutex_lock(&myrpt->lock);
- }
- ms = MSWAIT;
- who = ast_waitfor_n(cs, n, &ms);
- if (who == NULL)
- ms = 0;
- elap = MSWAIT - ms;
- if (myrpt->macrotimer)
- myrpt->macrotimer -= elap;
- if (myrpt->macrotimer < 0)
- myrpt->macrotimer = 0;
- if (myrpt->gosubtimer)
- myrpt->gosubtimer -= elap;
- if (myrpt->gosubtimer < 0)
- myrpt->gosubtimer = 0;
- rpt_mutex_unlock(&myrpt->lock);
- if (!ms)
- continue;
- rem_totx = keyed;
-
-
- if ((!myrpt->remoterx) && (!myrpt->remotetx)) {
- if ((myrpt->retxtimer += elap) >= REDUNDANT_TX_TIME) {
- myrpt->retxtimer = 0;
- ast_indicate(chan, AST_CONTROL_RADIO_UNKEY);
- }
- } else
- myrpt->retxtimer = 0;
- if (rem_totx && (!myrpt->remotetx)) { /* Remote base radio TX key */
- myrpt->remotetx = 1;
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_KEY);
- }
- if ((!rem_totx) && myrpt->remotetx) { /* Remote base radio TX unkey */
- myrpt->remotetx = 0;
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
- }
-
- if (myrpt->tunerequest && (!strcmp(myrpt->remote, remote_rig_ft897))) { /* ft-897 specific for now... */
- myrpt->tunerequest = 0;
- set_mode_ft897(myrpt, REM_MODE_AM);
- simple_command_ft897(myrpt, 8);
- myrpt->remotetx = 0;
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
- if (!myrpt->remoterx)
- ast_indicate(chan, AST_CONTROL_RADIO_KEY);
- if (play_tone(chan, 800, 6000, 8192) == -1)
- break;
-
- rmt_telem_finish(myrpt, chan);
- set_mode_ft897(myrpt, 0x88);
- setrem(myrpt);
- }
-
- if (myrpt->hfscanmode) {
- myrpt->scantimer -= elap;
- if (myrpt->scantimer <= 0) {
- myrpt->scantimer = REM_SCANTIME;
- service_scan(myrpt);
- }
- }
- if (who == chan) { /* if it was a read from incoming */
- f = ast_read(chan);
- if (!f) {
- ast_debug(1, "@@@@ link:Hung Up\n");
- break;
- }
- if (f->frametype == AST_FRAME_VOICE) {
- /* if not transmitting, zero-out audio */
- if (!myrpt->remotetx)
- memset(f->data, 0, f->datalen);
- ast_write(myrpt->txchannel, f);
- }
- if (f->frametype == AST_FRAME_DTMF) {
- myrpt->remchannel = chan; /* Save copy of channel */
- if (handle_remote_phone_dtmf(myrpt, f->subclass, &keyed, phone_mode) == -1) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- ast_frfree(f);
- break;
- }
- }
- if (f->frametype == AST_FRAME_TEXT) {
- myrpt->remchannel = chan; /* Save copy of channel */
- if (handle_remote_data(myrpt, f->data) == -1) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- ast_frfree(f);
- break;
- }
- }
- if (f->frametype == AST_FRAME_CONTROL) {
- if (f->subclass == AST_CONTROL_HANGUP) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- ast_frfree(f);
- break;
- }
- /* if RX key */
- if (f->subclass == AST_CONTROL_RADIO_KEY) {
- ast_debug(8, "@@@@ rx key\n");
- keyed = 1;
- }
- /* if RX un-key */
- if (f->subclass == AST_CONTROL_RADIO_UNKEY) {
- ast_debug(8, "@@@@ rx un-key\n");
- keyed = 0;
- }
- }
- if (myrpt->hfscanstatus) {
- myrpt->remchannel = chan; /* Save copy of channel */
- myrpt->remotetx = 0;
- ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
- if (!myrpt->remoterx) {
- ast_indicate(myrpt->remchannel, AST_CONTROL_RADIO_KEY);
- }
- if (myrpt->hfscanstatus < 0) {
- if (myrpt->hfscanstatus == -1) {
- if (ast_safe_sleep(myrpt->remchannel, 1000) == -1)
- break;
- }
- sayfile(myrpt->remchannel, "rpt/stop");
- } else {
- saynum(myrpt->remchannel, myrpt->hfscanstatus );
- }
- rmt_telem_finish(myrpt, myrpt->remchannel);
- myrpt->hfscanstatus = 0;
- }
- ast_frfree(f);
- rpt_mutex_lock(&myrpt->lock);
- c = myrpt->macrobuf[0];
- if (c && (!myrpt->macrotimer)) {
- myrpt->macrotimer = MACROTIME;
- memmove(myrpt->macrobuf, myrpt->macrobuf + 1, sizeof(myrpt->macrobuf) - 1);
- if ((c == 'p') || (c == 'P'))
- myrpt->macrotimer = MACROPTIME;
- rpt_mutex_unlock(&myrpt->lock);
- if (handle_remote_dtmf_digit(myrpt, c, &keyed, 0) == -1)
- break;
- continue;
- }
- c = myrpt->gosubbuf[0];
- if (c && (!myrpt->gosubtimer)) {
- myrpt->gosubtimer = GOSUBTIME;
- memmove(myrpt->gosubbuf, myrpt->gosubbuf + 1, sizeof(myrpt->gosubbuf) - 1);
- if ((c == 'p') || (c == 'P'))
- myrpt->gosubtimer = GOSUBPTIME;
- rpt_mutex_unlock(&myrpt->lock);
- if (handle_remote_dtmf_digit(myrpt, c, &keyed, 0) == -1)
- break;
- continue;
- }
- rpt_mutex_unlock(&myrpt->lock);
- continue;
- }
- if (who == myrpt->rxchannel) { /* if it was a read from radio */
- f = ast_read(myrpt->rxchannel);
- if (!f) {
- ast_debug(1, "@@@@ link:Hung Up\n");
- break;
- }
- if (f->frametype == AST_FRAME_VOICE) {
- if ((myrpt->remote) && (myrpt->remotetx))
- memset(f->data, 0, f->datalen);
- ast_write(chan, f);
- } else if (f->frametype == AST_FRAME_CONTROL) {
- if (f->subclass == AST_CONTROL_HANGUP) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- ast_frfree(f);
- break;
- }
- /* if RX key */
- if (f->subclass == AST_CONTROL_RADIO_KEY) {
- ast_debug(8, "@@@@ remote rx key\n");
- if (!myrpt->remotetx) {
- ast_indicate(chan, AST_CONTROL_RADIO_KEY);
- myrpt->remoterx = 1;
- }
- }
- /* if RX un-key */
- if (f->subclass == AST_CONTROL_RADIO_UNKEY) {
- ast_debug(8, "@@@@ remote rx un-key\n");
- if (!myrpt->remotetx) {
- ast_indicate(chan, AST_CONTROL_RADIO_UNKEY);
- myrpt->remoterx = 0;
- }
- }
- }
- ast_frfree(f);
- continue;
- }
- if ((myrpt->rxchannel != myrpt->txchannel) && (who == myrpt->txchannel)) {
- /* do this cuz you have to */
- f = ast_read(myrpt->txchannel);
- if (!f) {
- ast_debug(1, "@@@@ link:Hung Up\n");
- break;
- }
- if (f->frametype == AST_FRAME_CONTROL) {
- if (f->subclass == AST_CONTROL_HANGUP) {
- ast_debug(1, "@@@@ rpt:Hung Up\n");
- ast_frfree(f);
- break;
- }
- }
- ast_frfree(f);
- continue;
- }
-
- }
- rpt_mutex_lock(&myrpt->lock);
- if (myrpt->rxchannel != myrpt->txchannel)
- ast_hangup(myrpt->txchannel);
- ast_hangup(myrpt->rxchannel);
- myrpt->hfscanmode = 0;
- myrpt->hfscanstatus = 0;
- myrpt->remoteon = 0;
- rpt_mutex_unlock(&myrpt->lock);
- closerem(myrpt);
- return res;
-}
-
-static int unload_module(void)
-{
- int i;
-
- for (i = 0; i < nrpts; i++) {
- if (!strcmp(rpt_vars[i].name, rpt_vars[i].p.nodes))
- continue;
- ast_mutex_destroy(&rpt_vars[i].lock);
- }
- i = ast_unregister_application(app);
-
- /* Unregister cli extensions */
- ast_cli_unregister_multiple(cli_rpt, sizeof(cli_rpt) / sizeof(struct ast_cli_entry));
-
- return i;
-}
-
-static int load_module(void)
-{
- struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
- struct ast_config *cfg = ast_config_load("rpt.conf", config_flags);
- if (!cfg) {
- ast_log(LOG_WARNING, "No such configuration file rpt.conf\n");
- return AST_MODULE_LOAD_DECLINE;
- }
- ast_pthread_create(&rpt_master_thread, NULL, rpt_master, cfg);
-
- /* Register cli extensions */
- ast_cli_register_multiple(cli_rpt, sizeof(cli_rpt) / sizeof(struct ast_cli_entry));
-
- return ast_register_application(app, rpt_exec, synopsis, descrip);
-}
-
-static int reload(void)
-{
- int n;
-
- for (n = 0; n < nrpts; n++)
- rpt_vars[n].reload = 1;
- return(0);
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Radio Repeater / Remote Base",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
diff --git a/trunk/apps/app_sayunixtime.c b/trunk/apps/app_sayunixtime.c
deleted file mode 100644
index d0e23449e..000000000
--- a/trunk/apps/app_sayunixtime.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (c) 2003, 2006 Tilghman Lesher. All rights reserved.
- * Copyright (c) 2006 Digium, Inc.
- *
- * Tilghman Lesher <app_sayunixtime__200309@the-tilghman.com>
- *
- * This code is released by the author with no restrictions on usage.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- */
-
-/*! \file
- *
- * \brief SayUnixTime application
- *
- * \author Tilghman Lesher <app_sayunixtime__200309@the-tilghman.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/say.h"
-#include "asterisk/app.h"
-
-static char *app_sayunixtime = "SayUnixTime";
-static char *app_datetime = "DateTime";
-
-static char *sayunixtime_synopsis = "Says a specified time in a custom format";
-
-static char *sayunixtime_descrip =
-"SayUnixTime([unixtime][,[timezone][,format]])\n"
-" unixtime - time, in seconds since Jan 1, 1970. May be negative.\n"
-" defaults to now.\n"
-" timezone - timezone, see /usr/share/zoneinfo for a list.\n"
-" defaults to machine default.\n"
-" format - a format the time is to be said in. See voicemail.conf.\n"
-" defaults to \"ABdY 'digits/at' IMp\"\n";
-static char *datetime_descrip =
-"DateTime([unixtime][,[timezone][,format]])\n"
-" unixtime - time, in seconds since Jan 1, 1970. May be negative.\n"
-" defaults to now.\n"
-" timezone - timezone, see /usr/share/zoneinfo for a list.\n"
-" defaults to machine default.\n"
-" format: - a format the time is to be said in. See voicemail.conf.\n"
-" defaults to \"ABdY 'digits/at' IMp\"\n";
-
-
-static int sayunixtime_exec(struct ast_channel *chan, void *data)
-{
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(timeval);
- AST_APP_ARG(timezone);
- AST_APP_ARG(format);
- );
- char *parse;
- int res = 0;
- time_t unixtime;
-
- if (!data)
- return 0;
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- ast_get_time_t(args.timeval, &unixtime, time(NULL), NULL);
-
- if (chan->_state != AST_STATE_UP)
- res = ast_answer(chan);
-
- if (!res)
- res = ast_say_date_with_format(chan, unixtime, AST_DIGIT_ANY,
- chan->language, args.format, args.timezone);
-
- return res;
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(app_sayunixtime);
- res |= ast_unregister_application(app_datetime);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
-
- res = ast_register_application(app_sayunixtime, sayunixtime_exec, sayunixtime_synopsis, sayunixtime_descrip);
- res |= ast_register_application(app_datetime, sayunixtime_exec, sayunixtime_synopsis, datetime_descrip);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Say time");
diff --git a/trunk/apps/app_senddtmf.c b/trunk/apps/app_senddtmf.c
deleted file mode 100644
index 67bd4feaa..000000000
--- a/trunk/apps/app_senddtmf.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief App to send DTMF digits
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-#include "asterisk/manager.h"
-#include "asterisk/channel.h"
-
-static char *app = "SendDTMF";
-
-static char *synopsis = "Sends arbitrary DTMF digits";
-
-static char *descrip =
-" SendDTMF(digits[,timeout_ms]): Sends DTMF digits on a channel. \n"
-" Accepted digits: 0-9, *#abcd, w (.5s pause)\n"
-" The application will either pass the assigned digits or terminate if it\n"
-" encounters an error.\n";
-
-
-static int senddtmf_exec(struct ast_channel *chan, void *vdata)
-{
- int res = 0;
- char *data;
- int timeout = 0, duration = 0;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(digits);
- AST_APP_ARG(timeout);
- AST_APP_ARG(duration);
- );
-
- if (ast_strlen_zero(vdata)) {
- ast_log(LOG_WARNING, "SendDTMF requires an argument (digits or *#aAbBcCdD)\n");
- return 0;
- }
-
- data = ast_strdupa(vdata);
- AST_STANDARD_APP_ARGS(args, data);
-
- if (!ast_strlen_zero(args.timeout))
- timeout = atoi(args.timeout);
- if (!ast_strlen_zero(args.duration))
- duration = atoi(args.duration);
- res = ast_dtmf_stream(chan, NULL, args.digits, timeout <= 0 ? 250 : timeout, duration);
-
- return res;
-}
-
-static char mandescr_playdtmf[] =
-"Description: Plays a dtmf digit on the specified channel.\n"
-"Variables: (all are required)\n"
-" Channel: Channel name to send digit to\n"
-" Digit: The dtmf digit to play\n";
-
-static int manager_play_dtmf(struct mansession *s, const struct message *m)
-{
- const char *channel = astman_get_header(m, "Channel");
- const char *digit = astman_get_header(m, "Digit");
- struct ast_channel *chan = ast_get_channel_by_name_locked(channel);
-
- if (!chan) {
- astman_send_error(s, m, "Channel not specified");
- return 0;
- }
- if (!digit) {
- astman_send_error(s, m, "No digit specified");
- ast_channel_unlock(chan);
- return 0;
- }
-
- ast_senddigit(chan, *digit, 0);
-
- ast_channel_unlock(chan);
- astman_send_ack(s, m, "DTMF successfully queued");
-
- return 0;
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(app);
- res |= ast_manager_unregister("PlayDTMF");
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
-
- res = ast_manager_register2( "PlayDTMF", EVENT_FLAG_CALL, manager_play_dtmf, "Play DTMF signal on a specific channel.", mandescr_playdtmf );
- res |= ast_register_application(app, senddtmf_exec, synopsis, descrip);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send DTMF digits Application");
diff --git a/trunk/apps/app_sendtext.c b/trunk/apps/app_sendtext.c
deleted file mode 100644
index e55c59528..000000000
--- a/trunk/apps/app_sendtext.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief App to transmit a text message
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \note Requires support of sending text messages from channel driver
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-
-static const char *app = "SendText";
-
-static const char *synopsis = "Send a Text Message";
-
-static const char *descrip =
-" SendText(text[,options]): Sends text to current channel (callee).\n"
-"Result of transmission will be stored in the SENDTEXTSTATUS\n"
-"channel variable:\n"
-" SUCCESS Transmission succeeded\n"
-" FAILURE Transmission failed\n"
-" UNSUPPORTED Text transmission not supported by channel\n"
-"\n"
-"At this moment, text is supposed to be 7 bit ASCII in most channels.\n";
-
-static int sendtext_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char *status = "UNSUPPORTED";
- char *parse = NULL;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(text);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "SendText requires an argument (text[,options])\n");
- return -1;
- } else
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (args.options) {
- }
-
- ast_channel_lock(chan);
- if (!chan->tech->send_text) {
- ast_channel_unlock(chan);
- /* Does not support transport */
- return 0;
- }
- status = "FAILURE";
- ast_channel_unlock(chan);
- res = ast_sendtext(chan, args.text);
- if (!res)
- status = "SUCCESS";
- pbx_builtin_setvar_helper(chan, "SENDTEXTSTATUS", status);
- return 0;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, sendtext_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send Text Applications");
diff --git a/trunk/apps/app_setcallerid.c b/trunk/apps/app_setcallerid.c
deleted file mode 100644
index c76b80eef..000000000
--- a/trunk/apps/app_setcallerid.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief App to set callerid presentation
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/image.h"
-#include "asterisk/callerid.h"
-
-static char *app2 = "SetCallerPres";
-
-static char *synopsis2 = "Set CallerID Presentation";
-
-
-static char *descrip2 =
-" SetCallerPres(presentation): Set Caller*ID presentation on a call.\n"
-" Valid presentations are:\n"
-"\n"
-" allowed_not_screened : Presentation Allowed, Not Screened\n"
-" allowed_passed_screen : Presentation Allowed, Passed Screen\n"
-" allowed_failed_screen : Presentation Allowed, Failed Screen\n"
-" allowed : Presentation Allowed, Network Number\n"
-" prohib_not_screened : Presentation Prohibited, Not Screened\n"
-" prohib_passed_screen : Presentation Prohibited, Passed Screen\n"
-" prohib_failed_screen : Presentation Prohibited, Failed Screen\n"
-" prohib : Presentation Prohibited, Network Number\n"
-" unavailable : Number Unavailable\n"
-"\n"
-;
-
-static int setcallerid_pres_exec(struct ast_channel *chan, void *data)
-{
- int pres = -1;
- static int deprecated = 0;
-
- if (!deprecated) {
- deprecated = 1;
- ast_log(LOG_WARNING, "SetCallerPres is deprecated. Please use Set(CALLERPRES()=%s) instead.\n", (char *)data);
- }
- pres = ast_parse_caller_presentation(data);
-
- if (pres < 0) {
- ast_log(LOG_WARNING, "'%s' is not a valid presentation (see 'show application SetCallerPres')\n",
- (char *) data);
- return 0;
- }
-
- chan->cid.cid_pres = pres;
- return 0;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app2);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app2, setcallerid_pres_exec, synopsis2, descrip2);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Set CallerID Presentation Application");
diff --git a/trunk/apps/app_skel.c b/trunk/apps/app_skel.c
deleted file mode 100644
index 8365b506a..000000000
--- a/trunk/apps/app_skel.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) <Year>, <Your Name Here>
- *
- * <Your Name Here> <<Your Email Here>>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Skeleton application
- *
- * \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
- *
- * This is a skeleton for development of an Asterisk application
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <defaultenabled>no</defaultenabled>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-#include "asterisk/app.h"
-
-static char *app = "Skel";
-static char *synopsis =
-"Skeleton application.";
-static char *descrip = "This application is a template to build other applications from.\n"
- " It shows you the basic structure to create your own Asterisk applications.\n";
-
-enum {
- OPTION_A = (1 << 0),
- OPTION_B = (1 << 1),
- OPTION_C = (1 << 2),
-} option_flags;
-
-enum {
- OPTION_ARG_B = 0,
- OPTION_ARG_C = 1,
- /* This *must* be the last value in this enum! */
- OPTION_ARG_ARRAY_SIZE = 2,
-} option_args;
-
-AST_APP_OPTIONS(app_opts,{
- AST_APP_OPTION('a', OPTION_A),
- AST_APP_OPTION_ARG('b', OPTION_B, OPTION_ARG_B),
- AST_APP_OPTION_ARG('c', OPTION_C, OPTION_ARG_C),
-});
-
-
-static int app_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_flags flags;
- char *parse, *opts[OPTION_ARG_ARRAY_SIZE];
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(dummy);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "%s requires an argument (dummy[,options])\n", app);
- return -1;
- }
-
- /* Do our thing here */
-
- /* We need to make a copy of the input string if we are going to modify it! */
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (args.argc == 2)
- ast_app_parse_options(app_opts, &flags, opts, args.options);
-
- if (!ast_strlen_zero(args.dummy))
- ast_log(LOG_NOTICE, "Dummy value is : %s\n", args.dummy);
-
- if (ast_test_flag(&flags, OPTION_A))
- ast_log(LOG_NOTICE, "Option A is set\n");
-
- if (ast_test_flag(&flags, OPTION_B))
- ast_log(LOG_NOTICE, "Option B is set with : %s\n", opts[OPTION_ARG_B] ? opts[OPTION_ARG_B] : "<unspecified>");
-
- if (ast_test_flag(&flags, OPTION_C))
- ast_log(LOG_NOTICE, "Option C is set with : %s\n", opts[OPTION_ARG_C] ? opts[OPTION_ARG_C] : "<unspecified>");
-
- return res;
-}
-
-static int unload_module(void)
-{
- int res;
- res = ast_unregister_application(app);
- return res;
-}
-
-static int load_module(void)
-{
- if (ast_register_application(app, app_exec, synopsis, descrip))
- return AST_MODULE_LOAD_DECLINE;
-
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Skeleton (sample) Application");
diff --git a/trunk/apps/app_sms.c b/trunk/apps/app_sms.c
deleted file mode 100644
index 3c54d43f1..000000000
--- a/trunk/apps/app_sms.c
+++ /dev/null
@@ -1,1932 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2004 - 2005, Adrian Kennard, rights assigned to Digium
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief SMS application - ETSI ES 201 912 protocol 1 implementation
- *
- * \par Development notes
- * \note The ETSI standards are available free of charge from ETSI at
- * http://pda.etsi.org/pda/queryform.asp
- * Among the relevant documents here we have:
- *
- * ES 201 912 SMS for PSTN/ISDN
- * TS 123 040 Technical realization of SMS
- *
- *
- * \ingroup applications
- *
- * \author Adrian Kennard (for the original protocol 1 code)
- * \author Filippo Grassilli (Hyppo) - protocol 2 support
- * Not fully tested, under development
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <dirent.h>
-#include <ctype.h>
-#include <sys/stat.h>
-
-#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR and LOG_DIR */
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/alaw.h"
-#include "asterisk/callerid.h"
-#include "asterisk/utils.h"
-#include "asterisk/app.h"
-
-/* #define OUTALAW */ /* enable this to output Alaw rather than linear */
-
-/* ToDo */
-/* Add full VP support */
-/* Handle status report messages (generation and reception) */
-/* Time zones on time stamps */
-/* user ref field */
-
-static volatile unsigned char message_ref; /* arbitary message ref */
-static volatile unsigned int seq; /* arbitrary message sequence number for unqiue files */
-
-static char log_file[255];
-
-static char *app = "SMS";
-
-static char *synopsis = "Communicates with SMS service centres and SMS capable analogue phones";
-
-static char *descrip =
- " SMS(name,[a][s][t][p(d)][r][o],addr,body):\n"
- "SMS handles exchange of SMS data with a call to/from SMS capable\n"
- "phone or SMS PSTN service center. Can send and/or receive SMS messages.\n"
- "Works to ETSI ES 201 912; compatible with BT SMS PSTN service in UK\n"
- "and Telecom Italia in Italy.\n"
- "Typical usage is to use to handle calls from the SMS service centre CLI,\n"
- "or to set up a call using 'outgoing' or manager interface to connect\n"
- "service centre to SMS()\n"
- "name is the name of the queue used in /var/spool/asterisk/sms\n"
- "Arguments:\n"
- " a - answer, i.e. send initial FSK packet.\n"
- " s - act as service centre talking to a phone.\n"
- " t - use protocol 2 (default used is protocol 1).\n"
- " p(N) - set the initial delay to N ms (default is 300).\n"
- " addr and body are a deprecated format to send messages out.\n"
- " s - set the Status Report Request (SRR) bit.\n"
- " o - the body should be coded as octets not 7-bit symbols.\n"
- "Messages are processed as per text file message queues.\n"
- "smsq (a separate software) is a command to generate message\n"
- "queues and send messages.\n"
- "NOTE: the protocol has tight delay bounds. Please use short frames\n"
- "and disable/keep short the jitter buffer on the ATA to make sure that\n"
- "respones (ACK etc.) are received in time.\n";
-
-/*
- * 80 samples of a single period of the wave. At 8000 Hz, it means these
- * are the samples of a 100 Hz signal.
- * To pick the two carriers (1300Hz for '1' and 2100 Hz for '0') used by
- * the modulation, we should take one every 13 and 21 samples respectively.
- */
-static signed short wave[] = {
- 0, 392, 782, 1167, 1545, 1913, 2270, 2612, 2939, 3247, 3536, 3802, 4045, 4263, 4455, 4619, 4755, 4862, 4938, 4985,
- 5000, 4985, 4938, 4862, 4755, 4619, 4455, 4263, 4045, 3802, 3536, 3247, 2939, 2612, 2270, 1913, 1545, 1167, 782, 392,
- 0, -392, -782, -1167,
- -1545, -1913, -2270, -2612, -2939, -3247, -3536, -3802, -4045, -4263, -4455, -4619, -4755, -4862, -4938, -4985, -5000,
- -4985, -4938, -4862,
- -4755, -4619, -4455, -4263, -4045, -3802, -3536, -3247, -2939, -2612, -2270, -1913, -1545, -1167, -782, -392
-};
-
-#ifdef OUTALAW
-static unsigned char wavea[80];
-typedef unsigned char output_t;
-static const output_t *wave_out = wavea; /* outgoing samples */
-#define __OUT_FMT AST_FORMAT_ALAW;
-#else
-typedef signed short output_t;
-static const output_t *wave_out = wave; /* outgoing samples */
-#define __OUT_FMT AST_FORMAT_SLINEAR
-#endif
-
-#define OSYNC_BITS 80 /* initial sync bits */
-
-/*!
- * The SMS spec ETSI ES 201 912 defines two protocols with different message types.
- * Also note that the high bit is used to indicate whether the message
- * is complete or not, but in two opposite ways:
- * for Protocol 1, 0x80 means that the message is complete;
- * for Protocol 2, 0x00 means that the message is complete;
- */
-enum message_types {
- DLL_SMS_MASK = 0x7f, /* mask for the valid bits */
-
- /* Protocol 1 values */
- DLL1_SMS_DATA = 0x11, /* data packet */
- DLL1_SMS_ERROR = 0x12,
- DLL1_SMS_EST = 0x13, /* start the connection */
- DLL1_SMS_REL = 0x14, /* end the connection */
- DLL1_SMS_ACK = 0x15,
- DLL1_SMS_NACK = 0x16,
-
- DLL1_SMS_COMPLETE = 0x80, /* packet is complete */
- DLL1_SMS_MORE = 0x00, /* more data to follow */
-
- /* Protocol 2 values */
- DLL2_SMS_EST = 0x7f, /* magic number. No message body */
- DLL2_SMS_INFO_MO = 0x10,
- DLL2_SMS_INFO_MT = 0x11,
- DLL2_SMS_INFO_STA = 0x12,
- DLL2_SMS_NACK = 0x13,
- DLL2_SMS_ACK0 = 0x14, /* ack even-numbered frame */
- DLL2_SMS_ACK1 = 0x15, /* ack odd-numbered frame */
- DLL2_SMS_ENQ = 0x16,
- DLL2_SMS_REL = 0x17, /* end the connection */
-
- DLL2_SMS_COMPLETE = 0x00, /* packet is complete */
- DLL2_SMS_MORE = 0x80, /* more data to follow */
-};
-
-/* SMS 7 bit character mapping to UCS-2 */
-static const unsigned short defaultalphabet[] = {
- 0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC,
- 0x00F2, 0x00E7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5,
- 0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8,
- 0x03A3, 0x0398, 0x039E, 0x00A0, 0x00C6, 0x00E6, 0x00DF, 0x00C9,
- ' ', '!', '"', '#', 164, '%', '&', 39, '(', ')', '*', '+', ',', '-', '.', '/',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
- 161, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
- 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 196, 214, 209, 220, 167,
- 191, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
- 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 228, 246, 241, 252, 224,
-};
-
-static const unsigned short escapes[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x000C, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x005E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0x007B, 0x007D, 0, 0, 0, 0, 0, 0x005C,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x005B, 0x007E, 0x005D, 0,
- 0x007C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0x20AC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-};
-
-#define SMSLEN 160 /*!< max SMS length */
-#define SMSLEN_8 140 /*!< max SMS length for 8-bit char */
-
-typedef struct sms_s {
- unsigned char hangup; /*!< we are done... */
- unsigned char err; /*!< set for any errors */
- unsigned char smsc:1; /*!< we are SMSC */
- unsigned char rx:1; /*!< this is a received message */
- char queue[30]; /*!< queue name */
- char oa[20]; /*!< originating address */
- char da[20]; /*!< destination address */
- struct timeval scts; /*!< time stamp, UTC */
- unsigned char pid; /*!< protocol ID */
- unsigned char dcs; /*!< data coding scheme */
- short mr; /*!< message reference - actually a byte, but use -1 for not set */
- int udl; /*!< user data length */
- int udhl; /*!< user data header length */
- unsigned char srr:1; /*!< Status Report request */
- unsigned char udhi:1; /*!< User Data Header required, even if length 0 */
- unsigned char rp:1; /*!< Reply Path */
- unsigned int vp; /*!< validity period in minutes, 0 for not set */
- unsigned short ud[SMSLEN]; /*!< user data (message), UCS-2 coded */
- unsigned char udh[SMSLEN]; /*!< user data header */
- char cli[20]; /*!< caller ID */
- unsigned char ophase; /*!< phase (0-79) for 0 and 1 frequencies (1300Hz and 2100Hz) */
- unsigned char ophasep; /*!< phase (0-79) for 1200 bps */
- unsigned char obyte; /*!< byte being sent */
- unsigned int opause; /*!< silent pause before sending (in sample periods) */
- unsigned char obitp; /*!< bit in byte */
- unsigned char osync; /*!< sync bits to send */
- unsigned char obytep; /*!< byte in data */
- unsigned char obyten; /*!< bytes in data */
- unsigned char omsg[256]; /*!< data buffer (out) */
- unsigned char imsg[250]; /*!< data buffer (in) */
- signed long long ims0,
- imc0,
- ims1,
- imc1; /*!< magnitude averages sin/cos 0/1 */
- unsigned int idle;
- unsigned short imag; /*!< signal level */
- unsigned char ips0; /*!< phase sin for bit 0, start at 0 inc by 21 mod 80 */
- unsigned char ips1; /*!< phase cos for bit 0, start at 20 inc by 21 mod 80 */
- unsigned char ipc0; /*!< phase sin for bit 1, start at 0 inc by 13 mod 80 */
- unsigned char ipc1; /*!< phase cos for bit 1, start at 20 inc by 13 mod 80 */
- unsigned char ibitl; /*!< last bit */
- unsigned char ibitc; /*!< bit run length count */
- unsigned char iphasep; /*!< bit phase (0-79) for 1200 bps */
- unsigned char ibitn; /*!< bit number in byte being received */
- unsigned char ibytev; /*!< byte value being received */
- unsigned char ibytep; /*!< byte pointer in message */
- unsigned char ibytec; /*!< byte checksum for message */
- unsigned char ierr; /*!< error flag */
- unsigned char ibith; /*!< history of last bits */
- unsigned char ibitt; /*!< total of 1's in last 3 bytes */
- /* more to go here */
-
- int opause_0; /*!< initial delay in ms, p() option */
- int protocol; /*!< ETSI SMS protocol to use (passed at app call) */
- int oseizure; /*!< protocol 2: channel seizure bits to send */
- int framenumber; /*!< protocol 2: frame number (for sending ACK0 or ACK1) */
- char udtxt[SMSLEN]; /*!< user data (message), PLAIN text */
-} sms_t;
-
-/* different types of encoding */
-#define is7bit(dcs) ( ((dcs) & 0xC0) ? (!((dcs)&4) ) : (((dcs) & 0xc) == 0) )
-#define is8bit(dcs) ( ((dcs) & 0xC0) ? ( ((dcs)&4) ) : (((dcs) & 0xc) == 4) )
-#define is16bit(dcs) ( ((dcs) & 0xC0) ? 0 : (((dcs) & 0xc) == 8) )
-
-static void sms_messagetx(sms_t *h);
-
-/*! \brief copy number, skipping non digits apart from leading + */
-static void numcpy(char *d, char *s)
-{
- if (*s == '+')
- *d++ = *s++;
- while (*s) {
- if (isdigit(*s))
- *d++ = *s;
- s++;
- }
- *d = 0;
-}
-
-/*! \brief static, return a date/time in ISO format */
-static char *isodate(time_t t, char *buf, int len)
-{
- struct ast_tm tm;
- struct timeval tv = { t, 0 };
- ast_localtime(&tv, &tm, NULL);
- ast_strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm);
- return buf;
-}
-
-/*! \brief Reads next UCS character from NUL terminated UTF-8 string and advance pointer */
-/* for non valid UTF-8 sequences, returns character as is */
-/* Does not advance pointer for null termination */
-static long utf8decode(unsigned char **pp)
-{
- unsigned char *p = *pp;
- if (!*p)
- return 0; /* null termination of string */
- (*pp)++;
- if (*p < 0xC0)
- return *p; /* ascii or continuation character */
- if (*p < 0xE0) {
- if (*p < 0xC2 || (p[1] & 0xC0) != 0x80)
- return *p; /* not valid UTF-8 */
- (*pp)++;
- return ((*p & 0x1F) << 6) + (p[1] & 0x3F);
- }
- if (*p < 0xF0) {
- if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80)
- return *p; /* not valid UTF-8 */
- (*pp) += 2;
- return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F);
- }
- if (*p < 0xF8) {
- if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80)
- return *p; /* not valid UTF-8 */
- (*pp) += 3;
- return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F);
- }
- if (*p < 0xFC) {
- if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
- || (p[4] & 0xC0) != 0x80)
- return *p; /* not valid UTF-8 */
- (*pp) += 4;
- return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F);
- }
- if (*p < 0xFE) {
- if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
- || (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80)
- return *p; /* not valid UTF-8 */
- (*pp) += 5;
- return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) + (p[5] & 0x3F);
- }
- return *p; /* not sensible */
-}
-
-/*! \brief takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud) and packs in to o using SMS 7 bit character codes */
-/* The return value is the number of septets packed in to o, which is internally limited to SMSLEN */
-/* o can be null, in which case this is used to validate or count only */
-/* if the input contains invalid characters then the return value is -1 */
-static int packsms7(unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
-{
- unsigned char p = 0; /* output pointer (bytes) */
- unsigned char b = 0; /* bit position */
- unsigned char n = 0; /* output character count */
- unsigned char dummy[SMSLEN];
-
- if (o == NULL) /* output to a dummy buffer if o not set */
- o = dummy;
-
- if (udhl) { /* header */
- o[p++] = udhl;
- b = 1;
- n = 1;
- while (udhl--) {
- o[p++] = *udh++;
- b += 8;
- while (b >= 7) {
- b -= 7;
- n++;
- }
- if (n >= SMSLEN)
- return n;
- }
- if (b) {
- b = 7 - b;
- if (++n >= SMSLEN)
- return n;
- }; /* filling to septet boundary */
- }
- o[p] = 0;
- /* message */
- while (udl--) {
- long u;
- unsigned char v;
- u = *ud++;
- /* XXX 0 is invalid ? */
- /* look up in defaultalphabet[]. If found, v is the 7-bit code */
- for (v = 0; v < 128 && defaultalphabet[v] != u; v++);
- if (v == 128 /* not found */ && u && n + 1 < SMSLEN) {
- /* if not found, look in the escapes table (we need 2 bytes) */
- for (v = 0; v < 128 && escapes[v] != u; v++);
- if (v < 128) { /* escaped sequence, esc + v */
- /* store the low (8-b) bits in o[p], the remaining bits in o[p+1] */
- o[p] |= (27 << b); /* the low bits go into o[p] */
- b += 7;
- if (b >= 8) {
- b -= 8;
- p++;
- o[p] = (27 >> (7 - b));
- }
- n++;
- }
- }
- if (v == 128)
- return -1; /* invalid character */
- /* store, same as above */
- o[p] |= (v << b);
- b += 7;
- if (b >= 8) {
- b -= 8;
- p++;
- o[p] = (v >> (7 - b));
- }
- if (++n >= SMSLEN)
- return n;
- }
- return n;
-}
-
-/*! \brief takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud)
- * and packs in to o using 8 bit character codes.
- * The return value is the number of bytes packed in to o, which is internally limited to 140.
- * o can be null, in which case this is used to validate or count only.
- * if the input contains invalid characters then the return value is -1
- */
-static int packsms8(unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
-{
- unsigned char p = 0;
- unsigned char dummy[SMSLEN_8];
-
- if (o == NULL)
- o = dummy;
- /* header - no encoding */
- if (udhl) {
- o[p++] = udhl;
- while (udhl--) {
- o[p++] = *udh++;
- if (p >= SMSLEN_8)
- return p;
- }
- }
- while (udl--) {
- long u;
- u = *ud++;
- if (u < 0 || u > 0xFF)
- return -1; /* not valid */
- o[p++] = u;
- if (p >= SMSLEN_8)
- return p;
- }
- return p;
-}
-
-/*! \brief takes a binary header (udhl bytes at udh) and UCS-2
- message (udl characters at ud) and packs in to o using 16 bit
- UCS-2 character codes
- The return value is the number of bytes packed in to o, which is
- internally limited to 140
- o can be null, in which case this is used to validate or count
- only if the input contains invalid characters then
- the return value is -1 */
-static int packsms16(unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
-{
- unsigned char p = 0;
- unsigned char dummy[SMSLEN_8];
-
- if (o == NULL)
- o = dummy;
- /* header - no encoding */
- if (udhl) {
- o[p++] = udhl;
- while (udhl--) {
- o[p++] = *udh++;
- if (p >= SMSLEN_8)
- return p;
- }
- }
- while (udl--) {
- long u;
- u = *ud++;
- o[p++] = (u >> 8);
- if (p >= SMSLEN_8)
- return p - 1; /* could not fit last character */
- o[p++] = u;
- if (p >= SMSLEN_8)
- return p;
- }
- return p;
-}
-
-/*! \brief general pack, with length and data,
- returns number of bytes of target used */
-static int packsms(unsigned char dcs, unsigned char *base, unsigned int udhl, unsigned char *udh, int udl, unsigned short *ud)
-{
- unsigned char *p = base;
- if (udl == 0)
- *p++ = 0; /* no user data */
- else {
-
- int l = 0;
- if (is7bit(dcs)) { /* 7 bit */
- l = packsms7(p + 1, udhl, udh, udl, ud);
- if (l < 0)
- l = 0;
- *p++ = l;
- p += (l * 7 + 7) / 8;
- } else if (is8bit(dcs)) { /* 8 bit */
- l = packsms8(p + 1, udhl, udh, udl, ud);
- if (l < 0)
- l = 0;
- *p++ = l;
- p += l;
- } else { /* UCS-2 */
- l = packsms16(p + 1, udhl, udh, udl, ud);
- if (l < 0)
- l = 0;
- *p++ = l;
- p += l;
- }
- }
- return p - base;
-}
-
-
-/*! \brief pack a date and return */
-static void packdate(unsigned char *o, time_t w)
-{
- struct ast_tm t;
- struct timeval tv = { w, 0 };
- int z;
-
- ast_localtime(&tv, &t, NULL);
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) || defined(__CYGWIN__)
- z = -t.tm_gmtoff / 60 / 15;
-#else
- z = timezone / 60 / 15;
-#endif
- *o++ = ((t.tm_year % 10) << 4) + (t.tm_year % 100) / 10;
- *o++ = (((t.tm_mon + 1) % 10) << 4) + (t.tm_mon + 1) / 10;
- *o++ = ((t.tm_mday % 10) << 4) + t.tm_mday / 10;
- *o++ = ((t.tm_hour % 10) << 4) + t.tm_hour / 10;
- *o++ = ((t.tm_min % 10) << 4) + t.tm_min / 10;
- *o++ = ((t.tm_sec % 10) << 4) + t.tm_sec / 10;
- if (z < 0)
- *o++ = (((-z) % 10) << 4) + (-z) / 10 + 0x08;
- else
- *o++ = ((z % 10) << 4) + z / 10;
-}
-
-/*! \brief unpack a date and return */
-static time_t unpackdate(unsigned char *i)
-{
- struct tm t;
- t.tm_year = 100 + (i[0] & 0xF) * 10 + (i[0] >> 4);
- t.tm_mon = (i[1] & 0xF) * 10 + (i[1] >> 4) - 1;
- t.tm_mday = (i[2] & 0xF) * 10 + (i[2] >> 4);
- t.tm_hour = (i[3] & 0xF) * 10 + (i[3] >> 4);
- t.tm_min = (i[4] & 0xF) * 10 + (i[4] >> 4);
- t.tm_sec = (i[5] & 0xF) * 10 + (i[5] >> 4);
- t.tm_isdst = 0;
- if (i[6] & 0x08)
- t.tm_min += 15 * ((i[6] & 0x7) * 10 + (i[6] >> 4));
- else
- t.tm_min -= 15 * ((i[6] & 0x7) * 10 + (i[6] >> 4));
- return mktime(&t);
-}
-
-/*! \brief unpacks bytes (7 bit encoding) at i, len l septets,
- and places in udh and ud setting udhl and udl. udh not used
- if udhi not set */
-static void unpacksms7(unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
-{
- unsigned char b = 0, p = 0;
- unsigned short *o = ud;
- *udhl = 0;
- if (udhi && l) { /* header */
- int h = i[p];
- *udhl = h;
- if (h) {
- b = 1;
- p++;
- l--;
- while (h-- && l) {
- *udh++ = i[p++];
- b += 8;
- while (b >= 7) {
- b -= 7;
- l--;
- if (!l)
- break;
- }
- }
- /* adjust for fill, septets */
- if (b) {
- b = 7 - b;
- l--;
- }
- }
- }
- while (l--) {
- unsigned char v;
- if (b < 2)
- v = ((i[p] >> b) & 0x7F); /* everything in one byte */
- else
- v = ((((i[p] >> b) + (i[p + 1] << (8 - b)))) & 0x7F);
- b += 7;
- if (b >= 8) {
- b -= 8;
- p++;
- }
- /* 0x00A0 is the encoding of ESC (27) in defaultalphabet */
- if (o > ud && o[-1] == 0x00A0 && escapes[v])
- o[-1] = escapes[v];
- else
- *o++ = defaultalphabet[v];
- }
- *udl = (o - ud);
-}
-
-/*! \brief unpacks bytes (8 bit encoding) at i, len l septets,
- and places in udh and ud setting udhl and udl. udh not used
- if udhi not set */
-static void unpacksms8(unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
-{
- unsigned short *o = ud;
- *udhl = 0;
- if (udhi) {
- int n = *i;
- *udhl = n;
- if (n) {
- i++;
- l--;
- while (l && n) {
- l--;
- n--;
- *udh++ = *i++;
- }
- }
- }
- while (l--)
- *o++ = *i++; /* not to UTF-8 as explicitly 8 bit coding in DCS */
- *udl = (o - ud);
-}
-
-/*! \brief unpacks bytes (16 bit encoding) at i, len l septets,
- and places in udh and ud setting udhl and udl.
- udh not used if udhi not set */
-static void unpacksms16(unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
-{
- unsigned short *o = ud;
- *udhl = 0;
- if (udhi) {
- int n = *i;
- *udhl = n;
- if (n) {
- i++;
- l--;
- while (l && n) {
- l--;
- n--;
- *udh++ = *i++;
- }
- }
- }
- while (l--) {
- int v = *i++;
- if (l--)
- v = (v << 8) + *i++;
- *o++ = v;
- }
- *udl = (o - ud);
-}
-
-/*! \brief general unpack - starts with length byte (octet or septet) and returns number of bytes used, inc length */
-static int unpacksms(unsigned char dcs, unsigned char *i, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
-{
- int l = *i++;
- if (is7bit(dcs)) {
- unpacksms7(i, l, udh, udhl, ud, udl, udhi);
- l = (l * 7 + 7) / 8; /* adjust length to return */
- } else if (is8bit(dcs))
- unpacksms8(i, l, udh, udhl, ud, udl, udhi);
- else
- unpacksms16(i, l, udh, udhl, ud, udl, udhi);
- return l + 1;
-}
-
-/*! \brief unpack an address from i, return byte length, unpack to o */
-static unsigned char unpackaddress(char *o, unsigned char *i)
-{
- unsigned char l = i[0],
- p;
- if (i[1] == 0x91)
- *o++ = '+';
- for (p = 0; p < l; p++) {
- if (p & 1)
- *o++ = (i[2 + p / 2] >> 4) + '0';
- else
- *o++ = (i[2 + p / 2] & 0xF) + '0';
- }
- *o = 0;
- return (l + 5) / 2;
-}
-
-/*! \brief store an address at o, and return number of bytes used */
-static unsigned char packaddress(unsigned char *o, char *i)
-{
- unsigned char p = 2;
- o[0] = 0; /* number of bytes */
- if (*i == '+') { /* record as bit 0 in byte 1 */
- i++;
- o[1] = 0x91;
- } else
- o[1] = 0x81;
- for ( ; *i ; i++) {
- if (!isdigit(*i)) /* ignore non-digits */
- continue;
- if (o[0] & 1)
- o[p++] |= ((*i & 0xF) << 4);
- else
- o[p] = (*i & 0xF);
- o[0]++;
- }
- if (o[0] & 1)
- o[p++] |= 0xF0; /* pad */
- return p;
-}
-
-/*! \brief Log the output, and remove file */
-static void sms_log(sms_t * h, char status)
-{
- int o;
-
- if (*h->oa == '\0' && *h->da == '\0')
- return;
- o = open(log_file, O_CREAT | O_APPEND | O_WRONLY, AST_FILE_MODE);
- if (o >= 0) {
- char line[1000], mrs[3] = "", *p;
- char buf[30];
- unsigned char n;
-
- if (h->mr >= 0)
- snprintf(mrs, sizeof(mrs), "%02X", h->mr);
- snprintf(line, sizeof(line), "%s %c%c%c%s %s %s %s ",
- isodate(time(NULL), buf, sizeof(buf)),
- status, h->rx ? 'I' : 'O', h->smsc ? 'S' : 'M', mrs, h->queue,
- S_OR(h->oa, "-"), S_OR(h->da, "-") );
- p = line + strlen(line);
- for (n = 0; n < h->udl; n++) {
- if (h->ud[n] == '\\') {
- *p++ = '\\';
- *p++ = '\\';
- } else if (h->ud[n] == '\n') {
- *p++ = '\\';
- *p++ = 'n';
- } else if (h->ud[n] == '\r') {
- *p++ = '\\';
- *p++ = 'r';
- } else if (h->ud[n] < 32 || h->ud[n] == 127)
- *p++ = 191;
- else
- *p++ = h->ud[n];
- }
- *p++ = '\n';
- *p = 0;
- write(o, line, strlen(line));
- close(o);
- }
- *h->oa = *h->da = h->udl = 0;
-}
-
-/*! \brief parse and delete a file */
-static void sms_readfile(sms_t * h, char *fn)
-{
- char line[1000];
- FILE *s;
- char dcsset = 0; /* if DSC set */
- ast_log(LOG_EVENT, "Sending %s\n", fn);
- h->rx = h->udl = *h->oa = *h->da = h->pid = h->srr = h->udhi = h->rp = h->vp = h->udhl = 0;
- h->mr = -1;
- h->dcs = 0xF1; /* normal messages class 1 */
- h->scts = ast_tvnow();
- s = fopen(fn, "r");
- if (s) {
- if (unlink(fn)) { /* concurrent access, we lost */
- fclose(s);
- return;
- }
- while (fgets (line, sizeof(line), s)) { /* process line in file */
- char *p;
- void *pp = &p;
- for (p = line; *p && *p != '\n' && *p != '\r'; p++);
- *p = 0; /* strip eoln */
- p = line;
- if (!*p || *p == ';')
- continue; /* blank line or comment, ignore */
- while (isalnum(*p)) {
- *p = tolower (*p);
- p++;
- }
- while (isspace (*p))
- *p++ = 0;
- if (*p == '=') {
- *p++ = 0;
- if (!strcmp(line, "ud")) { /* parse message (UTF-8) */
- unsigned char o = 0;
- memcpy(h->udtxt, p, SMSLEN); /* for protocol 2 */
- while (*p && o < SMSLEN)
- h->ud[o++] = utf8decode(pp);
- h->udl = o;
- if (*p)
- ast_log(LOG_WARNING, "UD too long in %s\n", fn);
- } else {
- while (isspace (*p))
- p++;
- if (!strcmp(line, "oa") && strlen(p) < sizeof(h->oa))
- numcpy (h->oa, p);
- else if (!strcmp(line, "da") && strlen(p) < sizeof(h->oa))
- numcpy (h->da, p);
- else if (!strcmp(line, "pid"))
- h->pid = atoi(p);
- else if (!strcmp(line, "dcs")) {
- h->dcs = atoi(p);
- dcsset = 1;
- } else if (!strcmp(line, "mr"))
- h->mr = atoi(p);
- else if (!strcmp(line, "srr"))
- h->srr = (atoi(p) ? 1 : 0);
- else if (!strcmp(line, "vp"))
- h->vp = atoi(p);
- else if (!strcmp(line, "rp"))
- h->rp = (atoi(p) ? 1 : 0);
- else if (!strcmp(line, "scts")) { /* get date/time */
- int Y,
- m,
- d,
- H,
- M,
- S;
- if (sscanf (p, "%d-%d-%dT%d:%d:%d", &Y, &m, &d, &H, &M, &S) == 6) {
- struct ast_tm t = { 0, };
- t.tm_year = Y - 1900;
- t.tm_mon = m - 1;
- t.tm_mday = d;
- t.tm_hour = H;
- t.tm_min = M;
- t.tm_sec = S;
- t.tm_isdst = -1;
- h->scts = ast_mktime(&t, NULL);
- if (h->scts.tv_sec == 0)
- ast_log(LOG_WARNING, "Bad date/timein %s: %s", fn, p);
- }
- } else
- ast_log(LOG_WARNING, "Cannot parse in %s: %s=%si\n", fn, line, p);
- }
- } else if (*p == '#') { /* raw hex format */
- *p++ = 0;
- if (*p == '#') {
- p++;
- if (!strcmp(line, "ud")) { /* user data */
- int o = 0;
- while (*p && o < SMSLEN) {
- if (isxdigit(*p) && isxdigit(p[1]) && isxdigit(p[2]) && isxdigit(p[3])) {
- h->ud[o++] =
- (((isalpha(*p) ? 9 : 0) + (*p & 0xF)) << 12) +
- (((isalpha(p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) +
- (((isalpha(p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha(p[3]) ? 9 : 0) + (p[3] & 0xF));
- p += 4;
- } else
- break;
- }
- h->udl = o;
- if (*p)
- ast_log(LOG_WARNING, "UD too long / invalid UCS-2 hex in %s\n", fn);
- } else
- ast_log(LOG_WARNING, "Only ud can use ## format, %s\n", fn);
- } else if (!strcmp(line, "ud")) { /* user data */
- int o = 0;
- while (*p && o < SMSLEN) {
- if (isxdigit(*p) && isxdigit(p[1])) {
- h->ud[o++] = (((isalpha(*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha(p[1]) ? 9 : 0) + (p[1] & 0xF));
- p += 2;
- } else
- break;
- }
- h->udl = o;
- if (*p)
- ast_log(LOG_WARNING, "UD too long / invalid UCS-1 hex in %s\n", fn);
- } else if (!strcmp(line, "udh")) { /* user data header */
- unsigned char o = 0;
- h->udhi = 1;
- while (*p && o < SMSLEN) {
- if (isxdigit(*p) && isxdigit(p[1])) {
- h->udh[o] = (((isalpha(*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha(p[1]) ? 9 : 0) + (p[1] & 0xF));
- o++;
- p += 2;
- } else
- break;
- }
- h->udhl = o;
- if (*p)
- ast_log(LOG_WARNING, "UDH too long / invalid hex in %s\n", fn);
- } else
- ast_log(LOG_WARNING, "Only ud and udh can use # format, %s\n", fn);
- } else
- ast_log(LOG_WARNING, "Cannot parse in %s: %s\n", fn, line);
- }
- fclose(s);
- if (!dcsset && packsms7(0, h->udhl, h->udh, h->udl, h->ud) < 0) {
- if (packsms8(0, h->udhl, h->udh, h->udl, h->ud) < 0) {
- if (packsms16(0, h->udhl, h->udh, h->udl, h->ud) < 0)
- ast_log(LOG_WARNING, "Invalid UTF-8 message even for UCS-2 (%s)\n", fn);
- else {
- h->dcs = 0x08; /* default to 16 bit */
- ast_log(LOG_WARNING, "Sending in 16 bit format(%s)\n", fn);
- }
- } else {
- h->dcs = 0xF5; /* default to 8 bit */
- ast_log(LOG_WARNING, "Sending in 8 bit format(%s)\n", fn);
- }
- }
- if (is7bit(h->dcs) && packsms7(0, h->udhl, h->udh, h->udl, h->ud) < 0)
- ast_log(LOG_WARNING, "Invalid 7 bit GSM data %s\n", fn);
- if (is8bit(h->dcs) && packsms8(0, h->udhl, h->udh, h->udl, h->ud) < 0)
- ast_log(LOG_WARNING, "Invalid 8 bit data %s\n", fn);
- if (is16bit(h->dcs) && packsms16(0, h->udhl, h->udh, h->udl, h->ud) < 0)
- ast_log(LOG_WARNING, "Invalid 16 bit data %s\n", fn);
- }
-}
-
-/*! \brief white a received text message to a file */
-static void sms_writefile(sms_t * h)
-{
- char fn[200] = "", fn2[200] = "";
- char buf[30];
- FILE *o;
-
- snprintf(fn, sizeof(fn), "%s/sms/%s", ast_config_AST_SPOOL_DIR, h->smsc ? h->rx ? "morx" : "mttx" : h->rx ? "mtrx" : "motx");
- ast_mkdir(fn, 0777); /* ensure it exists */
- ast_copy_string(fn2, fn, sizeof(fn2));
- snprintf(fn2 + strlen(fn2), sizeof(fn2) - strlen(fn2), "/%s.%s-%d", h->queue, isodate(h->scts.tv_sec, buf, sizeof(buf)), seq++);
- snprintf(fn + strlen(fn), sizeof(fn) - strlen(fn), "/.%s", fn2 + strlen(fn) + 1);
- o = fopen(fn, "w");
- if (o == NULL)
- return;
-
- if (*h->oa)
- fprintf(o, "oa=%s\n", h->oa);
- if (*h->da)
- fprintf(o, "da=%s\n", h->da);
- if (h->udhi) {
- unsigned int p;
- fprintf(o, "udh#");
- for (p = 0; p < h->udhl; p++)
- fprintf(o, "%02X", h->udh[p]);
- fprintf(o, "\n");
- }
- if (h->udl) {
- unsigned int p;
- for (p = 0; p < h->udl && h->ud[p] >= ' '; p++);
- if (p < h->udl)
- fputc(';', o); /* cannot use ud=, but include as a comment for human readable */
- fprintf(o, "ud=");
- for (p = 0; p < h->udl; p++) {
- unsigned short v = h->ud[p];
- if (v < 32)
- fputc(191, o);
- else if (v < 0x80)
- fputc(v, o);
- else if (v < 0x800)
- {
- fputc(0xC0 + (v >> 6), o);
- fputc(0x80 + (v & 0x3F), o);
- } else
- {
- fputc(0xE0 + (v >> 12), o);
- fputc(0x80 + ((v >> 6) & 0x3F), o);
- fputc(0x80 + (v & 0x3F), o);
- }
- }
- fprintf(o, "\n");
- for (p = 0; p < h->udl && h->ud[p] >= ' '; p++);
- if (p < h->udl) {
- for (p = 0; p < h->udl && h->ud[p] < 0x100; p++);
- if (p == h->udl) { /* can write in ucs-1 hex */
- fprintf(o, "ud#");
- for (p = 0; p < h->udl; p++)
- fprintf(o, "%02X", h->ud[p]);
- fprintf(o, "\n");
- } else { /* write in UCS-2 */
- fprintf(o, "ud##");
- for (p = 0; p < h->udl; p++)
- fprintf(o, "%04X", h->ud[p]);
- fprintf(o, "\n");
- }
- }
- }
- if (h->scts.tv_sec) {
- char buf[30];
- fprintf(o, "scts=%s\n", isodate(h->scts.tv_sec, buf, sizeof(buf)));
- }
- if (h->pid)
- fprintf(o, "pid=%d\n", h->pid);
- if (h->dcs != 0xF1)
- fprintf(o, "dcs=%d\n", h->dcs);
- if (h->vp)
- fprintf(o, "vp=%d\n", h->vp);
- if (h->srr)
- fprintf(o, "srr=1\n");
- if (h->mr >= 0)
- fprintf(o, "mr=%d\n", h->mr);
- if (h->rp)
- fprintf(o, "rp=1\n");
- fclose(o);
- if (rename(fn, fn2))
- unlink(fn);
- else
- ast_log(LOG_EVENT, "Received to %s\n", fn2);
-}
-
-/*! \brief read dir skipping dot files... */
-static struct dirent *readdirqueue(DIR *d, char *queue)
-{
- struct dirent *f;
- do {
- f = readdir(d);
- } while (f && (*f->d_name == '.' || strncmp(f->d_name, queue, strlen(queue)) || f->d_name[strlen(queue)] != '.'));
- return f;
-}
-
-/*! \brief handle the incoming message */
-static unsigned char sms_handleincoming (sms_t * h)
-{
- unsigned char p = 3;
- if (h->smsc) { /* SMSC */
- if ((h->imsg[2] & 3) == 1) { /* SMS-SUBMIT */
- h->udhl = h->udl = 0;
- h->vp = 0;
- h->srr = ((h->imsg[2] & 0x20) ? 1 : 0);
- h->udhi = ((h->imsg[2] & 0x40) ? 1 : 0);
- h->rp = ((h->imsg[2] & 0x80) ? 1 : 0);
- ast_copy_string(h->oa, h->cli, sizeof(h->oa));
- h->scts = ast_tvnow();
- h->mr = h->imsg[p++];
- p += unpackaddress(h->da, h->imsg + p);
- h->pid = h->imsg[p++];
- h->dcs = h->imsg[p++];
- if ((h->imsg[2] & 0x18) == 0x10) { /* relative VP */
- if (h->imsg[p] < 144)
- h->vp = (h->imsg[p] + 1) * 5;
- else if (h->imsg[p] < 168)
- h->vp = 720 + (h->imsg[p] - 143) * 30;
- else if (h->imsg[p] < 197)
- h->vp = (h->imsg[p] - 166) * 1440;
- else
- h->vp = (h->imsg[p] - 192) * 10080;
- p++;
- } else if (h->imsg[2] & 0x18)
- p += 7; /* ignore enhanced / absolute VP */
- p += unpacksms(h->dcs, h->imsg + p, h->udh, &h->udhl, h->ud, &h->udl, h->udhi);
- h->rx = 1; /* received message */
- sms_writefile(h); /* write the file */
- if (p != h->imsg[1] + 2) {
- ast_log(LOG_WARNING, "Mismatch receive unpacking %d/%d\n", p, h->imsg[1] + 2);
- return 0xFF; /* duh! */
- }
- } else {
- ast_log(LOG_WARNING, "Unknown message type %02X\n", h->imsg[2]);
- return 0xFF;
- }
- } else { /* client */
- if (!(h->imsg[2] & 3)) { /* SMS-DELIVER */
- *h->da = h->srr = h->rp = h->vp = h->udhi = h->udhl = h->udl = 0;
- h->srr = ((h->imsg[2] & 0x20) ? 1 : 0);
- h->udhi = ((h->imsg[2] & 0x40) ? 1 : 0);
- h->rp = ((h->imsg[2] & 0x80) ? 1 : 0);
- h->mr = -1;
- p += unpackaddress(h->oa, h->imsg + p);
- h->pid = h->imsg[p++];
- h->dcs = h->imsg[p++];
- h->scts.tv_sec = unpackdate(h->imsg + p);
- p += 7;
- p += unpacksms(h->dcs, h->imsg + p, h->udh, &h->udhl, h->ud, &h->udl, h->udhi);
- h->rx = 1; /* received message */
- sms_writefile(h); /* write the file */
- if (p != h->imsg[1] + 2) {
- ast_log(LOG_WARNING, "Mismatch receive unpacking %d/%d\n", p, h->imsg[1] + 2);
- return 0xFF; /* duh! */
- }
- } else {
- ast_log(LOG_WARNING, "Unknown message type %02X\n", h->imsg[2]);
- return 0xFF;
- }
- }
- return 0; /* no error */
-}
-
-#ifdef SOLARIS
-#define NAME_MAX 1024
-#endif
-
-/*!
- * Add data to a protocol 2 message.
- * Use the length field (h->omsg[1]) as a pointer to the next free position.
- */
-static void adddata_proto2(sms_t *h, unsigned char msg, char *data, int size)
-{
- int x = h->omsg[1]+2; /* Get current position */
- if (x == 2)
- x += 2; /* First: skip Payload length (set later) */
- h->omsg[x++] = msg; /* Message code */
- h->omsg[x++] = (unsigned char)size; /* Data size Low */
- h->omsg[x++] = 0; /* Data size Hi */
- for (; size > 0 ; size--)
- h->omsg[x++] = *data++;
- h->omsg[1] = x - 2; /* Frame size */
- h->omsg[2] = x - 4; /* Payload length (Lo) */
- h->omsg[3] = 0; /* Payload length (Hi) */
-}
-
-static void putdummydata_proto2(sms_t *h)
-{
- adddata_proto2(h, 0x10, "\0", 1); /* Media Identifier > SMS */
- adddata_proto2(h, 0x11, "\0\0\0\0\0\0", 6); /* Firmware version */
- adddata_proto2(h, 0x12, "\2\0\4", 3); /* SMS provider ID */
- adddata_proto2(h, 0x13, h->udtxt, h->udl); /* Body */
-}
-
-static void sms_compose2(sms_t *h, int more)
-{
- struct ast_tm tm;
- struct timeval tv = h->scts;
- char stm[9];
-
- h->omsg[0] = 0x00; /* set later... */
- h->omsg[1] = 0;
- putdummydata_proto2(h);
- if (h->smsc) { /* deliver */
- h->omsg[0] = 0x11; /* SMS_DELIVERY */
- /* Required: 10 11 12 13 14 15 17 (seems they must be ordered!) */
- ast_localtime(&tv, &tm, NULL);
- sprintf(stm, "%02d%02d%02d%02d", tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min); /* Date mmddHHMM */
- adddata_proto2(h, 0x14, stm, 8); /* Date */
- if (*h->oa == 0)
- strcpy(h->oa, "00000000");
- adddata_proto2(h, 0x15, h->oa, strlen(h->oa)); /* Originator */
- adddata_proto2(h, 0x17, "\1", 1); /* Calling Terminal ID */
- } else { /* submit */
- h->omsg[0] = 0x10; /* SMS_SUBMIT */
- /* Required: 10 11 12 13 17 18 1B 1C (seems they must be ordered!) */
- adddata_proto2(h, 0x17, "\1", 1); /* Calling Terminal ID */
- if (*h->da == 0)
- strcpy(h->da, "00000000");
- adddata_proto2(h, 0x18, h->da, strlen(h->da)); /* Originator */
- adddata_proto2(h, 0x1B, "\1", 1); /* Called Terminal ID */
- adddata_proto2(h, 0x1C, "\0\0\0", 3); /* Notification */
- }
-}
-
-static void putdummydata_proto2(sms_t *h);
-
-#define MAX_DEBUG_LEN 300
-static char *sms_hexdump(unsigned char buf[], int size, char *s /* destination */)
-{
- char *p;
- int f;
-
- for (p = s, f = 0; f < size && f < MAX_DEBUG_LEN; f++, p += 3)
- sprintf(p, "%02X ", (unsigned char)buf[f]);
- return(s);
-}
-
-
-/*! \brief sms_handleincoming_proto2: handle the incoming message */
-static int sms_handleincoming_proto2(sms_t *h)
-{
- int f, i, sz = 0;
- int msg, msgsz;
- struct ast_tm tm;
- struct timeval tv = { 0, 0 };
- char debug_buf[MAX_DEBUG_LEN * 3 + 1];
-
- sz = h->imsg[1] + 2;
- /* ast_verb(3, "SMS-P2 Frame: %s\n", sms_hexdump(h->imsg, sz, debug_buf)); */
-
- /* Parse message body (called payload) */
- tv = h->scts = ast_tvnow();
- for (f = 4; f < sz; ) {
- msg = h->imsg[f++];
- msgsz = h->imsg[f++];
- msgsz += (h->imsg[f++] * 256);
- switch (msg) {
- case 0x13: /* Body */
- ast_verb(3, "SMS-P2 Body#%02X=[%.*s]\n", msg, msgsz, &h->imsg[f]);
- if (msgsz >= sizeof(h->imsg))
- msgsz = sizeof(h->imsg) - 1;
- for (i = 0; i < msgsz; i++)
- h->ud[i] = h->imsg[f + i];
- h->udl = msgsz;
- break;
- case 0x14: /* Date SCTS */
- tv = h->scts = ast_tvnow();
- ast_localtime(&tv, &tm, NULL);
- tm.tm_mon = ( (h->imsg[f] * 10) + h->imsg[f + 1] ) - 1;
- tm.tm_mday = ( (h->imsg[f + 2] * 10) + h->imsg[f + 3] );
- tm.tm_hour = ( (h->imsg[f + 4] * 10) + h->imsg[f + 5] );
- tm.tm_min = ( (h->imsg[f + 6] * 10) + h->imsg[f + 7] );
- tm.tm_sec = 0;
- h->scts = ast_mktime(&tm, NULL);
- ast_verb(3, "SMS-P2 Date#%02X=%02d/%02d %02d:%02d\n", msg, tm.tm_mday, tm.tm_mon + 1, tm.tm_hour, tm.tm_min);
- break;
- case 0x15: /* Calling line (from SMSC) */
- if (msgsz >= 20)
- msgsz = 20 - 1;
- ast_verb(3, "SMS-P2 Origin#%02X=[%.*s]\n", msg, msgsz, &h->imsg[f]);
- ast_copy_string(h->oa, (char *)(&h->imsg[f]), msgsz + 1);
- break;
- case 0x18: /* Destination(from TE/phone) */
- if (msgsz >= 20)
- msgsz = 20 - 1;
- ast_verb(3, "SMS-P2 Destination#%02X=[%.*s]\n", msg, msgsz, &h->imsg[f]);
- ast_copy_string(h->da, (char *)(&h->imsg[f]), msgsz + 1);
- break;
- case 0x1C: /* Notify */
- ast_verb(3, "SMS-P2 Notify#%02X=%s\n", msg, sms_hexdump(&h->imsg[f], 3, debug_buf));
- break;
- default:
- ast_verb(3, "SMS-P2 Par#%02X [%d]: %s\n", msg, msgsz, sms_hexdump(&h->imsg[f], msgsz, debug_buf));
- break;
- }
- f+=msgsz; /* Skip to next */
- }
- h->rx = 1; /* received message */
- sms_writefile(h); /* write the file */
- return 0; /* no error */
-}
-
-#if 0
-static void smssend(sms_t *h, char *c)
-{
- int f, x;
- for (f = 0; f < strlen(c); f++) {
- sscanf(&c[f*3], "%x", &x);
- h->omsg[f] = x;
- }
- sms_messagetx(h);
-}
-#endif
-
-static void sms_nextoutgoing (sms_t *h);
-
-static void sms_messagerx2(sms_t * h)
-{
- int p = h->imsg[0] & DLL_SMS_MASK ; /* mask the high bit */
- int cause;
-
-#define DLL2_ACK(h) ((h->framenumber & 1) ? DLL2_SMS_ACK1: DLL2_SMS_ACK1)
- switch (p) {
- case DLL2_SMS_EST: /* Protocol 2: Connection ready (fake): send message */
- sms_nextoutgoing (h);
- /* smssend(h,"11 29 27 00 10 01 00 00 11 06 00 00 00 00 00 00 00 12 03 00 02 00 04 13 01 00 41 14 08 00 30 39 31 35 30 02 30 02 15 02 00 39 30 "); */
- break;
-
- case DLL2_SMS_INFO_MO: /* transport SMS_SUBMIT */
- case DLL2_SMS_INFO_MT: /* transport SMS_DELIVERY */
- cause = sms_handleincoming_proto2(h);
- if (!cause) /* ACK */
- sms_log(h, 'Y');
- h->omsg[0] = DLL2_ACK(h);
- h->omsg[1] = 0x06; /* msg len */
- h->omsg[2] = 0x04; /* payload len */
- h->omsg[3] = 0x00; /* payload len */
- h->omsg[4] = 0x1f; /* Response type */
- h->omsg[5] = 0x01; /* parameter len */
- h->omsg[6] = 0x00; /* parameter len */
- h->omsg[7] = cause; /* CONFIRM or error */
- sms_messagetx(h);
- break;
-
- case DLL2_SMS_NACK: /* Protocol 2: SMS_NAK */
- h->omsg[0] = DLL2_SMS_REL; /* SMS_REL */
- h->omsg[1] = 0x00; /* msg len */
- sms_messagetx(h);
- break;
-
- case DLL2_SMS_ACK0:
- case DLL2_SMS_ACK1:
- /* SMS_ACK also transport SMS_SUBMIT or SMS_DELIVERY */
- if ( (h->omsg[0] & DLL_SMS_MASK) == DLL2_SMS_REL) {
- /* a response to our Release, just hangup */
- h->hangup = 1; /* hangup */
- } else {
- /* XXX depending on what we are.. */
- ast_log(LOG_NOTICE, "SMS_SUBMIT or SMS_DELIVERY");
- sms_nextoutgoing (h);
- }
- break;
-
- case DLL2_SMS_REL: /* Protocol 2: SMS_REL (hangup req) */
- h->omsg[0] = DLL2_ACK(h);
- h->omsg[1] = 0;
- sms_messagetx(h);
- break;
- }
-}
-
-/*! \brief compose a message for protocol 1 */
-static void sms_compose1(sms_t *h, int more)
-{
- unsigned int p = 2; /* next byte to write. Skip type and len */
-
- h->omsg[0] = 0x91; /* SMS_DATA */
- if (h->smsc) { /* deliver */
- h->omsg[p++] = (more ? 4 : 0) + ((h->udhl > 0) ? 0x40 : 0);
- p += packaddress(h->omsg + p, h->oa);
- h->omsg[p++] = h->pid;
- h->omsg[p++] = h->dcs;
- packdate(h->omsg + p, h->scts.tv_sec);
- p += 7;
- p += packsms(h->dcs, h->omsg + p, h->udhl, h->udh, h->udl, h->ud);
- } else { /* submit */
- h->omsg[p++] =
- 0x01 + (more ? 4 : 0) + (h->srr ? 0x20 : 0) + (h->rp ? 0x80 : 0) + (h->vp ? 0x10 : 0) + (h->udhi ? 0x40 : 0);
- if (h->mr < 0)
- h->mr = message_ref++;
- h->omsg[p++] = h->mr;
- p += packaddress(h->omsg + p, h->da);
- h->omsg[p++] = h->pid;
- h->omsg[p++] = h->dcs;
- if (h->vp) { /* relative VP */
- if (h->vp < 720)
- h->omsg[p++] = (h->vp + 4) / 5 - 1;
- else if (h->vp < 1440)
- h->omsg[p++] = (h->vp - 720 + 29) / 30 + 143;
- else if (h->vp < 43200)
- h->omsg[p++] = (h->vp + 1439) / 1440 + 166;
- else if (h->vp < 635040)
- h->omsg[p++] = (h->vp + 10079) / 10080 + 192;
- else
- h->omsg[p++] = 255; /* max */
- }
- p += packsms(h->dcs, h->omsg + p, h->udhl, h->udh, h->udl, h->ud);
- }
- h->omsg[1] = p - 2;
-}
-
-/*! \brief find and fill in next message, or send a REL if none waiting */
-static void sms_nextoutgoing (sms_t * h)
-{
- char fn[100 + NAME_MAX] = "";
- DIR *d;
- char more = 0;
-
- *h->da = *h->oa = '\0'; /* clear destinations */
- h->rx = 0; /* outgoing message */
- snprintf(fn, sizeof(fn), "%s/sms/%s", ast_config_AST_SPOOL_DIR, h->smsc ? "mttx" : "motx");
- ast_mkdir(fn, 0777); /* ensure it exists */
- d = opendir(fn);
- if (d) {
- struct dirent *f = readdirqueue(d, h->queue);
- if (f) {
- snprintf(fn + strlen(fn), sizeof(fn) - strlen(fn), "/%s", f->d_name);
- sms_readfile(h, fn);
- if (readdirqueue(d, h->queue))
- more = 1; /* more to send */
- }
- closedir(d);
- }
- if (*h->da || *h->oa) { /* message to send */
- if (h->protocol == 2)
- sms_compose2(h, more);
- else
- sms_compose1(h, more);
- } else { /* no message */
- if (h->protocol == 2) {
- h->omsg[0] = 0x17; /* SMS_REL */
- h->omsg[1] = 0;
- } else {
- h->omsg[0] = 0x94; /* SMS_REL */
- h->omsg[1] = 0;
- }
- }
- sms_messagetx(h);
-}
-
-#define DIR_RX 1
-#define DIR_TX 2
-static void sms_debug (int dir, sms_t *h)
-{
- char txt[259 * 3 + 1];
- char *p = txt; /* always long enough */
- unsigned char *msg = (dir == DIR_RX) ? h->imsg : h->omsg;
- int n = (dir == DIR_RX) ? h->ibytep : msg[1] + 2;
- int q = 0;
- while (q < n && q < 30) {
- sprintf(p, " %02X", msg[q++]);
- p += 3;
- }
- if (q < n)
- sprintf(p, "...");
- ast_verb(3, "SMS %s%s\n", dir == DIR_RX ? "RX" : "TX", txt);
-}
-
-
-static void sms_messagerx(sms_t * h)
-{
- int cause;
-
- sms_debug (DIR_RX, h);
- if (h->protocol == 2) {
- sms_messagerx2(h);
- return;
- }
- /* parse incoming message for Protocol 1 */
- switch (h->imsg[0]) {
- case 0x91: /* SMS_DATA */
- cause = sms_handleincoming (h);
- if (!cause) {
- sms_log(h, 'Y');
- h->omsg[0] = 0x95; /* SMS_ACK */
- h->omsg[1] = 0x02;
- h->omsg[2] = 0x00; /* deliver report */
- h->omsg[3] = 0x00; /* no parameters */
- } else { /* NACK */
- sms_log(h, 'N');
- h->omsg[0] = 0x96; /* SMS_NACK */
- h->omsg[1] = 3;
- h->omsg[2] = 0; /* delivery report */
- h->omsg[3] = cause; /* cause */
- h->omsg[4] = 0; /* no parameters */
- }
- sms_messagetx(h);
- break;
-
- case 0x92: /* SMS_ERROR */
- h->err = 1;
- sms_messagetx(h); /* send whatever we sent again */
- break;
- case 0x93: /* SMS_EST */
- sms_nextoutgoing (h);
- break;
- case 0x94: /* SMS_REL */
- h->hangup = 1; /* hangup */
- break;
- case 0x95: /* SMS_ACK */
- sms_log(h, 'Y');
- sms_nextoutgoing (h);
- break;
- case 0x96: /* SMS_NACK */
- h->err = 1;
- sms_log(h, 'N');
- sms_nextoutgoing (h);
- break;
- default: /* Unknown */
- h->omsg[0] = 0x92; /* SMS_ERROR */
- h->omsg[1] = 1;
- h->omsg[2] = 3; /* unknown message type; */
- sms_messagetx(h);
- break;
- }
-}
-
-static void sms_messagetx(sms_t * h)
-{
- unsigned char c = 0, p;
- int len = h->omsg[1] + 2; /* total message length excluding checksum */
-
- for (p = 0; p < len; p++) /* compute checksum */
- c += h->omsg[p];
- h->omsg[len] = 0 - c; /* actually, (256 - (c & 0fxx)) & 0xff) */
- sms_debug(DIR_TX, h);
- h->framenumber++; /* Proto 2 */
- h->obyte = 1; /* send mark ('1') at the beginning */
- h->opause = 200;
- /* Change the initial message delay. BT requires 300ms,
- * but for others this might be way too much and the phone
- * could time out. XXX make it configurable.
- */
- if (h->omsg[0] == 0x93)
- h->opause = 8 * h->opause_0; /* initial message delay */
- h->obytep = 0;
- h->obitp = 0;
- if (h->protocol == 2) {
- h->oseizure = 300; /* Proto 2: 300bits (or more ?) */
- h->obyte = 0; /* Seizure starts with space (0) */
- h->opause = 400;
- } else {
- h->oseizure = 0; /* Proto 1: No seizure */
- }
- /* Note - setting osync triggers the generator */
- h->osync = OSYNC_BITS; /* 80 sync bits */
- h->obyten = len + 1; /* bytes to send (including checksum) */
-}
-
-/*!
- * outgoing data are produced by this generator function, that reads from
- * the descriptor whether it has data to send and which ones.
- */
-static int sms_generate(struct ast_channel *chan, void *data, int len, int samples)
-{
- struct ast_frame f = { 0 };
-#define MAXSAMPLES (800)
- output_t *buf;
- sms_t *h = data;
- int i;
-
- if (samples > MAXSAMPLES) {
- ast_log(LOG_WARNING, "Only doing %d samples (%d requested)\n",
- MAXSAMPLES, samples);
- samples = MAXSAMPLES;
- }
- len = samples * sizeof(*buf) + AST_FRIENDLY_OFFSET;
- buf = alloca(len);
-
- f.frametype = AST_FRAME_VOICE;
- f.subclass = __OUT_FMT;
- f.datalen = samples * sizeof(*buf);
- f.offset = AST_FRIENDLY_OFFSET;
- f.mallocd = 0;
- f.data = buf;
- f.samples = samples;
- f.src = "app_sms";
- /* create a buffer containing the digital sms pattern */
- for (i = 0; i < samples; i++) {
- buf[i] = wave_out[0]; /* default is silence */
-
- if (h->opause)
- h->opause--;
- else if (h->obyten || h->osync) { /* sending data */
- buf[i] = wave_out[h->ophase];
- h->ophase += (h->obyte & 1) ? 13 : 21; /* compute next phase */
- if (h->ophase >= 80)
- h->ophase -= 80;
- if ((h->ophasep += 12) >= 80) { /* time to send the next bit */
- h->ophasep -= 80;
- if (h->oseizure > 0) { /* sending channel seizure (proto 2) */
- h->oseizure--;
- h->obyte ^= 1; /* toggle low bit */
- } else if (h->osync) {
- h->obyte = 1; /* send mark as sync bit */
- h->osync--; /* sending sync bits */
- if (h->osync == 0 && h->protocol == 2 && h->omsg[0] == DLL2_SMS_EST) {
- h->obytep = h->obyten = 0; /* we are done */
- }
- } else {
- h->obitp++;
- if (h->obitp == 1)
- h->obyte = 0; /* start bit; */
- else if (h->obitp == 2)
- h->obyte = h->omsg[h->obytep];
- else if (h->obitp == 10) {
- h->obyte = 1; /* stop bit */
- h->obitp = 0;
- h->obytep++;
- if (h->obytep == h->obyten) {
- h->obytep = h->obyten = 0; /* sent */
- h->osync = 10; /* trailing marks */
- }
- } else
- h->obyte >>= 1;
- }
- }
- }
- }
- if (ast_write(chan, &f) < 0) {
- ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
- return -1;
- }
- return 0;
-#undef MAXSAMPLES
-}
-
-/*!
- * Just return the pointer to the descriptor that we received.
- */
-static void *sms_alloc(struct ast_channel *chan, void *sms_t_ptr)
-{
- return sms_t_ptr;
-}
-
-static void sms_release(struct ast_channel *chan, void *data)
-{
- return; /* nothing to do here. */
-}
-
-static struct ast_generator smsgen = {
- .alloc = sms_alloc,
- .release = sms_release,
- .generate = sms_generate,
-};
-
-/*!
- * Process an incoming frame, trying to detect the carrier and
- * decode the message. The two frequencies are 1300 and 2100 Hz.
- * The decoder detects the amplitude of the signal over the last
- * few samples, filtering the absolute values with a lowpass filter.
- * If the magnitude (h->imag) is large enough, multiply the signal
- * by the two carriers, and compute the amplitudes m0 and m1.
- * Record the current sample as '0' or '1' depending on which one is greater.
- * The last 3 bits are stored in h->ibith, with the count of '1'
- * bits in h->ibitt.
- * XXX the rest is to be determined.
- */
-static void sms_process(sms_t * h, int samples, signed short *data)
-{
- int bit;
-
- /*
- * Ignore incoming audio while a packet is being transmitted,
- * the protocol is half-duplex.
- * Unfortunately this means that if the outbound and incoming
- * transmission overlap (which is an error condition anyways),
- * we may miss some data and this makes debugging harder.
- */
- if (h->obyten || h->osync)
- return;
- for ( ; samples-- ; data++) {
- unsigned long long m0, m1;
- if (abs(*data) > h->imag)
- h->imag = abs(*data);
- else
- h->imag = h->imag * 7 / 8;
- if (h->imag <= 500) { /* below [arbitrary] threahold: lost carrier */
- if (h->idle++ == 80000) { /* nothing happening */
- ast_log(LOG_NOTICE, "No data, hanging up\n");
- h->hangup = 1;
- h->err = 1;
- }
- if (h->ierr) { /* error */
- ast_log(LOG_NOTICE, "Error %d, hanging up\n", h->ierr);
- /* Protocol 1 */
- h->err = 1;
- h->omsg[0] = 0x92; /* error */
- h->omsg[1] = 1;
- h->omsg[2] = h->ierr;
- sms_messagetx(h); /* send error */
- }
- h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
- continue;
- }
- h->idle = 0;
-
- /* multiply signal by the two carriers. */
- h->ims0 = (h->ims0 * 6 + *data * wave[h->ips0]) / 7;
- h->imc0 = (h->imc0 * 6 + *data * wave[h->ipc0]) / 7;
- h->ims1 = (h->ims1 * 6 + *data * wave[h->ips1]) / 7;
- h->imc1 = (h->imc1 * 6 + *data * wave[h->ipc1]) / 7;
- /* compute the amplitudes */
- m0 = h->ims0 * h->ims0 + h->imc0 * h->imc0;
- m1 = h->ims1 * h->ims1 + h->imc1 * h->imc1;
-
- /* advance the sin/cos pointers */
- if ((h->ips0 += 21) >= 80)
- h->ips0 -= 80;
- if ((h->ipc0 += 21) >= 80)
- h->ipc0 -= 80;
- if ((h->ips1 += 13) >= 80)
- h->ips1 -= 80;
- if ((h->ipc1 += 13) >= 80)
- h->ipc1 -= 80;
-
- /* set new bit to 1 or 0 depending on which value is stronger */
- h->ibith <<= 1;
- if (m1 > m0)
- h->ibith |= 1;
- if (h->ibith & 8)
- h->ibitt--;
- if (h->ibith & 1)
- h->ibitt++;
- bit = ((h->ibitt > 1) ? 1 : 0);
- if (bit != h->ibitl)
- h->ibitc = 1;
- else
- h->ibitc++;
- h->ibitl = bit;
- if (!h->ibitn && h->ibitc == 4 && !bit) {
- h->ibitn = 1;
- h->iphasep = 0;
- }
- if (bit && h->ibitc == 200) { /* sync, restart message */
- /* Protocol 2: empty connnection ready (I am master) */
- if (h->framenumber < 0 && h->ibytec >= 160 && !memcmp(h->imsg, "UUUUUUUUUUUUUUUUUUUU", 20)) {
- h->framenumber = 1;
- ast_verb(3, "SMS protocol 2 detected\n");
- h->protocol = 2;
- h->imsg[0] = 0xff; /* special message (fake) */
- h->imsg[1] = h->imsg[2] = 0x00;
- h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
- sms_messagerx(h);
- }
- h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
- }
- if (h->ibitn) {
- h->iphasep += 12;
- if (h->iphasep >= 80) { /* next bit */
- h->iphasep -= 80;
- if (h->ibitn++ == 9) { /* end of byte */
- if (!bit) { /* bad stop bit */
- ast_log(LOG_NOTICE, "bad stop bit");
- h->ierr = 0xFF; /* unknown error */
- } else {
- if (h->ibytep < sizeof(h->imsg)) {
- h->imsg[h->ibytep] = h->ibytev;
- h->ibytec += h->ibytev;
- h->ibytep++;
- } else if (h->ibytep == sizeof(h->imsg)) {
- ast_log(LOG_NOTICE, "msg too large");
- h->ierr = 2; /* bad message length */
- }
- if (h->ibytep > 1 && h->ibytep == 3 + h->imsg[1] && !h->ierr) {
- if (!h->ibytec)
- sms_messagerx(h);
- else {
- ast_log(LOG_NOTICE, "bad checksum");
- h->ierr = 1; /* bad checksum */
- }
- }
- }
- h->ibitn = 0;
- }
- h->ibytev = (h->ibytev >> 1) + (bit ? 0x80 : 0);
- }
- }
- }
-}
-
-/*
- * Standard argument parsing:
- * - one enum for the flags we recognise,
- * - one enum for argument indexes
- * - AST_APP_OPTIONS() to drive the parsing routine
- * - in the function, AST_DECLARE_APP_ARGS(...) for the arguments.
- */
-enum {
- OPTION_BE_SMSC = (1 << 0), /* act as sms center */
- OPTION_ANSWER = (1 << 1), /* answer on incoming calls */
- OPTION_TWO = (1 << 2), /* Use Protocol Two */
- OPTION_PAUSE = (1 << 3), /* pause before sending data, in ms */
- OPTION_SRR = (1 << 4), /* set srr */
- OPTION_DCS = (1 << 5), /* set dcs */
-} sms_flags;
-
-enum {
- OPTION_ARG_PAUSE = 0,
- OPTION_ARG_ARRAY_SIZE
-} sms_opt_args;
-
-AST_APP_OPTIONS(sms_options, {
- AST_APP_OPTION('s', OPTION_BE_SMSC),
- AST_APP_OPTION('a', OPTION_ANSWER),
- AST_APP_OPTION('t', OPTION_TWO),
- AST_APP_OPTION('r', OPTION_SRR),
- AST_APP_OPTION('o', OPTION_DCS),
- AST_APP_OPTION_ARG('p', OPTION_PAUSE, OPTION_ARG_PAUSE),
- } );
-
-static int sms_exec(struct ast_channel *chan, void *data)
-{
- int res = -1;
- sms_t h = { 0 };
- /* argument parsing support */
- struct ast_flags sms_flags;
- char *parse, *sms_opts[OPTION_ARG_ARRAY_SIZE];
- char *p;
- AST_DECLARE_APP_ARGS(sms_args,
- AST_APP_ARG(queue);
- AST_APP_ARG(options);
- AST_APP_ARG(addr);
- AST_APP_ARG(body);
- );
-
- if (!data) {
- ast_log(LOG_ERROR, "Requires queue name at least\n");
- return -1;
- }
-
- parse = ast_strdupa(data); /* create a local copy */
- AST_STANDARD_APP_ARGS(sms_args, parse);
- if (sms_args.argc > 1)
- ast_app_parse_options(sms_options, &sms_flags, sms_opts, sms_args.options);
-
- ast_verb(1, "sms argc %d queue <%s> opts <%s> addr <%s> body <%s>\n",
- sms_args.argc, S_OR(sms_args.queue, ""),
- S_OR(sms_args.options, ""),
- S_OR(sms_args.addr, ""),
- S_OR(sms_args.body, "") );
-
- h.ipc0 = h.ipc1 = 20; /* phase for cosine */
- h.dcs = 0xF1; /* default */
-
- if (chan->cid.cid_num)
- ast_copy_string(h.cli, chan->cid.cid_num, sizeof(h.cli));
-
- if (ast_strlen_zero(sms_args.queue)) {
- ast_log(LOG_ERROR, "Requires queue name\n");
- goto done;
- }
- if (strlen(sms_args.queue) >= sizeof(h.queue)) {
- ast_log(LOG_ERROR, "Queue name too long\n");
- goto done;
- }
- ast_copy_string(h.queue, sms_args.queue, sizeof(h.queue));
-
- for (p = h.queue; *p; p++)
- if (!isalnum(*p))
- *p = '-'; /* make very safe for filenames */
-
- h.smsc = ast_test_flag(&sms_flags, OPTION_BE_SMSC);
- h.protocol = ast_test_flag(&sms_flags, OPTION_TWO) ? 2 : 1;
- if (!ast_strlen_zero(sms_opts[OPTION_ARG_PAUSE]))
- h.opause_0 = atoi(sms_opts[OPTION_ARG_PAUSE]);
- if (h.opause_0 < 25 || h.opause_0 > 2000)
- h.opause_0 = 300; /* default 300ms */
- ast_verb(1, "initial delay %dms\n", h.opause_0);
-
-
- /* the following apply if there is an arg3/4 and apply to the created message file */
- if (ast_test_flag(&sms_flags, OPTION_SRR))
- h.srr = 1;
- if (ast_test_flag(&sms_flags, OPTION_DCS))
- h.dcs = 1;
-#if 0
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7': /* set the pid for saved local message */
- h.pid = 0x40 + (*d & 0xF);
- break;
- }
-#endif
- if (sms_args.argc > 2) {
- unsigned char *up;
-
- /* submitting a message, not taking call. */
- /* deprecated, use smsq instead */
- h.scts = ast_tvnow();
- if (ast_strlen_zero(sms_args.addr) || strlen(sms_args.addr) >= sizeof(h.oa)) {
- ast_log(LOG_ERROR, "Address too long %s\n", sms_args.addr);
- goto done;
- }
- if (h.smsc)
- ast_copy_string(h.oa, sms_args.addr, sizeof(h.oa));
- else {
- ast_copy_string(h.da, sms_args.addr, sizeof(h.da));
- ast_copy_string(h.oa, h.cli, sizeof(h.oa));
- }
- h.udl = 0;
- if (ast_strlen_zero(sms_args.body)) {
- ast_log(LOG_ERROR, "Missing body for %s\n", sms_args.addr);
- goto done;
- }
- up = (unsigned char *)sms_args.body;
- while (*up && h.udl < SMSLEN)
- h.ud[h.udl++] = utf8decode(&up);
- if (is7bit(h.dcs) && packsms7(0, h.udhl, h.udh, h.udl, h.ud) < 0) {
- ast_log(LOG_WARNING, "Invalid 7 bit GSM data\n");
- goto done;
- }
- if (is8bit(h.dcs) && packsms8(0, h.udhl, h.udh, h.udl, h.ud) < 0) {
- ast_log(LOG_WARNING, "Invalid 8 bit data\n");
- goto done;
- }
- if (is16bit(h.dcs) && packsms16(0, h.udhl, h.udh, h.udl, h.ud) < 0) {
- ast_log(LOG_WARNING, "Invalid 16 bit data\n");
- goto done;
- }
- h.rx = 0; /* sent message */
- h.mr = -1;
- sms_writefile(&h);
- res = h.err;
- goto done;
- }
-
- if (ast_test_flag(&sms_flags, OPTION_ANSWER)) {
- h.framenumber = 1; /* Proto 2 */
- /* set up SMS_EST initial message */
- if (h.protocol == 2) {
- h.omsg[0] = DLL2_SMS_EST;
- h.omsg[1] = 0;
- } else {
- h.omsg[0] = DLL1_SMS_EST | DLL1_SMS_COMPLETE;
- h.omsg[1] = 0;
- }
- sms_messagetx(&h);
- }
-
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- res = ast_set_write_format(chan, __OUT_FMT);
- if (res >= 0)
- res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
- if (res < 0) {
- ast_log(LOG_ERROR, "Unable to set to linear mode, giving up\n");
- goto done;
- }
-
- if ( (res = ast_activate_generator(chan, &smsgen, &h)) < 0) {
- ast_log(LOG_ERROR, "Failed to activate generator on '%s'\n", chan->name);
- goto done;
- }
-
- /* Do our thing here */
- for (;;) {
- struct ast_frame *f;
- int i = ast_waitfor(chan, -1);
- if (i < 0) {
- ast_log(LOG_NOTICE, "waitfor failed\n");
- break;
- }
- if (h.hangup) {
- ast_log(LOG_NOTICE, "channel hangup\n");
- break;
- }
- f = ast_read(chan);
- if (!f) {
- ast_log(LOG_NOTICE, "ast_read failed\n");
- break;
- }
- if (f->frametype == AST_FRAME_VOICE) {
- sms_process(&h, f->samples, f->data);
- }
-
- ast_frfree(f);
- }
- res = h.err; /* XXX */
-
- sms_log(&h, '?'); /* log incomplete message */
-done:
- return (res);
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
-#ifdef OUTALAW
- int p;
- for (p = 0; p < 80; p++)
- wavea[p] = AST_LIN2A (wave[p]);
-#endif
- snprintf(log_file, sizeof(log_file), "%s/sms", ast_config_AST_LOG_DIR);
- return ast_register_application(app, sms_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SMS/PSTN handler");
diff --git a/trunk/apps/app_softhangup.c b/trunk/apps/app_softhangup.c
deleted file mode 100644
index 7af852560..000000000
--- a/trunk/apps/app_softhangup.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief SoftHangup application
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-#include "asterisk/app.h"
-
-static char *synopsis = "Soft Hangup Application";
-
-static char *desc = " SoftHangup(Technology/resource[,options]):\n"
-"Hangs up the requested channel. If there are no channels to hangup,\n"
-"the application will report it.\n"
-" Options:\n"
-" 'a' - hang up all channels on a specified device instead of a single resource\n";
-
-static char *app = "SoftHangup";
-
-enum {
- OPTION_ALL = (1 << 0),
-};
-
-AST_APP_OPTIONS(app_opts,{
- AST_APP_OPTION('a', OPTION_ALL),
-});
-
-static int softhangup_exec(struct ast_channel *chan, void *data)
-{
- struct ast_channel *c = NULL;
- char *cut, *opts[0];
- char name[AST_CHANNEL_NAME] = "", *parse;
- struct ast_flags flags;
- int lenmatch;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(channel);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "SoftHangup requires an argument (Technology/resource)\n");
- return 0;
- }
-
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (args.argc == 2)
- ast_app_parse_options(app_opts, &flags, opts, args.options);
- lenmatch = strlen(args.channel);
-
- for (c = ast_walk_channel_by_name_prefix_locked(NULL, args.channel, lenmatch);
- c;
- c = ast_walk_channel_by_name_prefix_locked(c, args.channel, lenmatch)) {
- ast_copy_string(name, c->name, sizeof(name));
- if (ast_test_flag(&flags, OPTION_ALL)) {
- /* CAPI is set up like CAPI[foo/bar]/clcnt */
- if (!strcmp(c->tech->type, "CAPI"))
- cut = strrchr(name, '/');
- /* Basically everything else is Foo/Bar-Z */
- else
- cut = strchr(name, '-');
- /* Get rid of what we've cut */
- if (cut)
- *cut = 0;
- }
- if (!strcasecmp(name, args.channel)) {
- ast_log(LOG_WARNING, "Soft hanging %s up.\n", c->name);
- ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
- if (!ast_test_flag(&flags, OPTION_ALL)) {
- ast_channel_unlock(c);
- break;
- }
- }
- ast_channel_unlock(c);
- }
-
- return 0;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, softhangup_exec, synopsis, desc);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hangs up the requested channel");
diff --git a/trunk/apps/app_speech_utils.c b/trunk/apps/app_speech_utils.c
deleted file mode 100644
index 327e7922f..000000000
--- a/trunk/apps/app_speech_utils.c
+++ /dev/null
@@ -1,796 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2006, Digium, Inc.
- *
- * Joshua Colp <jcolp@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Speech Recognition Utility Applications
- *
- * \author Joshua Colp <jcolp@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-#include "asterisk/app.h"
-#include "asterisk/speech.h"
-
-/* Descriptions for each application */
-static char *speechcreate_descrip =
-" SpeechCreate(engine name):\n"
-"This application creates information to be used by all the other applications.\n"
-"It must be called before doing any speech recognition activities such as activating a grammar.\n"
-"It takes the engine name to use as the argument, if not specified the default engine will be used.\n";
-
-static char *speechactivategrammar_descrip =
-" SpeechActivateGrammar(Grammar Name):\n"
-"This activates the specified grammar to be recognized by the engine.\n"
-"A grammar tells the speech recognition engine what to recognize, and how to portray it back to you \n"
-"in the dialplan. The grammar name is the only argument to this application.\n";
-
-static char *speechstart_descrip =
-" SpeechStart():\n"
-"Tell the speech recognition engine that it should start trying to get results from audio being \n"
-"fed to it. This has no arguments.\n";
-
-static char *speechbackground_descrip =
-" SpeechBackground(Sound File,Timeout):\n"
-"This application plays a sound file and waits for the person to speak. Once they start speaking playback\n"
-"of the file stops, and silence is heard. Once they stop talking the processing sound is played to indicate\n"
-"the speech recognition engine is working. Once results are available the application returns and results \n"
-"(score and text) are available using dialplan functions.\n"
-"The first text and score are ${SPEECH_TEXT(0)} AND ${SPEECH_SCORE(0)} while the second are ${SPEECH_TEXT(1)}\n"
-"and ${SPEECH_SCORE(1)}.\n"
-"The first argument is the sound file and the second is the timeout integer in seconds. Note the timeout will\n"
-"only start once the sound file has stopped playing.\n";
-
-static char *speechdeactivategrammar_descrip =
-" SpeechDeactivateGrammar(Grammar Name):\n"
-"This deactivates the specified grammar so that it is no longer recognized.\n"
-"The only argument is the grammar name to deactivate.\n";
-
-static char *speechprocessingsound_descrip =
-" SpeechProcessingSound(Sound File):\n"
-"This changes the processing sound that SpeechBackground plays back when the speech recognition engine is\n"
-"processing and working to get results.\n"
-"It takes the sound file as the only argument.\n";
-
-static char *speechdestroy_descrip =
-" SpeechDestroy():\n"
-"This destroys the information used by all the other speech recognition applications.\n"
-"If you call this application but end up wanting to recognize more speech, you must call SpeechCreate\n"
- "again before calling any other application. It takes no arguments.\n";
-
-static char *speechload_descrip =
-" SpeechLoadGrammar(Grammar Name,Path):\n"
-"Load a grammar only on the channel, not globally.\n"
-"It takes the grammar name as first argument and path as second.\n";
-
-static char *speechunload_descrip =
-" SpeechUnloadGrammar(Grammar Name):\n"
-"Unload a grammar. It takes the grammar name as the only argument.\n";
-
-/*! \brief Helper function used by datastores to destroy the speech structure upon hangup */
-static void destroy_callback(void *data)
-{
- struct ast_speech *speech = (struct ast_speech*)data;
-
- if (speech == NULL) {
- return;
- }
-
- /* Deallocate now */
- ast_speech_destroy(speech);
-
- return;
-}
-
-/*! \brief Static structure for datastore information */
-static const struct ast_datastore_info speech_datastore = {
- .type = "speech",
- .destroy = destroy_callback
-};
-
-/*! \brief Helper function used to find the speech structure attached to a channel */
-static struct ast_speech *find_speech(struct ast_channel *chan)
-{
- struct ast_speech *speech = NULL;
- struct ast_datastore *datastore = NULL;
-
- datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
- if (datastore == NULL) {
- return NULL;
- }
- speech = datastore->data;
-
- return speech;
-}
-
-/* Helper function to find a specific speech recognition result by number and nbest alternative */
-static struct ast_speech_result *find_result(struct ast_speech_result *results, char *result_num)
-{
- struct ast_speech_result *result = results;
- char *tmp = NULL;
- int nbest_num = 0, wanted_num = 0, i = 0;
-
- if (!result)
- return NULL;
-
- if ((tmp = strchr(result_num, '/'))) {
- *tmp++ = '\0';
- nbest_num = atoi(result_num);
- wanted_num = atoi(tmp);
- } else {
- wanted_num = atoi(result_num);
- }
-
- do {
- if (result->nbest_num != nbest_num)
- continue;
- if (i == wanted_num)
- break;
- i++;
- } while ((result = AST_LIST_NEXT(result, list)));
-
- return result;
-}
-
-/*! \brief SPEECH_SCORE() Dialplan Function */
-static int speech_score(struct ast_channel *chan, const char *cmd, char *data,
- char *buf, size_t len)
-{
- struct ast_speech_result *result = NULL;
- struct ast_speech *speech = find_speech(chan);
- char tmp[128] = "";
-
- if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
- return -1;
-
- snprintf(tmp, sizeof(tmp), "%d", result->score);
-
- ast_copy_string(buf, tmp, len);
-
- return 0;
-}
-
-static struct ast_custom_function speech_score_function = {
- .name = "SPEECH_SCORE",
- .synopsis = "Gets the confidence score of a result.",
- .syntax = "SPEECH_SCORE([nbest number/]result number)",
- .desc =
- "Gets the confidence score of a result.\n",
- .read = speech_score,
- .write = NULL,
-};
-
-/*! \brief SPEECH_TEXT() Dialplan Function */
-static int speech_text(struct ast_channel *chan, const char *cmd, char *data,
- char *buf, size_t len)
-{
- struct ast_speech_result *result = NULL;
- struct ast_speech *speech = find_speech(chan);
-
- if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
- return -1;
-
- if (result->text != NULL)
- ast_copy_string(buf, result->text, len);
-
- return 0;
-}
-
-static struct ast_custom_function speech_text_function = {
- .name = "SPEECH_TEXT",
- .synopsis = "Gets the recognized text of a result.",
- .syntax = "SPEECH_TEXT([nbest number/]result number)",
- .desc =
- "Gets the recognized text of a result.\n",
- .read = speech_text,
- .write = NULL,
-};
-
-/*! \brief SPEECH_GRAMMAR() Dialplan Function */
-static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data,
- char *buf, size_t len)
-{
- struct ast_speech_result *result = NULL;
- struct ast_speech *speech = find_speech(chan);
-
- if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
- return -1;
-
- if (result->grammar != NULL)
- ast_copy_string(buf, result->grammar, len);
-
- return 0;
-}
-
-static struct ast_custom_function speech_grammar_function = {
- .name = "SPEECH_GRAMMAR",
- .synopsis = "Gets the matched grammar of a result if available.",
- .syntax = "SPEECH_GRAMMAR([nbest number/]result number)",
- .desc =
- "Gets the matched grammar of a result if available.\n",
- .read = speech_grammar,
- .write = NULL,
-};
-
-/*! \brief SPEECH_ENGINE() Dialplan Function */
-static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
-{
- struct ast_speech *speech = find_speech(chan);
-
- if (data == NULL || speech == NULL)
- return -1;
-
- ast_speech_change(speech, data, value);
-
- return 0;
-}
-
-static struct ast_custom_function speech_engine_function = {
- .name = "SPEECH_ENGINE",
- .synopsis = "Change a speech engine specific attribute.",
- .syntax = "SPEECH_ENGINE(name)=value",
- .desc =
- "Changes a speech engine specific attribute.\n",
- .read = NULL,
- .write = speech_engine_write,
-};
-
-/*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */
-static int speech_results_type_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
-{
- struct ast_speech *speech = find_speech(chan);
-
- if (data == NULL || speech == NULL)
- return -1;
-
- if (!strcasecmp(value, "normal"))
- ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NORMAL);
- else if (!strcasecmp(value, "nbest"))
- ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NBEST);
-
- return 0;
-}
-
-static struct ast_custom_function speech_results_type_function = {
- .name = "SPEECH_RESULTS_TYPE",
- .synopsis = "Sets the type of results that will be returned.",
- .syntax = "SPEECH_RESULTS_TYPE()=results type",
- .desc =
- "Sets the type of results that will be returned. Valid options are normal or nbest.",
- .read = NULL,
- .write = speech_results_type_write,
-};
-
-/*! \brief SPEECH() Dialplan Function */
-static int speech_read(struct ast_channel *chan, const char *cmd, char *data,
- char *buf, size_t len)
-{
- int results = 0;
- struct ast_speech_result *result = NULL;
- struct ast_speech *speech = find_speech(chan);
- char tmp[128] = "";
-
- /* Now go for the various options */
- if (!strcasecmp(data, "status")) {
- if (speech != NULL)
- ast_copy_string(buf, "1", len);
- else
- ast_copy_string(buf, "0", len);
- return 0;
- }
-
- /* Make sure we have a speech structure for everything else */
- if (speech == NULL) {
- return -1;
- }
-
- /* Check to see if they are checking for silence */
- if (!strcasecmp(data, "spoke")) {
- if (ast_test_flag(speech, AST_SPEECH_SPOKE))
- ast_copy_string(buf, "1", len);
- else
- ast_copy_string(buf, "0", len);
- } else if (!strcasecmp(data, "results")) {
- /* Count number of results */
- for (result = speech->results; result; result = AST_LIST_NEXT(result, list))
- results++;
- snprintf(tmp, sizeof(tmp), "%d", results);
- ast_copy_string(buf, tmp, len);
- }
-
- return 0;
-}
-
-static struct ast_custom_function speech_function = {
- .name = "SPEECH",
- .synopsis = "Gets information about speech recognition results.",
- .syntax = "SPEECH(argument)",
- .desc =
- "Gets information about speech recognition results.\n"
- "status: Returns 1 upon speech object existing, or 0 if not\n"
- "spoke: Returns 1 if spoker spoke, or 0 if not\n"
- "results: Returns number of results that were recognized\n",
- .read = speech_read,
- .write = NULL,
-};
-
-
-
-/*! \brief SpeechCreate() Dialplan Application */
-static int speech_create(struct ast_channel *chan, void *data)
-{
- struct ast_speech *speech = NULL;
- struct ast_datastore *datastore = NULL;
-
- /* Request a speech object */
- speech = ast_speech_new(data, chan->nativeformats);
- if (speech == NULL) {
- /* Not available */
- pbx_builtin_setvar_helper(chan, "ERROR", "1");
- return 0;
- }
-
- datastore = ast_channel_datastore_alloc(&speech_datastore, NULL);
- if (datastore == NULL) {
- ast_speech_destroy(speech);
- pbx_builtin_setvar_helper(chan, "ERROR", "1");
- return 0;
- }
- datastore->data = speech;
- ast_channel_datastore_add(chan, datastore);
-
- return 0;
-}
-
-/*! \brief SpeechLoadGrammar(Grammar Name,Path) Dialplan Application */
-static int speech_load(struct ast_channel *chan, void *vdata)
-{
- int res = 0;
- struct ast_speech *speech = find_speech(chan);
- char *data;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(grammar);
- AST_APP_ARG(path);
- );
-
- data = ast_strdupa(vdata);
- AST_STANDARD_APP_ARGS(args, data);
-
- if (speech == NULL)
- return -1;
-
- if (args.argc != 2)
- return -1;
-
- /* Load the grammar locally on the object */
- res = ast_speech_grammar_load(speech, args.grammar, args.path);
-
- return res;
-}
-
-/*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
-static int speech_unload(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_speech *speech = find_speech(chan);
-
- if (speech == NULL)
- return -1;
-
- /* Unload the grammar */
- res = ast_speech_grammar_unload(speech, data);
-
- return res;
-}
-
-/*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
-static int speech_deactivate(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_speech *speech = find_speech(chan);
-
- if (speech == NULL)
- return -1;
-
- /* Deactivate the grammar on the speech object */
- res = ast_speech_grammar_deactivate(speech, data);
-
- return res;
-}
-
-/*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
-static int speech_activate(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_speech *speech = find_speech(chan);
-
- if (speech == NULL)
- return -1;
-
- /* Activate the grammar on the speech object */
- res = ast_speech_grammar_activate(speech, data);
-
- return res;
-}
-
-/*! \brief SpeechStart() Dialplan Application */
-static int speech_start(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_speech *speech = find_speech(chan);
-
- if (speech == NULL)
- return -1;
-
- ast_speech_start(speech);
-
- return res;
-}
-
-/*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
-static int speech_processing_sound(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_speech *speech = find_speech(chan);
-
- if (speech == NULL)
- return -1;
-
- if (speech->processing_sound != NULL) {
- ast_free(speech->processing_sound);
- speech->processing_sound = NULL;
- }
-
- speech->processing_sound = ast_strdup(data);
-
- return res;
-}
-
-/*! \brief Helper function used by speech_background to playback a soundfile */
-static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
-{
- struct ast_filestream *fs = NULL;
-
- if (!(fs = ast_openstream(chan, filename, preflang)))
- return -1;
-
- if (ast_applystream(chan, fs))
- return -1;
-
- ast_playstream(fs);
-
- return 0;
-}
-
-/*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
-static int speech_background(struct ast_channel *chan, void *data)
-{
- unsigned int timeout = 0;
- int res = 0, done = 0, started = 0, quieted = 0, max_dtmf_len = 0;
- struct ast_speech *speech = find_speech(chan);
- struct ast_frame *f = NULL;
- int oldreadformat = AST_FORMAT_SLINEAR;
- char dtmf[AST_MAX_EXTENSION] = "";
- time_t start, current;
- struct ast_datastore *datastore = NULL;
- char *parse, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#';
- const char *tmp2 = NULL;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(soundfile);
- AST_APP_ARG(timeout);
- );
-
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (speech == NULL)
- return -1;
-
- /* If channel is not already answered, then answer it */
- if (chan->_state != AST_STATE_UP && ast_answer(chan))
- return -1;
-
- /* Record old read format */
- oldreadformat = chan->readformat;
-
- /* Change read format to be signed linear */
- if (ast_set_read_format(chan, speech->format))
- return -1;
-
- if (!ast_strlen_zero(args.soundfile)) {
- /* Yay sound file */
- filename_tmp = ast_strdupa(args.soundfile);
- if (!ast_strlen_zero(args.timeout)) {
- if ((timeout = atoi(args.timeout)) == 0)
- timeout = -1;
- } else
- timeout = 0;
- }
-
- /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */
- if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2))
- max_dtmf_len = atoi(tmp2);
-
- /* See if a terminator is specified */
- if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
- if (ast_strlen_zero(tmp2))
- dtmf_terminator = '\0';
- else
- dtmf_terminator = tmp2[0];
- }
-
- /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
- if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
- ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
- ast_speech_start(speech);
- }
-
- /* Ensure no streams are currently running */
- ast_stopstream(chan);
-
- /* Okay it's streaming so go into a loop grabbing frames! */
- while (done == 0) {
- /* If the filename is null and stream is not running, start up a new sound file */
- if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) {
- /* Discard old stream information */
- ast_stopstream(chan);
- /* Start new stream */
- speech_streamfile(chan, filename, chan->language);
- }
-
- /* Run scheduled stuff */
- ast_sched_runq(chan->sched);
-
- /* Yay scheduling */
- res = ast_sched_wait(chan->sched);
- if (res < 0)
- res = 1000;
-
- /* If there is a frame waiting, get it - if not - oh well */
- if (ast_waitfor(chan, res) > 0) {
- f = ast_read(chan);
- if (f == NULL) {
- /* The channel has hung up most likely */
- done = 3;
- break;
- }
- }
-
- /* Do timeout check (shared between audio/dtmf) */
- if ((!quieted || strlen(dtmf)) && started == 1) {
- time(&current);
- if ((current-start) >= timeout) {
- done = 1;
- if (f)
- ast_frfree(f);
- break;
- }
- }
-
- /* Do checks on speech structure to see if it's changed */
- ast_mutex_lock(&speech->lock);
- if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
- if (chan->stream)
- ast_stopstream(chan);
- ast_clear_flag(speech, AST_SPEECH_QUIET);
- quieted = 1;
- }
- /* Check state so we can see what to do */
- switch (speech->state) {
- case AST_SPEECH_STATE_READY:
- /* If audio playback has stopped do a check for timeout purposes */
- if (chan->streamid == -1 && chan->timingfunc == NULL)
- ast_stopstream(chan);
- if (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) {
- if (timeout == -1) {
- done = 1;
- if (f)
- ast_frfree(f);
- break;
- }
- time(&start);
- started = 1;
- }
- /* Write audio frame out to speech engine if no DTMF has been received */
- if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
- ast_speech_write(speech, f->data, f->datalen);
- }
- break;
- case AST_SPEECH_STATE_WAIT:
- /* Cue up waiting sound if not already playing */
- if (!strlen(dtmf)) {
- if (chan->stream == NULL) {
- if (speech->processing_sound != NULL) {
- if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
- speech_streamfile(chan, speech->processing_sound, chan->language);
- }
- }
- } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
- ast_stopstream(chan);
- if (speech->processing_sound != NULL) {
- if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
- speech_streamfile(chan, speech->processing_sound, chan->language);
- }
- }
- }
- }
- break;
- case AST_SPEECH_STATE_DONE:
- /* Now that we are done... let's switch back to not ready state */
- ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
- if (!strlen(dtmf)) {
- /* Copy to speech structure the results, if available */
- speech->results = ast_speech_results_get(speech);
- /* Break out of our background too */
- done = 1;
- /* Stop audio playback */
- if (chan->stream != NULL) {
- ast_stopstream(chan);
- }
- }
- break;
- default:
- break;
- }
- ast_mutex_unlock(&speech->lock);
-
- /* Deal with other frame types */
- if (f != NULL) {
- /* Free the frame we received */
- switch (f->frametype) {
- case AST_FRAME_DTMF:
- if (dtmf_terminator != '\0' && f->subclass == dtmf_terminator) {
- done = 1;
- } else {
- if (chan->stream != NULL) {
- ast_stopstream(chan);
- }
- if (!started) {
- /* Change timeout to be 5 seconds for DTMF input */
- timeout = (chan->pbx && chan->pbx->dtimeout) ? chan->pbx->dtimeout : 5;
- started = 1;
- }
- time(&start);
- snprintf(tmp, sizeof(tmp), "%c", f->subclass);
- strncat(dtmf, tmp, sizeof(dtmf));
- /* If the maximum length of the DTMF has been reached, stop now */
- if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
- done = 1;
- }
- break;
- case AST_FRAME_CONTROL:
- switch (f->subclass) {
- case AST_CONTROL_HANGUP:
- /* Since they hung up we should destroy the speech structure */
- done = 3;
- default:
- break;
- }
- default:
- break;
- }
- ast_frfree(f);
- f = NULL;
- }
- }
-
- if (!ast_strlen_zero(dtmf)) {
- /* We sort of make a results entry */
- speech->results = ast_calloc(1, sizeof(*speech->results));
- if (speech->results != NULL) {
- ast_speech_dtmf(speech, dtmf);
- speech->results->score = 1000;
- speech->results->text = ast_strdup(dtmf);
- speech->results->grammar = ast_strdup("dtmf");
- }
- }
-
- /* See if it was because they hung up */
- if (done == 3) {
- /* Destroy speech structure */
- ast_speech_destroy(speech);
- datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
- if (datastore != NULL)
- ast_channel_datastore_remove(chan, datastore);
- } else {
- /* Channel is okay so restore read format */
- ast_set_read_format(chan, oldreadformat);
- }
-
- return 0;
-}
-
-
-/*! \brief SpeechDestroy() Dialplan Application */
-static int speech_destroy(struct ast_channel *chan, void *data)
-{
- int res = 0;
- struct ast_speech *speech = find_speech(chan);
- struct ast_datastore *datastore = NULL;
-
- if (speech == NULL)
- return -1;
-
- /* Destroy speech structure */
- ast_speech_destroy(speech);
-
- datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
- if (datastore != NULL) {
- ast_channel_datastore_remove(chan, datastore);
- }
-
- return res;
-}
-
-static int unload_module(void)
-{
- int res = 0;
-
- res = ast_unregister_application("SpeechCreate");
- res |= ast_unregister_application("SpeechLoadGrammar");
- res |= ast_unregister_application("SpeechUnloadGrammar");
- res |= ast_unregister_application("SpeechActivateGrammar");
- res |= ast_unregister_application("SpeechDeactivateGrammar");
- res |= ast_unregister_application("SpeechStart");
- res |= ast_unregister_application("SpeechBackground");
- res |= ast_unregister_application("SpeechDestroy");
- res |= ast_unregister_application("SpeechProcessingSound");
- res |= ast_custom_function_unregister(&speech_function);
- res |= ast_custom_function_unregister(&speech_score_function);
- res |= ast_custom_function_unregister(&speech_text_function);
- res |= ast_custom_function_unregister(&speech_grammar_function);
- res |= ast_custom_function_unregister(&speech_engine_function);
- res |= ast_custom_function_unregister(&speech_results_type_function);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res = 0;
-
- res = ast_register_application("SpeechCreate", speech_create, "Create a Speech Structure", speechcreate_descrip);
- res |= ast_register_application("SpeechLoadGrammar", speech_load, "Load a Grammar", speechload_descrip);
- res |= ast_register_application("SpeechUnloadGrammar", speech_unload, "Unload a Grammar", speechunload_descrip);
- res |= ast_register_application("SpeechActivateGrammar", speech_activate, "Activate a Grammar", speechactivategrammar_descrip);
- res |= ast_register_application("SpeechDeactivateGrammar", speech_deactivate, "Deactivate a Grammar", speechdeactivategrammar_descrip);
- res |= ast_register_application("SpeechStart", speech_start, "Start recognizing voice in the audio stream", speechstart_descrip);
- res |= ast_register_application("SpeechBackground", speech_background, "Play a sound file and wait for speech to be recognized", speechbackground_descrip);
- res |= ast_register_application("SpeechDestroy", speech_destroy, "End speech recognition", speechdestroy_descrip);
- res |= ast_register_application("SpeechProcessingSound", speech_processing_sound, "Change background processing sound", speechprocessingsound_descrip);
- res |= ast_custom_function_register(&speech_function);
- res |= ast_custom_function_register(&speech_score_function);
- res |= ast_custom_function_register(&speech_text_function);
- res |= ast_custom_function_register(&speech_grammar_function);
- res |= ast_custom_function_register(&speech_engine_function);
- res |= ast_custom_function_register(&speech_results_type_function);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Speech Applications");
diff --git a/trunk/apps/app_stack.c b/trunk/apps/app_stack.c
deleted file mode 100644
index 7f53aff15..000000000
--- a/trunk/apps/app_stack.c
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (c) 2004-2006 Tilghman Lesher <app_stack_v003@the-tilghman.com>.
- *
- * This code is released by the author with no restrictions on usage.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Stack applications Gosub, Return, etc.
- *
- * \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-#include "asterisk/manager.h"
-#include "asterisk/channel.h"
-
-static const char *app_gosub = "Gosub";
-static const char *app_gosubif = "GosubIf";
-static const char *app_return = "Return";
-static const char *app_pop = "StackPop";
-
-static const char *gosub_synopsis = "Jump to label, saving return address";
-static const char *gosubif_synopsis = "Conditionally jump to label, saving return address";
-static const char *return_synopsis = "Return from gosub routine";
-static const char *pop_synopsis = "Remove one address from gosub stack";
-
-static const char *gosub_descrip =
-" Gosub([[context,]exten,]priority[(arg1[,...][,argN])]):\n"
-"Jumps to the label specified, saving the return address.\n";
-static const char *gosubif_descrip =
-" GosubIf(condition?labeliftrue[(arg1[,...])][:labeliffalse[(arg1[,...])]]):\n"
-"If the condition is true, then jump to labeliftrue. If false, jumps to\n"
-"labeliffalse, if specified. In either case, a jump saves the return point\n"
-"in the dialplan, to be returned to with a Return.\n";
-static const char *return_descrip =
-" Return([return-value]):\n"
-"Jumps to the last label on the stack, removing it. The return value, if\n"
-"any, is saved in the channel variable GOSUB_RETVAL.\n";
-static const char *pop_descrip =
-" StackPop():\n"
-"Removes last label on the stack, discarding it.\n";
-
-
-static void gosub_free(void *data);
-
-static struct ast_datastore_info stack_info = {
- .type = "GOSUB",
- .destroy = gosub_free,
-};
-
-struct gosub_stack_frame {
- AST_LIST_ENTRY(gosub_stack_frame) entries;
- /* 100 arguments is all that we support anyway, but this will handle up to 255 */
- unsigned char arguments;
- struct varshead varshead;
- int priority;
- char *context;
- char extension[0];
-};
-
-static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
-{
- struct ast_var_t *variables;
- int found = 0;
-
- /* Does this variable already exist? */
- AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
- if (!strcmp(var, ast_var_name(variables))) {
- found = 1;
- break;
- }
- }
-
- if (!ast_strlen_zero(value)) {
- if (!found) {
- variables = ast_var_assign(var, "");
- AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
- pbx_builtin_pushvar_helper(chan, var, value);
- } else
- pbx_builtin_setvar_helper(chan, var, value);
-
- manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
- "Channel: %s\r\n"
- "Variable: LOCAL(%s)\r\n"
- "Value: %s\r\n"
- "Uniqueid: %s\r\n",
- chan->name, var, value, chan->uniqueid);
- }
- return 0;
-}
-
-static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
-{
- unsigned char i;
- char argname[15];
- struct ast_var_t *vardata;
-
- /* If chan is not defined, then we're calling it as part of gosub_free,
- * and the channel variables will be deallocated anyway. Otherwise, we're
- * just releasing a single frame, so we need to clean up the arguments for
- * that frame, so that we re-expose the variables from the previous frame
- * that were hidden by this one.
- */
- if (chan) {
- for (i = 1; i <= frame->arguments && i != 0; i++) {
- snprintf(argname, sizeof(argname), "ARG%hhd", i);
- pbx_builtin_setvar_helper(chan, argname, NULL);
- }
- }
-
- /* Delete local variables */
- while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
- if (chan)
- pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
- ast_var_delete(vardata);
- }
-
- ast_free(frame);
-}
-
-static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
-{
- struct gosub_stack_frame *new = NULL;
- int len_extension = strlen(extension), len_context = strlen(context);
-
- if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
- AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
- strcpy(new->extension, extension);
- new->context = new->extension + len_extension + 1;
- strcpy(new->context, context);
- new->priority = priority;
- new->arguments = arguments;
- }
- return new;
-}
-
-static void gosub_free(void *data)
-{
- AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
- struct gosub_stack_frame *oldframe;
- AST_LIST_LOCK(oldlist);
- while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
- gosub_release_frame(NULL, oldframe);
- }
- AST_LIST_UNLOCK(oldlist);
- AST_LIST_HEAD_DESTROY(oldlist);
- ast_free(oldlist);
-}
-
-static int pop_exec(struct ast_channel *chan, void *data)
-{
- struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
- struct gosub_stack_frame *oldframe;
- AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
-
- if (!stack_store) {
- ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
- return 0;
- }
-
- oldlist = stack_store->data;
- AST_LIST_LOCK(oldlist);
- oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
- AST_LIST_UNLOCK(oldlist);
-
- if (oldframe) {
- gosub_release_frame(chan, oldframe);
- } else {
- ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
- }
- return 0;
-}
-
-static int return_exec(struct ast_channel *chan, void *data)
-{
- struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
- struct gosub_stack_frame *oldframe;
- AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
- char *retval = data;
-
- if (!stack_store) {
- ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
- return -1;
- }
-
- oldlist = stack_store->data;
- AST_LIST_LOCK(oldlist);
- oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
- AST_LIST_UNLOCK(oldlist);
-
- if (!oldframe) {
- ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
- return -1;
- }
-
- ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
- gosub_release_frame(chan, oldframe);
-
- /* Set a return value, if any */
- pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
- return 0;
-}
-
-static int gosub_exec(struct ast_channel *chan, void *data)
-{
- struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
- AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
- struct gosub_stack_frame *newframe;
- char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
- int i;
- AST_DECLARE_APP_ARGS(args2,
- AST_APP_ARG(argval)[100];
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "%s requires an argument: %s([[context|]exten|]priority[(arg1[|...][|argN])])\n", app_gosub, app_gosub);
- return -1;
- }
-
- if (!stack_store) {
- ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", chan->name);
- stack_store = ast_channel_datastore_alloc(&stack_info, NULL);
- if (!stack_store) {
- ast_log(LOG_ERROR, "Unable to allocate new datastore. Gosub will fail.\n");
- return -1;
- }
-
- oldlist = ast_calloc(1, sizeof(*oldlist));
- if (!oldlist) {
- ast_log(LOG_ERROR, "Unable to allocate datastore list head. Gosub will fail.\n");
- ast_channel_datastore_free(stack_store);
- return -1;
- }
-
- stack_store->data = oldlist;
- AST_LIST_HEAD_INIT(oldlist);
- ast_channel_datastore_add(chan, stack_store);
- }
-
- /* Separate the arguments from the label */
- /* NOTE: you cannot use ast_app_separate_args for this, because '(' cannot be used as a delimiter. */
- label = strsep(&tmp, "(");
- if (tmp) {
- endparen = strrchr(tmp, ')');
- if (endparen)
- *endparen = '\0';
- else
- ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
- AST_STANDARD_APP_ARGS(args2, tmp);
- } else
- args2.argc = 0;
-
- /* Create the return address, but don't save it until we know that the Gosub destination exists */
- newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, args2.argc);
-
- if (!newframe)
- return -1;
-
- if (ast_parseable_goto(chan, label)) {
- ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
- ast_free(newframe);
- return -1;
- }
-
- /* Now that we know for certain that we're going to a new location, set our arguments */
- for (i = 0; i < args2.argc; i++) {
- snprintf(argname, sizeof(argname), "ARG%d", i + 1);
- frame_set_var(chan, newframe, argname, args2.argval[i]);
- ast_debug(1, "Setting '%s' to '%s'\n", argname, args2.argval[i]);
- }
-
- /* And finally, save our return address */
- oldlist = stack_store->data;
- AST_LIST_LOCK(oldlist);
- AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
- AST_LIST_UNLOCK(oldlist);
-
- return 0;
-}
-
-static int gosubif_exec(struct ast_channel *chan, void *data)
-{
- char *args;
- int res=0;
- AST_DECLARE_APP_ARGS(cond,
- AST_APP_ARG(ition);
- AST_APP_ARG(labels);
- );
- AST_DECLARE_APP_ARGS(label,
- AST_APP_ARG(iftrue);
- AST_APP_ARG(iffalse);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
- return 0;
- }
-
- args = ast_strdupa(data);
- AST_NONSTANDARD_APP_ARGS(cond, args, '?');
- if (cond.argc != 2) {
- ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
- return 0;
- }
-
- AST_NONSTANDARD_APP_ARGS(label, cond.labels, ':');
-
- if (pbx_checkcondition(cond.ition)) {
- if (!ast_strlen_zero(label.iftrue))
- res = gosub_exec(chan, label.iftrue);
- } else if (!ast_strlen_zero(label.iffalse)) {
- res = gosub_exec(chan, label.iffalse);
- }
-
- return res;
-}
-
-static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
-{
- struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
- AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
- struct gosub_stack_frame *frame;
- struct ast_var_t *variables;
-
- if (!stack_store)
- return -1;
-
- oldlist = stack_store->data;
- AST_LIST_LOCK(oldlist);
- frame = AST_LIST_FIRST(oldlist);
- AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
- if (!strcmp(data, ast_var_name(variables))) {
- const char *tmp = pbx_builtin_getvar_helper(chan, data);
- ast_copy_string(buf, S_OR(tmp, ""), len);
- break;
- }
- }
- AST_LIST_UNLOCK(oldlist);
- return 0;
-}
-
-static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
-{
- struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
- AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
- struct gosub_stack_frame *frame;
-
- if (!stack_store) {
- ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
- return -1;
- }
-
- oldlist = stack_store->data;
- AST_LIST_LOCK(oldlist);
- frame = AST_LIST_FIRST(oldlist);
-
- if (frame)
- frame_set_var(chan, frame, var, value);
-
- AST_LIST_UNLOCK(oldlist);
-
- return 0;
-}
-
-static struct ast_custom_function local_function = {
- .name = "LOCAL",
- .synopsis = "Variables local to the gosub stack frame",
- .syntax = "LOCAL(<varname>)",
- .write = local_write,
- .read = local_read,
-};
-
-static int unload_module(void)
-{
- ast_unregister_application(app_return);
- ast_unregister_application(app_pop);
- ast_unregister_application(app_gosubif);
- ast_unregister_application(app_gosub);
- ast_custom_function_unregister(&local_function);
-
- return 0;
-}
-
-static int load_module(void)
-{
- ast_register_application(app_pop, pop_exec, pop_synopsis, pop_descrip);
- ast_register_application(app_return, return_exec, return_synopsis, return_descrip);
- ast_register_application(app_gosubif, gosubif_exec, gosubif_synopsis, gosubif_descrip);
- ast_register_application(app_gosub, gosub_exec, gosub_synopsis, gosub_descrip);
- ast_custom_function_register(&local_function);
-
- return 0;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan subroutines (Gosub, Return, etc)");
diff --git a/trunk/apps/app_system.c b/trunk/apps/app_system.c
deleted file mode 100644
index 1f39c5a4e..000000000
--- a/trunk/apps/app_system.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Execute arbitrary system commands
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-#include "asterisk/channel.h" /* autoservice */
-
-static char *app = "System";
-
-static char *app2 = "TrySystem";
-
-static char *synopsis = "Execute a system command";
-
-static char *synopsis2 = "Try executing a system command";
-
-static char *chanvar = "SYSTEMSTATUS";
-
-static char *descrip =
-" System(command): Executes a command by using system(). If the command\n"
-"fails, the console should report a fallthrough. \n"
-"Result of execution is returned in the SYSTEMSTATUS channel variable:\n"
-" FAILURE Could not execute the specified command\n"
-" SUCCESS Specified command successfully executed\n";
-
-static char *descrip2 =
-" TrySystem(command): Executes a command by using system().\n"
-"on any situation.\n"
-"Result of execution is returned in the SYSTEMSTATUS channel variable:\n"
-" FAILURE Could not execute the specified command\n"
-" SUCCESS Specified command successfully executed\n"
-" APPERROR Specified command successfully executed, but returned error code\n";
-
-static int system_exec_helper(struct ast_channel *chan, void *data, int failmode)
-{
- int res = 0;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "System requires an argument(command)\n");
- pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
- return failmode;
- }
-
- ast_autoservice_start(chan);
-
- /* Do our thing here */
- res = ast_safe_system((char *)data);
- if ((res < 0) && (errno != ECHILD)) {
- ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);
- pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
- res = failmode;
- } else if (res == 127) {
- ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);
- pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
- res = failmode;
- } else {
- if (res < 0)
- res = 0;
- if (res != 0)
- pbx_builtin_setvar_helper(chan, chanvar, "APPERROR");
- else
- pbx_builtin_setvar_helper(chan, chanvar, "SUCCESS");
- res = 0;
- }
-
- ast_autoservice_stop(chan);
-
- return res;
-}
-
-static int system_exec(struct ast_channel *chan, void *data)
-{
- return system_exec_helper(chan, data, -1);
-}
-
-static int trysystem_exec(struct ast_channel *chan, void *data)
-{
- return system_exec_helper(chan, data, 0);
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(app);
- res |= ast_unregister_application(app2);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
-
- res = ast_register_application(app2, trysystem_exec, synopsis2, descrip2);
- res |= ast_register_application(app, system_exec, synopsis, descrip);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Generic System() application");
diff --git a/trunk/apps/app_talkdetect.c b/trunk/apps/app_talkdetect.c
deleted file mode 100644
index 0aa5e818b..000000000
--- a/trunk/apps/app_talkdetect.c
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Playback a file with audio detect
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/utils.h"
-#include "asterisk/dsp.h"
-#include "asterisk/app.h"
-
-static char *app = "BackgroundDetect";
-
-static char *synopsis = "Background a file with talk detect";
-
-static char *descrip =
-" BackgroundDetect(filename[,sil[,min,[max]]]): Plays back a given\n"
-"filename, waiting for interruption from a given digit (the digit must\n"
-"start the beginning of a valid extension, or it will be ignored).\n"
-"During the playback of the file, audio is monitored in the receive\n"
-"direction, and if a period of non-silence which is greater than 'min' ms\n"
-"yet less than 'max' ms is followed by silence for at least 'sil' ms then\n"
-"the audio playback is aborted and processing jumps to the 'talk' extension\n"
-"if available. If unspecified, sil, min, and max default to 1000, 100, and\n"
-"infinity respectively.\n";
-
-
-static int background_detect_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char *tmp;
- struct ast_frame *fr;
- int notsilent = 0;
- struct timeval start = { 0, 0};
- int sil = 1000;
- int min = 100;
- int max = -1;
- int x;
- int origrformat=0;
- struct ast_dsp *dsp = NULL;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(filename);
- AST_APP_ARG(silence);
- AST_APP_ARG(min);
- AST_APP_ARG(max);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "BackgroundDetect requires an argument (filename)\n");
- return -1;
- }
-
- tmp = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, tmp);
-
- if ((sscanf(args.silence, "%d", &x) == 1) && (x > 0))
- sil = x;
- if ((sscanf(args.min, "%d", &x) == 1) && (x > 0))
- min = x;
- if ((sscanf(args.max, "%d", &x) == 1) && (x > 0))
- max = x;
-
- ast_debug(1, "Preparing detect of '%s', sil=%d, min=%d, max=%d\n", args.filename, sil, min, max);
- do {
- if (chan->_state != AST_STATE_UP) {
- if ((res = ast_answer(chan)))
- break;
- }
-
- origrformat = chan->readformat;
- if ((ast_set_read_format(chan, AST_FORMAT_SLINEAR))) {
- ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
- res = -1;
- break;
- }
-
- if (!(dsp = ast_dsp_new())) {
- ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
- res = -1;
- break;
- }
- ast_stopstream(chan);
- if (ast_streamfile(chan, tmp, chan->language)) {
- ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
- break;
- }
-
- while (chan->stream) {
- res = ast_sched_wait(chan->sched);
- if ((res < 0) && !chan->timingfunc) {
- res = 0;
- break;
- }
- if (res < 0)
- res = 1000;
- res = ast_waitfor(chan, res);
- if (res < 0) {
- ast_log(LOG_WARNING, "Waitfor failed on %s\n", chan->name);
- break;
- } else if (res > 0) {
- fr = ast_read(chan);
- if (!fr) {
- res = -1;
- break;
- } else if (fr->frametype == AST_FRAME_DTMF) {
- char t[2];
- t[0] = fr->subclass;
- t[1] = '\0';
- if (ast_canmatch_extension(chan, chan->context, t, 1, chan->cid.cid_num)) {
- /* They entered a valid extension, or might be anyhow */
- res = fr->subclass;
- ast_frfree(fr);
- break;
- }
- } else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass == AST_FORMAT_SLINEAR)) {
- int totalsilence;
- int ms;
- res = ast_dsp_silence(dsp, fr, &totalsilence);
- if (res && (totalsilence > sil)) {
- /* We've been quiet a little while */
- if (notsilent) {
- /* We had heard some talking */
- ms = ast_tvdiff_ms(ast_tvnow(), start);
- ms -= sil;
- if (ms < 0)
- ms = 0;
- if ((ms > min) && ((max < 0) || (ms < max))) {
- char ms_str[10];
- ast_debug(1, "Found qualified token of %d ms\n", ms);
-
- /* Save detected talk time (in milliseconds) */
- sprintf(ms_str, "%d", ms );
- pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
-
- ast_goto_if_exists(chan, chan->context, "talk", 1);
- res = 0;
- ast_frfree(fr);
- break;
- } else {
- ast_debug(1, "Found unqualified token of %d ms\n", ms);
- }
- notsilent = 0;
- }
- } else {
- if (!notsilent) {
- /* Heard some audio, mark the begining of the token */
- start = ast_tvnow();
- ast_debug(1, "Start of voice token!\n");
- notsilent = 1;
- }
- }
- }
- ast_frfree(fr);
- }
- ast_sched_runq(chan->sched);
- }
- ast_stopstream(chan);
- } while (0);
-
- if (res > -1) {
- if (origrformat && ast_set_read_format(chan, origrformat)) {
- ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n",
- chan->name, ast_getformatname(origrformat));
- }
- }
- if (dsp)
- ast_dsp_free(dsp);
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, background_detect_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Playback with Talk Detection");
diff --git a/trunk/apps/app_test.c b/trunk/apps/app_test.c
deleted file mode 100644
index e13dc865f..000000000
--- a/trunk/apps/app_test.c
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- * Russell Bryant <russelb@clemson.edu>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Applications to test connection and produce report in text file
- *
- * \author Mark Spencer <markster@digium.com>
- * \author Russell Bryant <russelb@clemson.edu>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <sys/stat.h>
-
-#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
-#include "asterisk/channel.h"
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-#include "asterisk/app.h"
-#include "asterisk/pbx.h"
-#include "asterisk/utils.h"
-
-static char *tests_descrip =
- " TestServer(): Perform test server function and write call report.\n"
- "Results stored in /var/log/asterisk/testreports/<testid>-server.txt";
-static char *tests_app = "TestServer";
-static char *tests_synopsis = "Execute Interface Test Server";
-
-static char *testc_descrip =
- " TestClient(testid): Executes test client with given testid.\n"
- "Results stored in /var/log/asterisk/testreports/<testid>-client.txt";
-
-static char *testc_app = "TestClient";
-static char *testc_synopsis = "Execute Interface Test Client";
-
-static int measurenoise(struct ast_channel *chan, int ms, char *who)
-{
- int res=0;
- int mssofar;
- int noise=0;
- int samples=0;
- int x;
- short *foo;
- struct timeval start;
- struct ast_frame *f;
- int rformat;
- rformat = chan->readformat;
- if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
- ast_log(LOG_NOTICE, "Unable to set to linear mode!\n");
- return -1;
- }
- start = ast_tvnow();
- for(;;) {
- mssofar = ast_tvdiff_ms(ast_tvnow(), start);
- if (mssofar > ms)
- break;
- res = ast_waitfor(chan, ms - mssofar);
- if (res < 1)
- break;
- f = ast_read(chan);
- if (!f) {
- res = -1;
- break;
- }
- if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
- foo = (short *)f->data;
- for (x=0;x<f->samples;x++) {
- noise += abs(foo[x]);
- samples++;
- }
- }
- ast_frfree(f);
- }
-
- if (rformat) {
- if (ast_set_read_format(chan, rformat)) {
- ast_log(LOG_NOTICE, "Unable to restore original format!\n");
- return -1;
- }
- }
- if (res < 0)
- return res;
- if (!samples) {
- ast_log(LOG_NOTICE, "No samples were received from the other side!\n");
- return -1;
- }
- ast_debug(1, "%s: Noise: %d, samples: %d, avg: %d\n", who, noise, samples, noise / samples);
- return (noise / samples);
-}
-
-static int sendnoise(struct ast_channel *chan, int ms)
-{
- int res;
- res = ast_tonepair_start(chan, 1537, 2195, ms, 8192);
- if (!res) {
- res = ast_waitfordigit(chan, ms);
- ast_tonepair_stop(chan);
- }
- return res;
-}
-
-static int testclient_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char *testid=data;
- char fn[80];
- char serverver[80];
- FILE *f;
-
- /* Check for test id */
- if (ast_strlen_zero(testid)) {
- ast_log(LOG_WARNING, "TestClient requires an argument - the test id\n");
- return -1;
- }
-
- if (chan->_state != AST_STATE_UP)
- res = ast_answer(chan);
-
- /* Wait a few just to be sure things get started */
- res = ast_safe_sleep(chan, 3000);
- /* Transmit client version */
- if (!res)
- res = ast_dtmf_stream(chan, NULL, "8378*1#", 0, 0);
- ast_debug(1, "Transmit client version\n");
-
- /* Read server version */
- ast_debug(1, "Read server version\n");
- if (!res)
- res = ast_app_getdata(chan, NULL, serverver, sizeof(serverver) - 1, 0);
- if (res > 0)
- res = 0;
- ast_debug(1, "server version: %s\n", serverver);
-
- if (res > 0)
- res = 0;
-
- if (!res)
- res = ast_safe_sleep(chan, 1000);
- /* Send test id */
- if (!res)
- res = ast_dtmf_stream(chan, NULL, testid, 0, 0);
- if (!res)
- res = ast_dtmf_stream(chan, NULL, "#", 0, 0);
- ast_debug(1, "send test identifier: %s\n", testid);
-
- if ((res >=0) && (!ast_strlen_zero(testid))) {
- /* Make the directory to hold the test results in case it's not there */
- snprintf(fn, sizeof(fn), "%s/testresults", ast_config_AST_LOG_DIR);
- ast_mkdir(fn, 0777);
- snprintf(fn, sizeof(fn), "%s/testresults/%s-client.txt", ast_config_AST_LOG_DIR, testid);
- if ((f = fopen(fn, "w+"))) {
- setlinebuf(f);
- fprintf(f, "CLIENTCHAN: %s\n", chan->name);
- fprintf(f, "CLIENTTEST ID: %s\n", testid);
- fprintf(f, "ANSWER: PASS\n");
- res = 0;
-
- if (!res) {
- /* Step 1: Wait for "1" */
- ast_debug(1, "TestClient: 2. Wait DTMF 1\n");
- res = ast_waitfordigit(chan, 3000);
- fprintf(f, "WAIT DTMF 1: %s\n", (res != '1') ? "FAIL" : "PASS");
- if (res == '1')
- res = 0;
- else
- res = -1;
- }
- if (!res)
- res = ast_safe_sleep(chan, 1000);
- if (!res) {
- /* Step 2: Send "2" */
- ast_debug(1, "TestClient: 2. Send DTMF 2\n");
- res = ast_dtmf_stream(chan, NULL, "2", 0, 0);
- fprintf(f, "SEND DTMF 2: %s\n", (res < 0) ? "FAIL" : "PASS");
- if (res > 0)
- res = 0;
- }
- if (!res) {
- /* Step 3: Wait one second */
- ast_debug(1, "TestClient: 3. Wait one second\n");
- res = ast_safe_sleep(chan, 1000);
- fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
- if (res > 0)
- res = 0;
- }
- if (!res) {
- /* Step 4: Measure noise */
- ast_debug(1, "TestClient: 4. Measure noise\n");
- res = measurenoise(chan, 5000, "TestClient");
- fprintf(f, "MEASURENOISE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
- if (res > 0)
- res = 0;
- }
- if (!res) {
- /* Step 5: Wait for "4" */
- ast_debug(1, "TestClient: 5. Wait DTMF 4\n");
- res = ast_waitfordigit(chan, 3000);
- fprintf(f, "WAIT DTMF 4: %s\n", (res != '4') ? "FAIL" : "PASS");
- if (res == '4')
- res = 0;
- else
- res = -1;
- }
- if (!res) {
- /* Step 6: Transmit tone noise */
- ast_debug(1, "TestClient: 6. Transmit tone\n");
- res = sendnoise(chan, 6000);
- fprintf(f, "SENDTONE: %s\n", (res < 0) ? "FAIL" : "PASS");
- }
- if (!res || (res == '5')) {
- /* Step 7: Wait for "5" */
- ast_debug(1, "TestClient: 7. Wait DTMF 5\n");
- if (!res)
- res = ast_waitfordigit(chan, 3000);
- fprintf(f, "WAIT DTMF 5: %s\n", (res != '5') ? "FAIL" : "PASS");
- if (res == '5')
- res = 0;
- else
- res = -1;
- }
- if (!res) {
- /* Step 8: Wait one second */
- ast_debug(1, "TestClient: 8. Wait one second\n");
- res = ast_safe_sleep(chan, 1000);
- fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
- if (res > 0)
- res = 0;
- }
- if (!res) {
- /* Step 9: Measure noise */
- ast_debug(1, "TestClient: 6. Measure tone\n");
- res = measurenoise(chan, 4000, "TestClient");
- fprintf(f, "MEASURETONE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
- if (res > 0)
- res = 0;
- }
- if (!res) {
- /* Step 10: Send "7" */
- ast_debug(1, "TestClient: 7. Send DTMF 7\n");
- res = ast_dtmf_stream(chan, NULL, "7", 0, 0);
- fprintf(f, "SEND DTMF 7: %s\n", (res < 0) ? "FAIL" : "PASS");
- if (res > 0)
- res =0;
- }
- if (!res) {
- /* Step 11: Wait for "8" */
- ast_debug(1, "TestClient: 11. Wait DTMF 8\n");
- res = ast_waitfordigit(chan, 3000);
- fprintf(f, "WAIT DTMF 8: %s\n", (res != '8') ? "FAIL" : "PASS");
- if (res == '8')
- res = 0;
- else
- res = -1;
- }
- if (!res) {
- /* Step 12: Hangup! */
- ast_debug(1, "TestClient: 12. Hangup\n");
- }
-
- ast_debug(1, "-- TEST COMPLETE--\n");
- fprintf(f, "-- END TEST--\n");
- fclose(f);
- res = -1;
- } else
- res = -1;
- } else {
- ast_log(LOG_NOTICE, "Did not read a test ID on '%s'\n", chan->name);
- res = -1;
- }
- return res;
-}
-
-static int testserver_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char testid[80]="";
- char fn[80];
- FILE *f;
- if (chan->_state != AST_STATE_UP)
- res = ast_answer(chan);
- /* Read version */
- ast_debug(1, "Read client version\n");
- if (!res)
- res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0);
- if (res > 0)
- res = 0;
-
- ast_debug(1, "client version: %s\n", testid);
- ast_debug(1, "Transmit server version\n");
-
- res = ast_safe_sleep(chan, 1000);
- if (!res)
- res = ast_dtmf_stream(chan, NULL, "8378*1#", 0, 0);
- if (res > 0)
- res = 0;
-
- if (!res)
- res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0);
- ast_debug(1, "read test identifier: %s\n", testid);
- /* Check for sneakyness */
- if (strchr(testid, '/'))
- res = -1;
- if ((res >=0) && (!ast_strlen_zero(testid))) {
- /* Got a Test ID! Whoo hoo! */
- /* Make the directory to hold the test results in case it's not there */
- snprintf(fn, sizeof(fn), "%s/testresults", ast_config_AST_LOG_DIR);
- ast_mkdir(fn, 0777);
- snprintf(fn, sizeof(fn), "%s/testresults/%s-server.txt", ast_config_AST_LOG_DIR, testid);
- if ((f = fopen(fn, "w+"))) {
- setlinebuf(f);
- fprintf(f, "SERVERCHAN: %s\n", chan->name);
- fprintf(f, "SERVERTEST ID: %s\n", testid);
- fprintf(f, "ANSWER: PASS\n");
- ast_debug(1, "Processing Test ID '%s'\n", testid);
- res = ast_safe_sleep(chan, 1000);
- if (!res) {
- /* Step 1: Send "1" */
- ast_debug(1, "TestServer: 1. Send DTMF 1\n");
- res = ast_dtmf_stream(chan, NULL, "1", 0,0 );
- fprintf(f, "SEND DTMF 1: %s\n", (res < 0) ? "FAIL" : "PASS");
- if (res > 0)
- res = 0;
- }
- if (!res) {
- /* Step 2: Wait for "2" */
- ast_debug(1, "TestServer: 2. Wait DTMF 2\n");
- res = ast_waitfordigit(chan, 3000);
- fprintf(f, "WAIT DTMF 2: %s\n", (res != '2') ? "FAIL" : "PASS");
- if (res == '2')
- res = 0;
- else
- res = -1;
- }
- if (!res) {
- /* Step 3: Measure noise */
- ast_debug(1, "TestServer: 3. Measure noise\n");
- res = measurenoise(chan, 6000, "TestServer");
- fprintf(f, "MEASURENOISE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
- if (res > 0)
- res = 0;
- }
- if (!res) {
- /* Step 4: Send "4" */
- ast_debug(1, "TestServer: 4. Send DTMF 4\n");
- res = ast_dtmf_stream(chan, NULL, "4", 0, 0);
- fprintf(f, "SEND DTMF 4: %s\n", (res < 0) ? "FAIL" : "PASS");
- if (res > 0)
- res = 0;
- }
-
- if (!res) {
- /* Step 5: Wait one second */
- ast_debug(1, "TestServer: 5. Wait one second\n");
- res = ast_safe_sleep(chan, 1000);
- fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
- if (res > 0)
- res = 0;
- }
-
- if (!res) {
- /* Step 6: Measure noise */
- ast_debug(1, "TestServer: 6. Measure tone\n");
- res = measurenoise(chan, 4000, "TestServer");
- fprintf(f, "MEASURETONE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
- if (res > 0)
- res = 0;
- }
-
- if (!res) {
- /* Step 7: Send "5" */
- ast_debug(1, "TestServer: 7. Send DTMF 5\n");
- res = ast_dtmf_stream(chan, NULL, "5", 0, 0);
- fprintf(f, "SEND DTMF 5: %s\n", (res < 0) ? "FAIL" : "PASS");
- if (res > 0)
- res = 0;
- }
-
- if (!res) {
- /* Step 8: Transmit tone noise */
- ast_debug(1, "TestServer: 8. Transmit tone\n");
- res = sendnoise(chan, 6000);
- fprintf(f, "SENDTONE: %s\n", (res < 0) ? "FAIL" : "PASS");
- }
-
- if (!res || (res == '7')) {
- /* Step 9: Wait for "7" */
- ast_debug(1, "TestServer: 9. Wait DTMF 7\n");
- if (!res)
- res = ast_waitfordigit(chan, 3000);
- fprintf(f, "WAIT DTMF 7: %s\n", (res != '7') ? "FAIL" : "PASS");
- if (res == '7')
- res = 0;
- else
- res = -1;
- }
- if (!res)
- res = ast_safe_sleep(chan, 1000);
- if (!res) {
- /* Step 10: Send "8" */
- ast_debug(1, "TestServer: 10. Send DTMF 8\n");
- res = ast_dtmf_stream(chan, NULL, "8", 0, 0);
- fprintf(f, "SEND DTMF 8: %s\n", (res < 0) ? "FAIL" : "PASS");
- if (res > 0)
- res = 0;
- }
- if (!res) {
- /* Step 11: Wait for hangup to arrive! */
- ast_debug(1, "TestServer: 11. Waiting for hangup\n");
- res = ast_safe_sleep(chan, 10000);
- fprintf(f, "WAIT HANGUP: %s\n", (res < 0) ? "PASS" : "FAIL");
- }
-
- ast_log(LOG_NOTICE, "-- TEST COMPLETE--\n");
- fprintf(f, "-- END TEST--\n");
- fclose(f);
- res = -1;
- } else
- res = -1;
- } else {
- ast_log(LOG_NOTICE, "Did not read a test ID on '%s'\n", chan->name);
- res = -1;
- }
- return res;
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(testc_app);
- res |= ast_unregister_application(tests_app);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
-
- res = ast_register_application(testc_app, testclient_exec, testc_synopsis, testc_descrip);
- res |= ast_register_application(tests_app, testserver_exec, tests_synopsis, tests_descrip);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Interface Test Application");
diff --git a/trunk/apps/app_transfer.c b/trunk/apps/app_transfer.c
deleted file mode 100644
index ee6c2c588..000000000
--- a/trunk/apps/app_transfer.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Transfer a caller
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * Requires transfer support from channel driver
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-#include "asterisk/channel.h"
-
-
-static const char *app = "Transfer";
-
-static const char *synopsis = "Transfer caller to remote extension";
-
-static const char *descrip =
-" Transfer([Tech/]dest[,options]): Requests the remote caller be transferred\n"
-"to a given destination. If TECH (SIP, IAX2, LOCAL etc) is used, only\n"
-"an incoming call with the same channel technology will be transfered.\n"
-"Note that for SIP, if you transfer before call is setup, a 302 redirect\n"
-"SIP message will be returned to the caller.\n"
-"\nThe result of the application will be reported in the TRANSFERSTATUS\n"
-"channel variable:\n"
-" SUCCESS Transfer succeeded\n"
-" FAILURE Transfer failed\n"
-" UNSUPPORTED Transfer unsupported by channel driver\n";
-
-static int transfer_exec(struct ast_channel *chan, void *data)
-{
- int res;
- int len;
- char *slash;
- char *tech = NULL;
- char *dest = NULL;
- char *status;
- char *parse;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(dest);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero((char *)data)) {
- ast_log(LOG_WARNING, "Transfer requires an argument ([Tech/]destination[,options])\n");
- pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
- return 0;
- } else
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (args.options) {
- }
-
- dest = args.dest;
-
- if ((slash = strchr(dest, '/')) && (len = (slash - dest))) {
- tech = dest;
- dest = slash + 1;
- /* Allow execution only if the Tech/destination agrees with the type of the channel */
- if (strncasecmp(chan->tech->type, tech, len)) {
- pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
- return 0;
- }
- }
-
- /* Check if the channel supports transfer before we try it */
- if (!chan->tech->transfer) {
- pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "UNSUPPORTED");
- return 0;
- }
-
- res = ast_transfer(chan, dest);
-
- if (res < 0) {
- status = "FAILURE";
- res = 0;
- } else {
- status = "SUCCESS";
- res = 0;
- }
-
- pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", status);
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, transfer_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Transfers a caller to another extension");
diff --git a/trunk/apps/app_url.c b/trunk/apps/app_url.c
deleted file mode 100644
index f71b32fd0..000000000
--- a/trunk/apps/app_url.c
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief App to transmit a URL
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-#include "asterisk/channel.h"
-
-static char *app = "SendURL";
-
-static char *synopsis = "Send a URL";
-
-static char *descrip =
-" SendURL(URL[,option]): Requests client go to URL (IAX2) or sends the \n"
-"URL to the client (other channels).\n"
-"Result is returned in the SENDURLSTATUS channel variable:\n"
-" SUCCESS URL successfully sent to client\n"
-" FAILURE Failed to send URL\n"
-" NOLOAD Client failed to load URL (wait enabled)\n"
-" UNSUPPORTED Channel does not support URL transport\n"
-"\n"
-"If the option 'w' is specified, execution will wait for an\n"
-"acknowledgement that the URL has been loaded before continuing\n"
-"\n"
-"SendURL continues normally if the URL was sent correctly or if the channel\n"
-"does not support HTML transport. Otherwise, the channel is hung up.\n";
-
-enum {
- OPTION_WAIT = (1 << 0),
-} option_flags;
-
-AST_APP_OPTIONS(app_opts,{
- AST_APP_OPTION('w', OPTION_WAIT),
-});
-
-static int sendurl_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char *tmp;
- struct ast_frame *f;
- char *status = "FAILURE";
- char *opts[0];
- struct ast_flags flags;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(url);
- AST_APP_ARG(options);
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "SendURL requires an argument (URL)\n");
- pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", status);
- return -1;
- }
-
- tmp = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, tmp);
- if (args.argc == 2)
- ast_app_parse_options(app_opts, &flags, opts, args.options);
-
- if (!ast_channel_supports_html(chan)) {
- /* Does not support transport */
- pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", "UNSUPPORTED");
- return 0;
- }
- res = ast_channel_sendurl(chan, args.url);
- if (res == -1) {
- pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", "FAILURE");
- return res;
- }
- status = "SUCCESS";
- if (ast_test_flag(&flags, OPTION_WAIT)) {
- for(;;) {
- /* Wait for an event */
- res = ast_waitfor(chan, -1);
- if (res < 0)
- break;
- f = ast_read(chan);
- if (!f) {
- res = -1;
- status = "FAILURE";
- break;
- }
- if (f->frametype == AST_FRAME_HTML) {
- switch(f->subclass) {
- case AST_HTML_LDCOMPLETE:
- res = 0;
- ast_frfree(f);
- status = "NOLOAD";
- goto out;
- break;
- case AST_HTML_NOSUPPORT:
- /* Does not support transport */
- status = "UNSUPPORTED";
- res = 0;
- ast_frfree(f);
- goto out;
- break;
- default:
- ast_log(LOG_WARNING, "Don't know what to do with HTML subclass %d\n", f->subclass);
- };
- }
- ast_frfree(f);
- }
- }
-out:
- pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", status);
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, sendurl_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send URL Applications");
diff --git a/trunk/apps/app_userevent.c b/trunk/apps/app_userevent.c
deleted file mode 100644
index 91d067d54..000000000
--- a/trunk/apps/app_userevent.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief UserEvent application -- send manager event
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/manager.h"
-#include "asterisk/app.h"
-
-static char *app = "UserEvent";
-
-static char *synopsis = "Send an arbitrary event to the manager interface";
-
-static char *descrip =
-" UserEvent(eventname[,body]): Sends an arbitrary event to the manager\n"
-"interface, with an optional body representing additional arguments. The\n"
-"body may be specified as a | delimeted list of headers. Each additional\n"
-"argument will be placed on a new line in the event. The format of the\n"
-"event will be:\n"
-" Event: UserEvent\n"
-" UserEvent: <specified event name>\n"
-" [body]\n"
-"If no body is specified, only Event and UserEvent headers will be present.\n";
-
-
-static int userevent_exec(struct ast_channel *chan, void *data)
-{
- char *parse, buf[2048] = "";
- int x, buflen = 0;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(eventname);
- AST_APP_ARG(extra)[100];
- );
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "UserEvent requires an argument (eventname,optional event body)\n");
- return -1;
- }
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- for (x = 0; x < args.argc - 1; x++) {
- ast_copy_string(buf + buflen, args.extra[x], sizeof(buf) - buflen - 2);
- buflen += strlen(args.extra[x]);
- ast_copy_string(buf + buflen, "\r\n", 3);
- buflen += 2;
- }
-
- manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", args.eventname, buf);
-
- return 0;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, userevent_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Custom User Event Application");
diff --git a/trunk/apps/app_verbose.c b/trunk/apps/app_verbose.c
deleted file mode 100644
index 525cc1c55..000000000
--- a/trunk/apps/app_verbose.c
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (c) 2004 - 2005 Tilghman Lesher. All rights reserved.
- *
- * Tilghman Lesher <app_verbose_v001@the-tilghman.com>
- *
- * This code is released by the author with no restrictions on usage.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- */
-
-/*! \file
- *
- * \brief Verbose logging application
- *
- * \author Tilghman Lesher <app_verbose_v001@the-tilghman.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/app.h"
-#include "asterisk/channel.h"
-
-static char *app_verbose = "Verbose";
-static char *verbose_synopsis = "Send arbitrary text to verbose output";
-static char *verbose_descrip =
-"Verbose([<level>,]<message>)\n"
-" level must be an integer value. If not specified, defaults to 0.\n";
-
-static char *app_log = "Log";
-static char *log_synopsis = "Send arbitrary text to a selected log level";
-static char *log_descrip =
-"Log(<level>,<message>)\n"
-" level must be one of ERROR, WARNING, NOTICE, DEBUG, VERBOSE, DTMF\n";
-
-
-static int verbose_exec(struct ast_channel *chan, void *data)
-{
- int vsize;
- char *parse;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(level);
- AST_APP_ARG(msg);
- );
-
- if (ast_strlen_zero(data)) {
- return 0;
- }
-
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
- if (args.argc == 1) {
- args.msg = args.level;
- args.level = "0";
- }
-
- if (sscanf(args.level, "%d", &vsize) != 1) {
- vsize = 0;
- ast_log(LOG_WARNING, "'%s' is not a verboser number\n", args.level);
- }
- if (option_verbose >= vsize) {
- switch (vsize) {
- case 0:
- ast_verbose("%s\n", args.msg);
- break;
- case 1:
- ast_verbose(VERBOSE_PREFIX_1 "%s\n", args.msg);
- break;
- case 2:
- ast_verbose(VERBOSE_PREFIX_2 "%s\n", args.msg);
- break;
- case 3:
- ast_verbose(VERBOSE_PREFIX_3 "%s\n", args.msg);
- break;
- default:
- ast_verbose(VERBOSE_PREFIX_4 "%s\n", args.msg);
- }
- }
-
- return 0;
-}
-
-static int log_exec(struct ast_channel *chan, void *data)
-{
- char *parse;
- int lnum = -1;
- char extension[AST_MAX_EXTENSION + 5], context[AST_MAX_EXTENSION + 2];
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(level);
- AST_APP_ARG(msg);
- );
-
- if (ast_strlen_zero(data))
- return 0;
-
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (!strcasecmp(args.level, "ERROR")) {
- lnum = __LOG_ERROR;
- } else if (!strcasecmp(args.level, "WARNING")) {
- lnum = __LOG_WARNING;
- } else if (!strcasecmp(args.level, "NOTICE")) {
- lnum = __LOG_NOTICE;
- } else if (!strcasecmp(args.level, "DEBUG")) {
- lnum = __LOG_DEBUG;
- } else if (!strcasecmp(args.level, "VERBOSE")) {
- lnum = __LOG_VERBOSE;
- } else if (!strcasecmp(args.level, "DTMF")) {
- lnum = __LOG_DTMF;
- } else if (!strcasecmp(args.level, "EVENT")) {
- lnum = __LOG_EVENT;
- } else {
- ast_log(LOG_ERROR, "Unknown log level: '%s'\n", args.level);
- }
-
- if (lnum > -1) {
- snprintf(context, sizeof(context), "@ %s", chan->context);
- snprintf(extension, sizeof(extension), "Ext. %s", chan->exten);
-
- ast_log(lnum, extension, chan->priority, context, "%s\n", args.msg);
- }
-
- return 0;
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(app_verbose);
- res |= ast_unregister_application(app_log);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
-
- res = ast_register_application(app_log, log_exec, log_synopsis, log_descrip);
- res |= ast_register_application(app_verbose, verbose_exec, verbose_synopsis, verbose_descrip);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send verbose output");
diff --git a/trunk/apps/app_voicemail.c b/trunk/apps/app_voicemail.c
deleted file mode 100644
index ad1f0a23a..000000000
--- a/trunk/apps/app_voicemail.c
+++ /dev/null
@@ -1,9840 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2006, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Comedian Mail - Voicemail System
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \extref Unixodbc - http://www.unixodbc.org
- * \extref A source distribution of University of Washington's IMAP
-c-client (http://www.washington.edu/imap/
- *
- * \par See also
- * \arg \ref Config_vm
- * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
- * \ingroup applications
- * \note This module requires res_adsi to load. This needs to be optional
- * during compilation.
- *
- *
- *
- * \note This file is now almost impossible to work with, due to all \#ifdefs.
- * Feels like the database code before realtime. Someone - please come up
- * with a plan to clean this up.
- */
-
-/*** MODULEINFO
- <depend>res_smdi</depend>
- ***/
-
-/*** MAKEOPTS
-<category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_directory.o">
- <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
- <depend>unixodbc</depend>
- <depend>ltdl</depend>
- <conflict>IMAP_STORAGE</conflict>
- <defaultenabled>no</defaultenabled>
- </member>
- <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
- <depend>imap_tk</depend>
- <conflict>ODBC_STORAGE</conflict>
- <use>ssl</use>
- <defaultenabled>no</defaultenabled>
- </member>
-</category>
- ***/
-
-/*It is important to include the IMAP_STORAGE related headers
- * before asterisk.h since asterisk.h includes logger.h. logger.h
- * and c-client.h have conflicting definitions for LOG_WARNING and
- * LOG_DEBUG, so it's important that we use Asterisk's definitions
- * here instead of the c-client's
- */
-#ifdef IMAP_STORAGE
-#include <ctype.h>
-#include <signal.h>
-#include <pwd.h>
-#ifdef USE_SYSTEM_IMAP
-#include <imap/c-client.h>
-#include <imap/imap4r1.h>
-#include <imap/linkage.h>
-#else
-#include "c-client.h"
-#include "imap4r1.h"
-#include "linkage.h"
-#endif
-#endif
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <time.h>
-#include <dirent.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/config.h"
-#include "asterisk/say.h"
-#include "asterisk/module.h"
-#include "asterisk/adsi.h"
-#include "asterisk/app.h"
-#include "asterisk/manager.h"
-#include "asterisk/dsp.h"
-#include "asterisk/localtime.h"
-#include "asterisk/cli.h"
-#include "asterisk/utils.h"
-#include "asterisk/stringfields.h"
-#include "asterisk/smdi.h"
-#include "asterisk/event.h"
-
-#ifdef ODBC_STORAGE
-#include "asterisk/res_odbc.h"
-#endif
-
-#ifdef IMAP_STORAGE
-static char imapserver[48];
-static char imapport[8];
-static char imapflags[128];
-static char imapfolder[64];
-static char greetingfolder[64];
-static char authuser[32];
-static char authpassword[42];
-
-static int expungeonhangup = 1;
-static int imapgreetings = 0;
-static char delimiter = '\0';
-
-struct vm_state;
-struct ast_vm_user;
-
-/* Forward declarations for IMAP */
-static int init_mailstream(struct vm_state *vms, int box);
-static void write_file(char *filename, char *buffer, unsigned long len);
-static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
-static void vm_imap_delete(int msgnum, struct vm_state *vms);
-static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
-static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
-static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
-static void vmstate_insert(struct vm_state *vms);
-static void vmstate_delete(struct vm_state *vms);
-static void set_update(MAILSTREAM * stream);
-static void init_vm_state(struct vm_state *vms);
-static void check_msgArray(struct vm_state *vms);
-static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
-static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
-static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num);
-static void get_mailbox_delimiter(MAILSTREAM *stream);
-static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
-static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
-static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms);
-static void update_messages_by_imapuser(const char *user, unsigned long number);
-
-static int imap_remove_file (char *dir, int msgnum);
-static int imap_retrieve_file (char *dir, int msgnum, const char *mailbox, char *context);
-static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
-static void check_quota(struct vm_state *vms, char *mailbox);
-static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
-struct vmstate {
- struct vm_state *vms;
- AST_LIST_ENTRY(vmstate) list;
-};
-
-static AST_LIST_HEAD_STATIC(vmstates, vmstate);
-
-#endif
-
-#define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
-
-#define COMMAND_TIMEOUT 5000
-/* Don't modify these here; set your umask at runtime instead */
-#define VOICEMAIL_DIR_MODE 0777
-#define VOICEMAIL_FILE_MODE 0666
-#define CHUNKSIZE 65536
-
-#define VOICEMAIL_CONFIG "voicemail.conf"
-#define ASTERISK_USERNAME "asterisk"
-
-/* Define fast-forward, pause, restart, and reverse keys
- while listening to a voicemail message - these are
- strings, not characters */
-#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
-#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
-#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
-#define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
-#define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
-#define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
-
-/* Default mail command to mail voicemail. Change it with the
- mailcmd= command in voicemail.conf */
-#define SENDMAIL "/usr/sbin/sendmail -t"
-
-#define INTRO "vm-intro"
-
-#define MAXMSG 100
-#define MAXMSGLIMIT 9999
-
-#define BASELINELEN 72
-#define BASEMAXINLINE 256
-#define eol "\r\n"
-
-#define MAX_DATETIME_FORMAT 512
-#define MAX_NUM_CID_CONTEXTS 10
-
-#define VM_REVIEW (1 << 0)
-#define VM_OPERATOR (1 << 1)
-#define VM_SAYCID (1 << 2)
-#define VM_SVMAIL (1 << 3)
-#define VM_ENVELOPE (1 << 4)
-#define VM_SAYDURATION (1 << 5)
-#define VM_SKIPAFTERCMD (1 << 6)
-#define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
-#define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
-#define VM_PBXSKIP (1 << 9)
-#define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
-#define VM_ATTACH (1 << 11)
-#define VM_DELETE (1 << 12)
-#define VM_ALLOCED (1 << 13)
-#define VM_SEARCH (1 << 14)
-#define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
-#define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
-#define ERROR_LOCK_PATH -100
-
-
-enum {
- NEW_FOLDER,
- OLD_FOLDER,
- WORK_FOLDER,
- FAMILY_FOLDER,
- FRIENDS_FOLDER,
- GREETINGS_FOLDER
-} vm_box;
-
-enum {
- OPT_SILENT = (1 << 0),
- OPT_BUSY_GREETING = (1 << 1),
- OPT_UNAVAIL_GREETING = (1 << 2),
- OPT_RECORDGAIN = (1 << 3),
- OPT_PREPEND_MAILBOX = (1 << 4),
- OPT_AUTOPLAY = (1 << 6),
- OPT_DTMFEXIT = (1 << 7),
-} vm_option_flags;
-
-enum {
- OPT_ARG_RECORDGAIN = 0,
- OPT_ARG_PLAYFOLDER = 1,
- OPT_ARG_DTMFEXIT = 2,
- /* This *must* be the last value in this enum! */
- OPT_ARG_ARRAY_SIZE = 3,
-} vm_option_args;
-
-AST_APP_OPTIONS(vm_app_options, {
- AST_APP_OPTION('s', OPT_SILENT),
- AST_APP_OPTION('b', OPT_BUSY_GREETING),
- AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
- AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
- AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
- AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
- AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
-});
-
-static int load_config(int reload);
-
-/*! \page vmlang Voicemail Language Syntaxes Supported
-
- \par Syntaxes supported, not really language codes.
- \arg \b en - English
- \arg \b de - German
- \arg \b es - Spanish
- \arg \b fr - French
- \arg \b it - Italian
- \arg \b nl - Dutch
- \arg \b pt - Portuguese
- \arg \b pt_BR - Portuguese (Brazil)
- \arg \b gr - Greek
- \arg \b no - Norwegian
- \arg \b se - Swedish
- \arg \b tw - Chinese (Taiwan)
- \arg \b ua - Ukrainian
-
-German requires the following additional soundfile:
-\arg \b 1F einE (feminine)
-
-Spanish requires the following additional soundfile:
-\arg \b 1M un (masculine)
-
-Dutch, Portuguese & Spanish require the following additional soundfiles:
-\arg \b vm-INBOXs singular of 'new'
-\arg \b vm-Olds singular of 'old/heard/read'
-
-NB these are plural:
-\arg \b vm-INBOX nieuwe (nl)
-\arg \b vm-Old oude (nl)
-
-Polish uses:
-\arg \b vm-new-a 'new', feminine singular accusative
-\arg \b vm-new-e 'new', feminine plural accusative
-\arg \b vm-new-ych 'new', feminine plural genitive
-\arg \b vm-old-a 'old', feminine singular accusative
-\arg \b vm-old-e 'old', feminine plural accusative
-\arg \b vm-old-ych 'old', feminine plural genitive
-\arg \b digits/1-a 'one', not always same as 'digits/1'
-\arg \b digits/2-ie 'two', not always same as 'digits/2'
-
-Swedish uses:
-\arg \b vm-nytt singular of 'new'
-\arg \b vm-nya plural of 'new'
-\arg \b vm-gammalt singular of 'old'
-\arg \b vm-gamla plural of 'old'
-\arg \b digits/ett 'one', not always same as 'digits/1'
-
-Norwegian uses:
-\arg \b vm-ny singular of 'new'
-\arg \b vm-nye plural of 'new'
-\arg \b vm-gammel singular of 'old'
-\arg \b vm-gamle plural of 'old'
-
-Dutch also uses:
-\arg \b nl-om 'at'?
-
-Spanish also uses:
-\arg \b vm-youhaveno
-
-Ukrainian requires the following additional soundfile:
-\arg \b vm-nove 'nove'
-\arg \b vm-stare 'stare'
-\arg \b digits/ua/1e 'odne'
-
-Italian requires the following additional soundfile:
-
-For vm_intro_it:
-\arg \b vm-nuovo new
-\arg \b vm-nuovi new plural
-\arg \b vm-vecchio old
-\arg \b vm-vecchi old plural
-
-Chinese (Taiwan) requires the following additional soundfile:
-\arg \b vm-tong A class-word for call (tong1)
-\arg \b vm-ri A class-word for day (ri4)
-\arg \b vm-you You (ni3)
-\arg \b vm-haveno Have no (mei2 you3)
-\arg \b vm-have Have (you3)
-\arg \b vm-listen To listen (yao4 ting1)
-
-
-\note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
-spelled among others when you have to change folder. For the above reasons, vm-INBOX
-and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
-
-*/
-
-struct baseio {
- int iocp;
- int iolen;
- int linelength;
- int ateof;
- unsigned char iobuf[BASEMAXINLINE];
-};
-
-/*! Structure for linked list of users
- * Use ast_vm_user_destroy() to free one of these structures. */
-struct ast_vm_user {
- char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
- char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
- char password[80]; /*!< Secret pin code, numbers only */
- char fullname[80]; /*!< Full name, for directory app */
- char email[80]; /*!< E-mail address */
- char pager[80]; /*!< E-mail address to pager (no attachment) */
- char serveremail[80]; /*!< From: Mail address */
- char mailcmd[160]; /*!< Configurable mail command */
- char language[MAX_LANGUAGE]; /*!< Config: Language setting */
- char zonetag[80]; /*!< Time zone */
- char callback[80];
- char dialout[80];
- char uniqueid[20]; /*!< Unique integer identifier */
- char exit[80];
- char attachfmt[20]; /*!< Attachment format */
- unsigned int flags; /*!< VM_ flags */
- int saydurationm;
- int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
- int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
- int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
-#ifdef IMAP_STORAGE
- char imapuser[80]; /*!< IMAP server login */
- char imappassword[80]; /*!< IMAP server password if authpassword not defined */
-#endif
- double volgain; /*!< Volume gain for voicemails sent via email */
- AST_LIST_ENTRY(ast_vm_user) list;
-};
-
-/*! Voicemail time zones */
-struct vm_zone {
- AST_LIST_ENTRY(vm_zone) list;
- char name[80];
- char timezone[80];
- char msg_format[512];
-};
-
-/*! Voicemail mailbox state */
-struct vm_state {
- char curbox[80];
- char username[80];
- char curdir[PATH_MAX];
- char vmbox[PATH_MAX];
- char fn[PATH_MAX];
- char fn2[PATH_MAX];
- int *deleted;
- int *heard;
- int curmsg;
- int lastmsg;
- int newmessages;
- int oldmessages;
- int starting;
- int repeats;
-#ifdef IMAP_STORAGE
- int updated; /*!< decremented on each mail check until 1 -allows delay */
- long msgArray[256];
- MAILSTREAM *mailstream;
- int vmArrayIndex;
- char imapuser[80]; /*!< IMAP server login */
- int interactive;
- unsigned int quota_limit;
- unsigned int quota_usage;
- struct vm_state *persist_vms;
-#endif
-};
-
-
-#ifdef ODBC_STORAGE
-static char odbc_database[80];
-static char odbc_table[80];
-#define RETRIEVE(a,b,c,d) retrieve_file(a,b)
-#define DISPOSE(a,b) remove_file(a,b)
-#define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
-#define EXISTS(a,b,c,d) (message_exists(a,b))
-#define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
-#define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
-#define DELETE(a,b,c) (delete_file(a,b))
-#else
-#ifdef IMAP_STORAGE
-#define RETRIEVE(a,b,c,d) (imap_retrieve_file(a,b,c,d ))
-#define DISPOSE(a,b) (imap_remove_file(a,b))
-#define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
-#define EXISTS(a,b,c,d) (ast_fileexists(c, NULL, d) > 0)
-#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
-#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
-#define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
-#define DELETE(a,b,c) (vm_delete(c))
-#else
-#define RETRIEVE(a,b,c,d)
-#define DISPOSE(a,b)
-#define STORE(a,b,c,d,e,f,g,h,i)
-#define EXISTS(a,b,c,d) (ast_fileexists(c, NULL, d) > 0)
-#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
-#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
-#define DELETE(a,b,c) (vm_delete(c))
-#endif
-#endif
-
-static char VM_SPOOL_DIR[PATH_MAX];
-
-static char ext_pass_cmd[128];
-
-int my_umask;
-
-#define PWDCHANGE_INTERNAL (1 << 1)
-#define PWDCHANGE_EXTERNAL (1 << 2)
-static int pwdchange = PWDCHANGE_INTERNAL;
-
-#ifdef ODBC_STORAGE
-#define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
-#else
-# ifdef IMAP_STORAGE
-# define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
-# else
-# define tdesc "Comedian Mail (Voicemail System)"
-# endif
-#endif
-
-static char userscontext[AST_MAX_EXTENSION] = "default";
-
-static char *addesc = "Comedian Mail";
-
-static char *synopsis_vm = "Leave a Voicemail message";
-
-static char *descrip_vm =
- " VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
- "application allows the calling party to leave a message for the specified\n"
- "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
- "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
- "specified mailbox does not exist.\n"
- " The Voicemail application will exit if any of the following DTMF digits are\n"
- "received:\n"
- " 0 - Jump to the 'o' extension in the current dialplan context.\n"
- " * - Jump to the 'a' extension in the current dialplan context.\n"
- " This application will set the following channel variable upon completion:\n"
- " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
- " application. The possible values are:\n"
- " SUCCESS | USEREXIT | FAILED\n\n"
- " Options:\n"
- " b - Play the 'busy' greeting to the calling party.\n"
- " d([c]) - Accept digits for a new extension in context c, if played during\n"
- " the greeting. Context defaults to the current context.\n"
- " g(#) - Use the specified amount of gain when recording the voicemail\n"
- " message. The units are whole-number decibels (dB).\n"
- " s - Skip the playback of instructions for leaving a message to the\n"
- " calling party.\n"
- " u - Play the 'unavailable' greeting.\n";
-
-static char *synopsis_vmain = "Check Voicemail messages";
-
-static char *descrip_vmain =
- " VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
- "calling party to check voicemail messages. A specific mailbox, and optional\n"
- "corresponding context, may be specified. If a mailbox is not provided, the\n"
- "calling party will be prompted to enter one. If a context is not specified,\n"
- "the 'default' context will be used.\n\n"
- " Options:\n"
- " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
- " is entered by the caller.\n"
- " g(#) - Use the specified amount of gain when recording a voicemail\n"
- " message. The units are whole-number decibels (dB).\n"
- " s - Skip checking the passcode for the mailbox.\n"
- " a(#) - Skip folder prompt and go directly to folder specified.\n"
- " Defaults to INBOX\n";
-
-static char *synopsis_vm_box_exists =
-"Check to see if Voicemail mailbox exists";
-
-static char *descrip_vm_box_exists =
- " MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
- "mailbox exists. If no voicemail context is specified, the 'default' context\n"
- "will be used.\n"
- " This application will set the following channel variable upon completion:\n"
- " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
- " MailboxExists application. Possible values include:\n"
- " SUCCESS | FAILED\n\n"
- " Options: (none)\n";
-
-static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
-
-static char *descrip_vmauthenticate =
- " VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
- "same way as the Authenticate application, but the passwords are taken from\n"
- "voicemail.conf.\n"
- " If the mailbox is specified, only that mailbox's password will be considered\n"
- "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
- "be set with the authenticated mailbox.\n\n"
- " Options:\n"
- " s - Skip playing the initial prompts.\n";
-
-/* Leave a message */
-static char *app = "VoiceMail";
-
-/* Check mail, control, etc */
-static char *app2 = "VoiceMailMain";
-
-static char *app3 = "MailboxExists";
-static char *app4 = "VMAuthenticate";
-
-static AST_LIST_HEAD_STATIC(users, ast_vm_user);
-static AST_LIST_HEAD_STATIC(zones, vm_zone);
-static int maxsilence;
-static int maxmsg;
-static int maxdeletedmsg;
-static int silencethreshold = 128;
-static char serveremail[80];
-static char mailcmd[160]; /* Configurable mail cmd */
-static char externnotify[160];
-static struct ast_smdi_interface *smdi_iface = NULL;
-static char vmfmts[80];
-static double volgain;
-static int vmminsecs;
-static int vmmaxsecs;
-static int maxgreet;
-static int skipms;
-static int maxlogins;
-
-/*! Poll mailboxes for changes since there is something external to
- * app_voicemail that may change them. */
-static unsigned int poll_mailboxes;
-
-/*! Polling frequency */
-static unsigned int poll_freq;
-/*! By default, poll every 30 seconds */
-#define DEFAULT_POLL_FREQ 30
-
-AST_MUTEX_DEFINE_STATIC(poll_lock);
-static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
-static pthread_t poll_thread = AST_PTHREADT_NULL;
-static unsigned char poll_thread_run;
-
-/*! Subscription to ... MWI event subscriptions */
-static struct ast_event_sub *mwi_sub_sub;
-/*! Subscription to ... MWI event un-subscriptions */
-static struct ast_event_sub *mwi_unsub_sub;
-
-/*!
- * \brief An MWI subscription
- *
- * This is so we can keep track of which mailboxes are subscribed to.
- * This way, we know which mailboxes to poll when the pollmailboxes
- * option is being used.
- */
-struct mwi_sub {
- AST_RWLIST_ENTRY(mwi_sub) entry;
- int old_new;
- int old_old;
- uint32_t uniqueid;
- char mailbox[1];
-};
-
-static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
-
-/* custom audio control prompts for voicemail playback */
-static char listen_control_forward_key[12];
-static char listen_control_reverse_key[12];
-static char listen_control_pause_key[12];
-static char listen_control_restart_key[12];
-static char listen_control_stop_key[12];
-
-/* custom password sounds */
-static char vm_password[80] = "vm-password";
-static char vm_newpassword[80] = "vm-newpassword";
-static char vm_passchanged[80] = "vm-passchanged";
-static char vm_reenterpassword[80] = "vm-reenterpassword";
-static char vm_mismatch[80] = "vm-mismatch";
-
-static struct ast_flags globalflags = {0};
-
-static int saydurationminfo;
-
-static char dialcontext[AST_MAX_CONTEXT] = "";
-static char callcontext[AST_MAX_CONTEXT] = "";
-static char exitcontext[AST_MAX_CONTEXT] = "";
-
-static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
-
-
-static char *emailbody = NULL;
-static char *emailsubject = NULL;
-static char *pagerbody = NULL;
-static char *pagersubject = NULL;
-static char fromstring[100];
-static char pagerfromstring[100];
-static char emailtitle[100];
-static char charset[32] = "ISO-8859-1";
-
-static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
-static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
-static int adsiver = 1;
-static char emaildateformat[32] = "%A, %B %d, %Y at %r";
-
-/* Forward declarations - generic */
-static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
-static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
-static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
-static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
- char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
- signed char record_gain, struct vm_state *vms);
-static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
-static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
-static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
-static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
-static void apply_options(struct ast_vm_user *vmu, const char *options);
-static int is_valid_dtmf(const char *key);
-
-#if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
-static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
-#endif
-
-
-
-static void populate_defaults(struct ast_vm_user *vmu)
-{
- ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
- if (saydurationminfo)
- vmu->saydurationm = saydurationminfo;
- ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
- ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
- ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
- if (vmmaxsecs)
- vmu->maxsecs = vmmaxsecs;
- if (maxmsg)
- vmu->maxmsg = maxmsg;
- if (maxdeletedmsg)
- vmu->maxdeletedmsg = maxdeletedmsg;
- vmu->volgain = volgain;
-}
-
-static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
-{
- int x;
- if (!strcasecmp(var, "attach")) {
- ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
- } else if (!strcasecmp(var, "attachfmt")) {
- ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
- } else if (!strcasecmp(var, "serveremail")) {
- ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
- } else if (!strcasecmp(var, "language")) {
- ast_copy_string(vmu->language, value, sizeof(vmu->language));
- } else if (!strcasecmp(var, "tz")) {
- ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
-#ifdef IMAP_STORAGE
- } else if (!strcasecmp(var, "imapuser")) {
- ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
- } else if (!strcasecmp(var, "imappassword")) {
- ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
-#endif
- } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
- ast_set2_flag(vmu, ast_true(value), VM_DELETE);
- } else if (!strcasecmp(var, "saycid")) {
- ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
- } else if (!strcasecmp(var, "sendvoicemail")) {
- ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
- } else if (!strcasecmp(var, "review")) {
- ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
- } else if (!strcasecmp(var, "tempgreetwarn")) {
- ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
- } else if (!strcasecmp(var, "operator")) {
- ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
- } else if (!strcasecmp(var, "envelope")) {
- ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
- } else if (!strcasecmp(var, "moveheard")) {
- ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
- } else if (!strcasecmp(var, "sayduration")) {
- ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
- } else if (!strcasecmp(var, "saydurationm")) {
- if (sscanf(value, "%d", &x) == 1) {
- vmu->saydurationm = x;
- } else {
- ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
- }
- } else if (!strcasecmp(var, "forcename")) {
- ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
- } else if (!strcasecmp(var, "forcegreetings")) {
- ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
- } else if (!strcasecmp(var, "callback")) {
- ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
- } else if (!strcasecmp(var, "dialout")) {
- ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
- } else if (!strcasecmp(var, "exitcontext")) {
- ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
- } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
- if (vmu->maxsecs <= 0) {
- ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
- vmu->maxsecs = vmmaxsecs;
- } else {
- vmu->maxsecs = atoi(value);
- }
- if (!strcasecmp(var, "maxmessage"))
- ast_log(LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
- } else if (!strcasecmp(var, "maxmsg")) {
- vmu->maxmsg = atoi(value);
- if (vmu->maxmsg <= 0) {
- ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
- vmu->maxmsg = MAXMSG;
- } else if (vmu->maxmsg > MAXMSGLIMIT) {
- ast_log(LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
- vmu->maxmsg = MAXMSGLIMIT;
- }
- } else if (!strcasecmp(var, "backupdeleted")) {
- if (sscanf(value, "%d", &x) == 1)
- vmu->maxdeletedmsg = x;
- else if (ast_true(value))
- vmu->maxdeletedmsg = MAXMSG;
- else
- vmu->maxdeletedmsg = 0;
-
- if (vmu->maxdeletedmsg < 0) {
- ast_log(LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
- vmu->maxdeletedmsg = MAXMSG;
- } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
- ast_log(LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
- vmu->maxdeletedmsg = MAXMSGLIMIT;
- }
- } else if (!strcasecmp(var, "volgain")) {
- sscanf(value, "%lf", &vmu->volgain);
- } else if (!strcasecmp(var, "options")) {
- apply_options(vmu, value);
- }
-}
-
-static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
-{
- int res;
- if (!ast_strlen_zero(vmu->uniqueid)) {
- res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
- if (res > 0) {
- ast_copy_string(vmu->password, password, sizeof(vmu->password));
- res = 0;
- } else if (!res) {
- res = -1;
- }
- return res;
- }
- return -1;
-}
-
-static void apply_options(struct ast_vm_user *vmu, const char *options)
-{ /* Destructively Parse options and apply */
- char *stringp;
- char *s;
- char *var, *value;
- stringp = ast_strdupa(options);
- while ((s = strsep(&stringp, "|"))) {
- value = s;
- if ((var = strsep(&value, "=")) && value) {
- apply_option(vmu, var, value);
- }
- }
-}
-
-static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
-{
- struct ast_variable *tmp;
- tmp = var;
- while (tmp) {
- if (!strcasecmp(tmp->name, "vmsecret")) {
- ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
- } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
- if (ast_strlen_zero(retval->password))
- ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
- } else if (!strcasecmp(tmp->name, "uniqueid")) {
- ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
- } else if (!strcasecmp(tmp->name, "pager")) {
- ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
- } else if (!strcasecmp(tmp->name, "email")) {
- ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
- } else if (!strcasecmp(tmp->name, "fullname")) {
- ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
- } else if (!strcasecmp(tmp->name, "context")) {
- ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
-#ifdef IMAP_STORAGE
- } else if (!strcasecmp(tmp->name, "imapuser")) {
- ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
- } else if (!strcasecmp(tmp->name, "imappassword")) {
- ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
-#endif
- } else
- apply_option(retval, tmp->name, tmp->value);
- tmp = tmp->next;
- }
-}
-
-static int is_valid_dtmf(const char *key)
-{
- int i;
- char *local_key = ast_strdupa(key);
-
- for (i = 0; i < strlen(key); ++i) {
- if (!strchr(VALID_DTMF, *local_key)) {
- ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
- return 0;
- }
- local_key++;
- }
- return 1;
-}
-
-static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
-{
- struct ast_variable *var;
- struct ast_vm_user *retval;
-
- if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
- if (!ivm)
- ast_set_flag(retval, VM_ALLOCED);
- else
- memset(retval, 0, sizeof(*retval));
- if (mailbox)
- ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
- populate_defaults(retval);
- if (!context && ast_test_flag((&globalflags), VM_SEARCH))
- var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
- else
- var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
- if (var) {
- apply_options_full(retval, var);
- ast_variables_destroy(var);
- } else {
- if (!ivm)
- ast_free(retval);
- retval = NULL;
- }
- }
- return retval;
-}
-
-static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
-{
- /* This function could be made to generate one from a database, too */
- struct ast_vm_user *vmu = NULL, *cur;
- AST_LIST_LOCK(&users);
-
- if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
- context = "default";
-
- AST_LIST_TRAVERSE(&users, cur, list) {
- if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
- break;
- if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
- break;
- }
- if (cur) {
- /* Make a copy, so that on a reload, we have no race */
- if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
- memcpy(vmu, cur, sizeof(*vmu));
- ast_set2_flag(vmu, !ivm, VM_ALLOCED);
- AST_LIST_NEXT(vmu, list) = NULL;
- }
- } else
- vmu = find_user_realtime(ivm, context, mailbox);
- AST_LIST_UNLOCK(&users);
- return vmu;
-}
-
-static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
-{
- /* This function could be made to generate one from a database, too */
- struct ast_vm_user *cur;
- int res = -1;
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, cur, list) {
- if ((!context || !strcasecmp(context, cur->context)) &&
- (!strcasecmp(mailbox, cur->mailbox)))
- break;
- }
- if (cur) {
- ast_copy_string(cur->password, newpass, sizeof(cur->password));
- res = 0;
- }
- AST_LIST_UNLOCK(&users);
- return res;
-}
-
-static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
-{
- struct ast_config *cfg = NULL;
- struct ast_variable *var = NULL;
- struct ast_category *cat = NULL;
- char *category = NULL, *value = NULL, *new = NULL;
- const char *tmp = NULL;
- struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
- if (!change_password_realtime(vmu, newpassword))
- return;
-
- /* check voicemail.conf */
- if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
- while ((category = ast_category_browse(cfg, category))) {
- if (!strcasecmp(category, vmu->context)) {
- if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
- ast_log(LOG_WARNING, "We could not find the mailbox.\n");
- break;
- }
- value = strstr(tmp, ",");
- if (!value) {
- ast_log(LOG_WARNING, "variable has bad format.\n");
- break;
- }
- new = alloca(strlen(value) + strlen(newpassword) + 1);
- sprintf(new, "%s%s", newpassword, value);
- if (!(cat = ast_category_get(cfg, category))) {
- ast_log(LOG_WARNING, "Failed to get category structure.\n");
- break;
- }
- ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
- }
- }
- /* save the results */
- reset_user_pw(vmu->context, vmu->mailbox, newpassword);
- ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
- config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
- }
- category = NULL;
- var = NULL;
- /* check users.conf and update the password stored for the mailbox*/
- /* if no vmsecret entry exists create one. */
- if ((cfg = ast_config_load("users.conf", config_flags))) {
- ast_debug(4, "we are looking for %s\n", vmu->mailbox);
- while ((category = ast_category_browse(cfg, category))) {
- ast_debug(4, "users.conf: %s\n", category);
- if (!strcasecmp(category, vmu->mailbox)) {
- if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
- ast_debug(3, "looks like we need to make vmsecret!\n");
- var = ast_variable_new("vmsecret", newpassword, "");
- }
- new = alloca(strlen(newpassword) + 1);
- sprintf(new, "%s", newpassword);
- if (!(cat = ast_category_get(cfg, category))) {
- ast_debug(4, "failed to get category!\n");
- break;
- }
- if (!var)
- ast_variable_update(cat, "vmsecret", new, NULL, 0);
- else
- ast_variable_append(cat, var);
- }
- }
- /* save the results and clean things up */
- reset_user_pw(vmu->context, vmu->mailbox, newpassword);
- ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
- config_text_file_save("users.conf", cfg, "AppVoicemail");
- }
-}
-
-static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
-{
- char buf[255];
- snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
- if (!ast_safe_system(buf)) {
- ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
- /* Reset the password in memory, too */
- reset_user_pw(vmu->context, vmu->mailbox, newpassword);
- }
-}
-
-static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
-{
- return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
-}
-
-#ifdef IMAP_STORAGE
-static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num)
-{
- int res;
- if ((res = ast_mkdir(dir, 01777))) {
- ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
- return snprintf(dest, len, "%s/msg%04d", dir, num);
- }
- return snprintf(dest, len, "%s/msg%04d", dir, num);
-}
-
-static void vm_imap_delete(int msgnum, struct vm_state *vms)
-{
- unsigned long messageNum = 0;
- char arg[10];
-
- /* find real message number based on msgnum */
- /* this may be an index into vms->msgArray based on the msgnum. */
-
- messageNum = vms->msgArray[msgnum];
- if (messageNum == 0) {
- ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
- return;
- }
- ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
- /* delete message */
- snprintf(arg, sizeof(arg), "%lu", messageNum);
- mail_setflag(vms->mailstream, arg, "\\DELETED");
-}
-
-#endif
-static int make_file(char *dest, int len, char *dir, int num)
-{
- return snprintf(dest, len, "%s/msg%04d", dir, num);
-}
-
-/*! \brief basically mkdir -p $dest/$context/$ext/$folder
- * \param dest String. base directory.
- * \param len Length of dest.
- * \param context String. Ignored if is null or empty string.
- * \param ext String. Ignored if is null or empty string.
- * \param folder String. Ignored if is null or empty string.
- * \return -1 on failure, 0 on success.
- */
-static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
-{
- mode_t mode = VOICEMAIL_DIR_MODE;
- int res;
-
- make_dir(dest, len, context, ext, folder);
- if ((res = ast_mkdir(dest, mode))) {
- ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
- return -1;
- }
- return 0;
-}
-
-/*! \brief Lock file path
- only return failure if ast_lock_path returns 'timeout',
- not if the path does not exist or any other reason
-*/
-static int vm_lock_path(const char *path)
-{
- switch (ast_lock_path(path)) {
- case AST_LOCK_TIMEOUT:
- return -1;
- default:
- return 0;
- }
-}
-
-
-#ifdef ODBC_STORAGE
-struct generic_prepare_struct {
- char *sql;
- int argc;
- char **argv;
-};
-
-static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
-{
- struct generic_prepare_struct *gps = data;
- int res, i;
- SQLHSTMT stmt;
-
- res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
- return NULL;
- }
- res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- return NULL;
- }
- for (i = 0; i < gps->argc; i++)
- SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
-
- return stmt;
-}
-
-static int retrieve_file(char *dir, int msgnum)
-{
- int x = 0;
- int res;
- int fd = -1;
- size_t fdlen = 0;
- void *fdm = MAP_FAILED;
- SQLSMALLINT colcount = 0;
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char fmt[80] = "";
- char *c;
- char coltitle[256];
- SQLSMALLINT collen;
- SQLSMALLINT datatype;
- SQLSMALLINT decimaldigits;
- SQLSMALLINT nullable;
- SQLULEN colsize;
- SQLLEN colsize2;
- FILE *f = NULL;
- char rowdata[80];
- char fn[PATH_MAX];
- char full_fn[PATH_MAX];
- char msgnums[80];
- char *argv[] = { dir, msgnums };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
-
- struct odbc_obj *obj;
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- ast_copy_string(fmt, vmfmts, sizeof(fmt));
- c = strchr(fmt, '|');
- if (c)
- *c = '\0';
- if (!strcasecmp(fmt, "wav49"))
- strcpy(fmt, "WAV");
- snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
- if (msgnum > -1)
- make_file(fn, sizeof(fn), dir, msgnum);
- else
- ast_copy_string(fn, dir, sizeof(fn));
- snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
-
- if (!(f = fopen(full_fn, "w+"))) {
- ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
- goto yuck;
- }
-
- snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
- snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt) {
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLFetch(stmt);
- if (res == SQL_NO_DATA) {
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
- if (fd < 0) {
- ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLNumResultCols(stmt, &colcount);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (f)
- fprintf(f, "[message]\n");
- for (x = 0; x < colcount; x++) {
- rowdata[0] = '\0';
- collen = sizeof(coltitle);
- res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
- &datatype, &colsize, &decimaldigits, &nullable);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (!strcasecmp(coltitle, "recording")) {
- off_t offset;
- res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
- fdlen = colsize2;
- if (fd > -1) {
- char tmp[1] = "";
- lseek(fd, fdlen - 1, SEEK_SET);
- if (write(fd, tmp, 1) != 1) {
- close(fd);
- fd = -1;
- continue;
- }
- /* Read out in small chunks */
- for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
- if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
- ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- } else {
- res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
- munmap(fdm, CHUNKSIZE);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- unlink(full_fn);
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- }
- }
- truncate(full_fn, fdlen);
- }
- } else {
- res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
- fprintf(f, "%s=%s\n", coltitle, rowdata);
- }
- }
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
- if (f)
- fclose(f);
- if (fd > -1)
- close(fd);
- return x - 1;
-}
-
-static int remove_file(char *dir, int msgnum)
-{
- char fn[PATH_MAX];
- char full_fn[PATH_MAX];
- char msgnums[80];
-
- if (msgnum > -1) {
- snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
- make_file(fn, sizeof(fn), dir, msgnum);
- } else
- ast_copy_string(fn, dir, sizeof(fn));
- ast_filedelete(fn, NULL);
- if (ast_check_realtime("voicemail_data")) {
- ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
- }
- snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
- unlink(full_fn);
- return 0;
-}
-
-static int last_message_index(struct ast_vm_user *vmu, char *dir)
-{
- int x = 0;
- int res;
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char rowdata[20];
- char *argv[] = { dir };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
-
- struct odbc_obj *obj;
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt) {
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLFetch(stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (sscanf(rowdata, "%d", &x) != 1)
- ast_log(LOG_WARNING, "Failed to read message count!\n");
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
- return x - 1;
-}
-
-static int message_exists(char *dir, int msgnum)
-{
- int x = 0;
- int res;
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char rowdata[20];
- char msgnums[20];
- char *argv[] = { dir, msgnums };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
-
- struct odbc_obj *obj;
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt) {
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLFetch(stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (sscanf(rowdata, "%d", &x) != 1)
- ast_log(LOG_WARNING, "Failed to read message count!\n");
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
- return x;
-}
-
-static int count_messages(struct ast_vm_user *vmu, char *dir)
-{
- return last_message_index(vmu, dir) + 1;
-}
-
-static void delete_file(char *sdir, int smsg)
-{
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char msgnums[20];
- char *argv[] = { sdir, msgnums };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
-
- struct odbc_obj *obj;
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(msgnums, sizeof(msgnums), "%d", smsg);
- snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt)
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- else
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
- return;
-}
-
-static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
-{
- SQLHSTMT stmt;
- char sql[512];
- char msgnums[20];
- char msgnumd[20];
- struct odbc_obj *obj;
- char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
-
- delete_file(ddir, dmsg);
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(msgnums, sizeof(msgnums), "%d", smsg);
- snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
- snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?", odbc_table, odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt)
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
- else
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
- return;
-}
-
-static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
-{
- int x = 0;
- int res;
- int fd = -1;
- void *fdm = MAP_FAILED;
- size_t fdlen = -1;
- SQLHSTMT stmt;
- SQLLEN len;
- char sql[PATH_MAX];
- char msgnums[20];
- char fn[PATH_MAX];
- char full_fn[PATH_MAX];
- char fmt[80] = "";
- char *c;
- const char *context = "", *macrocontext = "", *callerid = "", *origtime = "", *duration = "";
- const char *category = "";
- struct ast_config *cfg = NULL;
- struct odbc_obj *obj;
- struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
-
- delete_file(dir, msgnum);
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- ast_copy_string(fmt, vmfmts, sizeof(fmt));
- c = strchr(fmt, '|');
- if (c)
- *c = '\0';
- if (!strcasecmp(fmt, "wav49"))
- strcpy(fmt, "WAV");
- snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
- if (msgnum > -1)
- make_file(fn, sizeof(fn), dir, msgnum);
- else
- ast_copy_string(fn, dir, sizeof(fn));
- snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
- cfg = ast_config_load(full_fn, config_flags);
- snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
- fd = open(full_fn, O_RDWR);
- if (fd < 0) {
- ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (cfg) {
- context = ast_variable_retrieve(cfg, "message", "context");
- if (!context) context = "";
- macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
- if (!macrocontext) macrocontext = "";
- callerid = ast_variable_retrieve(cfg, "message", "callerid");
- if (!callerid) callerid = "";
- origtime = ast_variable_retrieve(cfg, "message", "origtime");
- if (!origtime) origtime = "";
- duration = ast_variable_retrieve(cfg, "message", "duration");
- if (!duration) duration = "";
- category = ast_variable_retrieve(cfg, "message", "category");
- if (!category) category = "";
- }
- fdlen = lseek(fd, 0, SEEK_END);
- lseek(fd, 0, SEEK_SET);
- printf("Length is %zd\n", fdlen);
- fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (fdm == MAP_FAILED) {
- ast_log(LOG_WARNING, "Memory map failed!\n");
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (!ast_strlen_zero(category))
- snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
- else
- snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)", odbc_table);
- res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
- SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
- SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
- SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
- SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
- SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
- SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
- SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
- SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
- SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
- SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
- if (!ast_strlen_zero(category))
- SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
- res = ast_odbc_smart_execute(obj, stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
- if (cfg)
- ast_config_destroy(cfg);
- if (fdm != MAP_FAILED)
- munmap(fdm, fdlen);
- if (fd > -1)
- close(fd);
- return x;
-}
-
-static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
-{
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char msgnums[20];
- char msgnumd[20];
- struct odbc_obj *obj;
- char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
-
- delete_file(ddir, dmsg);
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(msgnums, sizeof(msgnums), "%d", smsg);
- snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
- snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt)
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- else
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
- return;
-}
-
-#else
-#ifndef IMAP_STORAGE
-static int count_messages(struct ast_vm_user *vmu, char *dir)
-{
- /* Find all .txt files - even if they are not in sequence from 0000 */
-
- int vmcount = 0;
- DIR *vmdir = NULL;
- struct dirent *vment = NULL;
-
- if (vm_lock_path(dir))
- return ERROR_LOCK_PATH;
-
- if ((vmdir = opendir(dir))) {
- while ((vment = readdir(vmdir))) {
- if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
- vmcount++;
- }
- closedir(vmdir);
- }
- ast_unlock_path(dir);
-
- return vmcount;
-}
-
-static void rename_file(char *sfn, char *dfn)
-{
- char stxt[PATH_MAX];
- char dtxt[PATH_MAX];
- ast_filerename(sfn, dfn, NULL);
- snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
- snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
- if (ast_check_realtime("voicemail_data")) {
- ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
- }
- rename(stxt, dtxt);
-}
-
-static int copy(char *infile, char *outfile)
-{
- int ifd;
- int ofd;
- int res;
- int len;
- char buf[4096];
-
-#ifdef HARDLINK_WHEN_POSSIBLE
- /* Hard link if possible; saves disk space & is faster */
- if (link(infile, outfile)) {
-#endif
- if ((ifd = open(infile, O_RDONLY)) < 0) {
- ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
- return -1;
- }
- if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
- ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
- close(ifd);
- return -1;
- }
- do {
- len = read(ifd, buf, sizeof(buf));
- if (len < 0) {
- ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
- close(ifd);
- close(ofd);
- unlink(outfile);
- }
- if (len) {
- res = write(ofd, buf, len);
- if (errno == ENOMEM || errno == ENOSPC || res != len) {
- ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
- close(ifd);
- close(ofd);
- unlink(outfile);
- }
- }
- } while (len);
- close(ifd);
- close(ofd);
- return 0;
-#ifdef HARDLINK_WHEN_POSSIBLE
- } else {
- /* Hard link succeeded */
- return 0;
- }
-#endif
-}
-
-static void copy_file(char *frompath, char *topath)
-{
- char frompath2[PATH_MAX], topath2[PATH_MAX];
- struct ast_variable *tmp, *var = NULL;
- const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
- ast_filecopy(frompath, topath, NULL);
- snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
- snprintf(topath2, sizeof(topath2), "%s.txt", topath);
- if (ast_check_realtime("voicemail_data")) {
- var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
- /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
- for (tmp = var; tmp; tmp = tmp->next) {
- if (!strcasecmp(tmp->name, "origmailbox")) {
- origmailbox = tmp->value;
- } else if (!strcasecmp(tmp->name, "context")) {
- context = tmp->value;
- } else if (!strcasecmp(tmp->name, "macrocontext")) {
- macrocontext = tmp->value;
- } else if (!strcasecmp(tmp->name, "exten")) {
- exten = tmp->value;
- } else if (!strcasecmp(tmp->name, "priority")) {
- priority = tmp->value;
- } else if (!strcasecmp(tmp->name, "callerchan")) {
- callerchan = tmp->value;
- } else if (!strcasecmp(tmp->name, "callerid")) {
- callerid = tmp->value;
- } else if (!strcasecmp(tmp->name, "origdate")) {
- origdate = tmp->value;
- } else if (!strcasecmp(tmp->name, "origtime")) {
- origtime = tmp->value;
- } else if (!strcasecmp(tmp->name, "category")) {
- category = tmp->value;
- } else if (!strcasecmp(tmp->name, "duration")) {
- duration = tmp->value;
- }
- }
- ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, NULL);
- }
- copy(frompath2, topath2);
- ast_variables_destroy(var);
-}
-/*! \brief
- * A negative return value indicates an error.
- * \note Should always be called with a lock already set on dir.
- */
-static int last_message_index(struct ast_vm_user *vmu, char *dir)
-{
- int x;
- unsigned char map[MAXMSGLIMIT] = "";
- DIR *msgdir;
- struct dirent *msgdirent;
- int msgdirint;
-
- /* Reading the entire directory into a file map scales better than
- * doing a stat repeatedly on a predicted sequence. I suspect this
- * is partially due to stat(2) internally doing a readdir(2) itself to
- * find each file. */
- msgdir = opendir(dir);
- while ((msgdirent = readdir(msgdir))) {
- if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
- map[msgdirint] = 1;
- }
- closedir(msgdir);
-
- for (x = 0; x < vmu->maxmsg; x++) {
- if (map[x] == 0)
- break;
- }
-
- return x - 1;
-}
-
-#endif /*#ifndef IMAP_STORAGE*/
-#endif /*#else of #ifdef ODBC_STORAGE*/
-#ifndef ODBC_STORAGE
-static int vm_delete(char *file)
-{
- char *txt;
- int txtsize = 0;
-
- txtsize = (strlen(file) + 5) * sizeof(char);
- txt = alloca(txtsize);
- /* Sprintf here would safe because we alloca'd exactly the right length,
- * but trying to eliminate all sprintf's anyhow
- */
- if (ast_check_realtime("voicemail_data")) {
- ast_destroy_realtime("voicemail_data", "filename", file, NULL);
- }
- snprintf(txt, txtsize, "%s.txt", file);
- unlink(txt);
- return ast_filedelete(file, NULL);
-}
-#endif
-
-static int inbuf(struct baseio *bio, FILE *fi)
-{
- int l;
-
- if (bio->ateof)
- return 0;
-
- if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
- if (ferror(fi))
- return -1;
-
- bio->ateof = 1;
- return 0;
- }
-
- bio->iolen = l;
- bio->iocp = 0;
-
- return 1;
-}
-
-static int inchar(struct baseio *bio, FILE *fi)
-{
- if (bio->iocp >= bio->iolen) {
- if (!inbuf(bio, fi))
- return EOF;
- }
-
- return bio->iobuf[bio->iocp++];
-}
-
-static int ochar(struct baseio *bio, int c, FILE *so)
-{
- if (bio->linelength >= BASELINELEN) {
- if (fputs(eol, so) == EOF)
- return -1;
-
- bio->linelength= 0;
- }
-
- if (putc(((unsigned char)c), so) == EOF)
- return -1;
-
- bio->linelength++;
-
- return 1;
-}
-
-static int base_encode(char *filename, FILE *so)
-{
- static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
- 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
- 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
- '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
- int i, hiteof = 0;
- FILE *fi;
- struct baseio bio;
-
- memset(&bio, 0, sizeof(bio));
- bio.iocp = BASEMAXINLINE;
-
- if (!(fi = fopen(filename, "rb"))) {
- ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
- return -1;
- }
-
- while (!hiteof) {
- unsigned char igroup[3], ogroup[4];
- int c, n;
-
- igroup[0] = igroup[1] = igroup[2] = 0;
-
- for (n = 0; n < 3; n++) {
- if ((c = inchar(&bio, fi)) == EOF) {
- hiteof = 1;
- break;
- }
-
- igroup[n] = (unsigned char)c;
- }
-
- if (n > 0) {
- ogroup[0] = dtable[igroup[0] >> 2];
- ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
- ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
- ogroup[3] = dtable[igroup[2] & 0x3F];
-
- if (n < 3) {
- ogroup[3] = '=';
-
- if (n < 2)
- ogroup[2] = '=';
- }
-
- for (i = 0; i < 4; i++)
- ochar(&bio, ogroup[i], so);
- }
- }
-
- fclose(fi);
-
- if (fputs(eol, so) == EOF)
- return 0;
-
- return 1;
-}
-
-static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category)
-{
- char callerid[256];
- /* Prepare variables for substitution in email body and subject */
- pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
- pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
- snprintf(passdata, passdatasize, "%d", msgnum);
- pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
- pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
- pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
- pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
- pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
- pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
- pbx_builtin_setvar_helper(ast, "VM_DATE", date);
- pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
-}
-
-static char *quote(const char *from, char *to, size_t len)
-{
- char *ptr = to;
- *ptr++ = '"';
- for (; ptr < to + len - 1; from++) {
- if (*from == '"')
- *ptr++ = '\\';
- else if (*from == '\0')
- break;
- *ptr++ = *from;
- }
- if (ptr < to + len - 1)
- *ptr++ = '"';
- *ptr = '\0';
- return to;
-}
-
-/*! \brief
- * fill in *tm for current time according to the proper timezone, if any.
- * Return tm so it can be used as a function argument.
- */
-static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
-{
- const struct vm_zone *z = NULL;
- struct timeval t = ast_tvnow();
-
- /* Does this user have a timezone specified? */
- if (!ast_strlen_zero(vmu->zonetag)) {
- /* Find the zone in the list */
- AST_LIST_LOCK(&zones);
- AST_LIST_TRAVERSE(&zones, z, list) {
- if (!strcmp(z->name, vmu->zonetag))
- break;
- }
- AST_LIST_UNLOCK(&zones);
- }
- ast_localtime(&t, tm, z ? z->timezone : NULL);
- return tm;
-}
-
-/*! \brief same as mkstemp, but return a FILE * */
-static FILE *vm_mkftemp(char *template)
-{
- FILE *p = NULL;
- int pfd = mkstemp(template);
- chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
- if (pfd > -1) {
- p = fdopen(pfd, "w+");
- if (!p) {
- close(pfd);
- pfd = -1;
- }
- }
- return p;
-}
-
-static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
-{
- char date[256];
- char host[MAXHOSTNAMELEN] = "";
- char who[256];
- char bound[256];
- char fname[256];
- char dur[256];
- char tmpcmd[256];
- struct ast_tm tm;
- char *passdata2;
- size_t len_passdata;
- char *greeting_attachment;
-
-#ifdef IMAP_STORAGE
-#define ENDL "\r\n"
-#else
-#define ENDL "\n"
-#endif
-
- gethostname(host, sizeof(host) - 1);
-
- if (strchr(srcemail, '@'))
- ast_copy_string(who, srcemail, sizeof(who));
- else
- snprintf(who, sizeof(who), "%s@%s", srcemail, host);
-
- greeting_attachment = strrchr(ast_strdupa(attach), '/');
- if (greeting_attachment)
- *greeting_attachment++ = '\0';
-
- snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
- ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
- fprintf(p, "Date: %s" ENDL, date);
-
- /* Set date format for voicemail mail */
- ast_strftime(date, sizeof(date), emaildateformat, &tm);
-
- if (!ast_strlen_zero(fromstring)) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(fromstring) * 3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
- pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
- len_passdata = strlen(passdata) * 2 + 3;
- passdata2 = alloca(len_passdata);
- fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
- ast_channel_free(ast);
- } else
- ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- } else
- fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
- len_passdata = strlen(vmu->fullname) * 2 + 3;
- passdata2 = alloca(len_passdata);
- fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
- if (!ast_strlen_zero(emailsubject)) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(emailsubject) * 3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
- pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
- fprintf(p, "Subject: %s" ENDL, passdata);
- ast_channel_free(ast);
- } else
- ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- } else if (!ast_strlen_zero(emailtitle)) {
- fprintf(p, emailtitle, msgnum + 1, mailbox) ;
- fprintf(p, ENDL) ;
- } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
- fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
- else
- fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
- fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
- if (imap) {
- /* additional information needed for IMAP searching */
- fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
- /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
- fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
- fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
- fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
- fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
- fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
- fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
- fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
- fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
- if (!ast_strlen_zero(category))
- fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
- fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
- fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
- fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
- }
- if (!ast_strlen_zero(cidnum))
- fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
- if (!ast_strlen_zero(cidname))
- fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
- fprintf(p, "MIME-Version: 1.0" ENDL);
- if (attach_user_voicemail) {
- /* Something unique. */
- snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
-
- fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
- fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
- fprintf(p, "--%s" ENDL, bound);
- }
- fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
- if (emailbody) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(emailbody) * 3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
- pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
- fprintf(p, "%s" ENDL, passdata);
- ast_channel_free(ast);
- } else
- ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- } else if (msgnum > -1) {
- fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
-
- "in mailbox %s from %s, on %s so you might" ENDL
- "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
- dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
- } else {
- fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
- "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
- }
- if (attach_user_voicemail) {
- /* Eww. We want formats to tell us their own MIME type */
- char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
- char tmpdir[256], newtmp[256];
- int tmpfd = -1;
-
- if (vmu->volgain < -.001 || vmu->volgain > .001) {
- create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
- snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
- tmpfd = mkstemp(newtmp);
- chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
- ast_debug(3, "newtmp: %s\n", newtmp);
- if (tmpfd > -1) {
- snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
- ast_safe_system(tmpcmd);
- attach = newtmp;
- ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
- }
- }
- fprintf(p, "--%s" ENDL, bound);
- if (msgnum > -1)
- fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
- else
- fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
- fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
- fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
- if (msgnum > -1)
- fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
- else
- fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
- snprintf(fname, sizeof(fname), "%s.%s", attach, format);
- base_encode(fname, p);
- fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
- if (tmpfd > -1) {
- unlink(fname);
- close(tmpfd);
- unlink(newtmp);
- }
- }
-#undef ENDL
-}
-
-static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
-{
- FILE *p = NULL;
- char tmp[80] = "/tmp/astmail-XXXXXX";
- char tmp2[256];
-
- if (vmu && ast_strlen_zero(vmu->email)) {
- ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
- return(0);
- }
- if (!strcmp(format, "wav49"))
- format = "WAV";
- ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
- /* Make a temporary file instead of piping directly to sendmail, in case the mail
- command hangs */
- if ((p = vm_mkftemp(tmp)) == NULL) {
- ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
- return -1;
- } else {
- make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
- fclose(p);
- snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
- ast_safe_system(tmp2);
- ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
- }
- return 0;
-}
-
-static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category)
-{
- char date[256];
- char host[MAXHOSTNAMELEN] = "";
- char who[256];
- char dur[PATH_MAX];
- char tmp[80] = "/tmp/astmail-XXXXXX";
- char tmp2[PATH_MAX];
- struct ast_tm tm;
- FILE *p;
-
- if ((p = vm_mkftemp(tmp)) == NULL) {
- ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
- return -1;
- }
- gethostname(host, sizeof(host) - 1);
- if (strchr(srcemail, '@'))
- ast_copy_string(who, srcemail, sizeof(who));
- else
- snprintf(who, sizeof(who), "%s@%s", srcemail, host);
- snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
- ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
- fprintf(p, "Date: %s\n", date);
-
- if (*pagerfromstring) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(fromstring) * 3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
- pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
- fprintf(p, "From: %s <%s>\n", passdata, who);
- ast_channel_free(ast);
- } else
- ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- } else
- fprintf(p, "From: Asterisk PBX <%s>\n", who);
- fprintf(p, "To: %s\n", pager);
- if (pagersubject) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(pagersubject) * 3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
- pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
- fprintf(p, "Subject: %s\n\n", passdata);
- ast_channel_free(ast);
- } else
- ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- } else
- fprintf(p, "Subject: New VM\n\n");
-
- ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
- if (pagerbody) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(pagerbody) * 3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
- pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
- fprintf(p, "%s\n", passdata);
- ast_channel_free(ast);
- } else
- ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- } else {
- fprintf(p, "New %s long msg in box %s\n"
- "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
- }
- fclose(p);
- snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
- ast_safe_system(tmp2);
- ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
- return 0;
-}
-
-static int get_date(char *s, int len)
-{
- struct ast_tm tm;
- struct timeval t = ast_tvnow();
-
- ast_localtime(&t, &tm, "UTC");
-
- return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
-}
-
-static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
-{
- int res = -2;
-
-#ifdef ODBC_STORAGE
- int success =
-#endif
- RETRIEVE(filename, -1, vmu->mailbox, vmu->context);
- if (ast_fileexists(filename, NULL, NULL) > 0) {
- res = ast_streamfile(chan, filename, chan->language);
- if (res > -1)
- res = ast_waitstream(chan, ecodes);
-#ifdef ODBC_STORAGE
- if (success == -1) {
- /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
- ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
- store_file(filename, vmu->mailbox, vmu->context, -1);
- }
-#endif
- }
- DISPOSE(filename, -1);
-
- return res;
-}
-
-static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
-{
- int res;
- char fn[PATH_MAX];
- char dest[PATH_MAX];
-
- snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
-
- if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, ""))) {
- ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
- return -1;
- }
-
- res = play_greeting(chan, vmu, fn, ecodes);
- if (res == -2) {
- /* File did not exist */
- res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
- if (res)
- return res;
- res = ast_say_digit_str(chan, ext, ecodes, chan->language);
- }
- if (res)
- return res;
-
- res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
- return res;
-}
-
-static void free_user(struct ast_vm_user *vmu)
-{
- if (!ast_test_flag(vmu, VM_ALLOCED))
- return;
-
- ast_free(vmu);
-}
-
-static void free_zone(struct vm_zone *z)
-{
- ast_free(z);
-}
-
-static const char *mbox(int id)
-{
- static const char *msgs[] = {
-#ifdef IMAP_STORAGE
- imapfolder,
-#else
- "INBOX",
-#endif
- "Old",
- "Work",
- "Family",
- "Friends",
- "Cust1",
- "Cust2",
- "Cust3",
- "Cust4",
- "Cust5",
- "Deleted",
- };
- return (id >= 0 && id < (sizeof(msgs) / sizeof(msgs[0]))) ? msgs[id] : "Unknown";
-}
-#ifdef IMAP_STORAGE
-static int folder_int(const char *folder)
-{
- /*assume a NULL folder means INBOX*/
- if (!folder)
- return 0;
-#ifdef IMAP_STORAGE
- if (!strcasecmp(folder, imapfolder))
-#else
- if (!strcasecmp(folder, "INBOX"))
-#endif
- return 0;
- else if (!strcasecmp(folder, "Old"))
- return 1;
- else if (!strcasecmp(folder, "Work"))
- return 2;
- else if (!strcasecmp(folder, "Family"))
- return 3;
- else if (!strcasecmp(folder, "Friends"))
- return 4;
- else if (!strcasecmp(folder, "Cust1"))
- return 5;
- else if (!strcasecmp(folder, "Cust2"))
- return 6;
- else if (!strcasecmp(folder, "Cust3"))
- return 7;
- else if (!strcasecmp(folder, "Cust4"))
- return 8;
- else if (!strcasecmp(folder, "Cust5"))
- return 9;
- else if (!strcasecmp(folder, "Deleted"))
- return 10;
- else /*assume they meant INBOX if folder is not found otherwise*/
- return 0;
-}
-#endif
-
-#ifdef ODBC_STORAGE
-/*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
-static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
-{
- int x = -1;
- int res;
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char rowdata[20];
- char tmp[PATH_MAX] = "";
- struct odbc_obj *obj;
- char *context;
- struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
-
- if (newmsgs)
- *newmsgs = 0;
- if (oldmsgs)
- *oldmsgs = 0;
-
- /* If no mailbox, return immediately */
- if (ast_strlen_zero(mailbox))
- return 0;
-
- ast_copy_string(tmp, mailbox, sizeof(tmp));
-
- context = strchr(tmp, '@');
- if (context) {
- *context = '\0';
- context++;
- } else
- context = "default";
-
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt) {
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLFetch(stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- *newmsgs = atoi(rowdata);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt) {
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLFetch(stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- *oldmsgs = atoi(rowdata);
- x = 0;
- } else
- ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-
-yuck:
- return x;
-}
-
-static int messagecount(const char *context, const char *mailbox, const char *folder)
-{
- struct odbc_obj *obj = NULL;
- int nummsgs = 0;
- int res;
- SQLHSTMT stmt = NULL;
- char sql[PATH_MAX];
- char rowdata[20];
- struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
- if (!folder)
- folder = "INBOX";
- /* If no mailbox, return immediately */
- if (ast_strlen_zero(mailbox))
- return 0;
-
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt) {
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- goto yuck;
- }
- res = SQLFetch(stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- goto yuck;
- }
- res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- goto yuck;
- }
- nummsgs = atoi(rowdata);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- } else
- ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-
-yuck:
- if (obj)
- ast_odbc_release_obj(obj);
- return nummsgs;
-}
-
-static int has_voicemail(const char *mailbox, const char *folder)
-{
- char tmp[256], *tmp2 = tmp, *mbox, *context;
- ast_copy_string(tmp, mailbox, sizeof(tmp));
- while ((context = mbox = strsep(&tmp2, ","))) {
- strsep(&context, "@");
- if (ast_strlen_zero(context))
- context = "default";
- if (messagecount(context, mbox, folder))
- return 1;
- }
- return 0;
-}
-
-#elif defined(IMAP_STORAGE)
-
-static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms)
-{
- char *myserveremail = serveremail;
- char fn[PATH_MAX];
- char mailbox[256];
- char *stringp;
- FILE *p = NULL;
- char tmp[80] = "/tmp/astmail-XXXXXX";
- long len;
- void *buf;
- int tempcopy = 0;
- STRING str;
-
- /* Attach only the first format */
- fmt = ast_strdupa(fmt);
- stringp = fmt;
- strsep(&stringp, "|");
-
- if (!ast_strlen_zero(vmu->serveremail))
- myserveremail = vmu->serveremail;
-
- if (msgnum > -1)
- make_file(fn, sizeof(fn), dir, msgnum);
- else
- ast_copy_string (fn, dir, sizeof(fn));
-
- if (ast_strlen_zero(vmu->email)) {
- /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
- * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
- * string if tempcopy is 1
- */
- ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
- tempcopy = 1;
- }
-
- if (!strcmp(fmt, "wav49"))
- fmt = "WAV";
- ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
-
- /* Make a temporary file instead of piping directly to sendmail, in case the mail
- command hangs */
- if (!(p = vm_mkftemp(tmp))) {
- ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
- if (tempcopy)
- *(vmu->email) = '\0';
- return -1;
- }
-
- if (msgnum < 0 && imapgreetings) {
- init_mailstream(vms, GREETINGS_FOLDER);
- imap_delete_old_greeting(fn, vms);
- }
-
- make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, fmt, duration, 1, chan, NULL, 1);
- /* read mail file to memory */
- len = ftell(p);
- rewind(p);
- if (!(buf = ast_malloc(len + 1))) {
- ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
- fclose(p);
- if (tempcopy)
- *(vmu->email) = '\0';
- return -1;
- }
- fread(buf, len, 1, p);
- ((char *)buf)[len] = '\0';
- INIT(&str, mail_string, buf, len);
- init_mailstream(vms, NEW_FOLDER);
- imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
- if (!mail_append(vms->mailstream, mailbox, &str))
- ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
- fclose(p);
- unlink(tmp);
- ast_free(buf);
- ast_debug(3, "%s stored\n", fn);
-
- if (tempcopy)
- *(vmu->email) = '\0';
-
- return 0;
-
-}
-
-static int messagecount(const char *context, const char *mailbox, const char *folder)
-{
- SEARCHPGM *pgm;
- SEARCHHEADER *hdr;
-
- struct ast_vm_user *vmu, vmus;
- struct vm_state *vms_p;
- int ret = 0;
- int fold = folder_int(folder);
-
- if (ast_strlen_zero(mailbox))
- return 0;
-
- /* We have to get the user before we can open the stream! */
- /* ast_log(LOG_DEBUG, "Before find_user, context is %s and mailbox is %s\n", context, mailbox); */
- vmu = find_user(&vmus, context, mailbox);
- if (!vmu) {
- ast_log(LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
- return -1;
- } else {
- /* No IMAP account available */
- if (vmu->imapuser[0] == '\0') {
- ast_log(LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
- return -1;
- }
- }
-
- /* No IMAP account available */
- if (vmu->imapuser[0] == '\0') {
- ast_log(LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
- free_user(vmu);
- return -1;
- }
-
- /* check if someone is accessing this box right now... */
- vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
- if (!vms_p) {
- vms_p = get_vm_state_by_mailbox(mailbox, 1);
- }
- if (vms_p) {
- ast_debug(3, "Returning before search - user is logged in\n");
- if (fold == 0) {/*INBOX*/
- return vms_p->newmessages;
- }
- if (fold == 1) {/*Old messages*/
- return vms_p->oldmessages;
- }
- }
-
- /* add one if not there... */
- vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
- if (!vms_p) {
- vms_p = get_vm_state_by_mailbox(mailbox, 0);
- }
-
- if (!vms_p) {
- ast_debug(3, "Adding new vmstate for %s\n", vmu->imapuser);
- if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
- return -1;
- }
- ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
- ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
- vms_p->mailstream = NIL; /* save for access from interactive entry point */
- ast_debug(3, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
- vms_p->updated = 1;
- /* set mailbox to INBOX! */
- ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
- init_vm_state(vms_p);
- vmstate_insert(vms_p);
- }
- ret = init_mailstream(vms_p, fold);
- if (!vms_p->mailstream) {
- ast_log(LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
- return -1;
- }
- if (ret == 0) {
- pgm = mail_newsearchpgm ();
- hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
- pgm->header = hdr;
- if (fold != 1) {
- pgm->unseen = 1;
- pgm->seen = 0;
- }
- /* In the special case where fold is 1 (old messages) we have to do things a bit
- * differently. Old messages are stored in the INBOX but are marked as "seen"
- */
- else {
- pgm->unseen = 0;
- pgm->seen = 1;
- }
- pgm->undeleted = 1;
- pgm->deleted = 0;
-
- vms_p->vmArrayIndex = 0;
- mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
- if (fold == 0)
- vms_p->newmessages = vms_p->vmArrayIndex;
- if (fold == 1)
- vms_p->oldmessages = vms_p->vmArrayIndex;
- /*Freeing the searchpgm also frees the searchhdr*/
- mail_free_searchpgm(&pgm);
- vms_p->updated = 0;
- return vms_p->vmArrayIndex;
- } else {
- mail_ping(vms_p->mailstream);
- }
- return 0;
-}
-static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
-{
- char tmp[PATH_MAX] = "";
- char *mailboxnc;
- char *context;
- char *mb;
- char *cur;
- if (newmsgs)
- *newmsgs = 0;
- if (oldmsgs)
- *oldmsgs = 0;
-
- ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
- /* If no mailbox, return immediately */
- if (ast_strlen_zero(mailbox_context))
- return 0;
-
- ast_copy_string(tmp, mailbox_context, sizeof(tmp));
- context = strchr(tmp, '@');
- if (strchr(mailbox_context, ',')) {
- int tmpnew, tmpold;
- ast_copy_string(tmp, mailbox_context, sizeof(tmp));
- mb = tmp;
- while ((cur = strsep(&mb, ", "))) {
- if (!ast_strlen_zero(cur)) {
- if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
- return -1;
- else {
- if (newmsgs)
- *newmsgs += tmpnew;
- if (oldmsgs)
- *oldmsgs += tmpold;
- }
- }
- }
- return 0;
- }
- if (context) {
- *context = '\0';
- mailboxnc = tmp;
- context++;
- } else {
- context = "default";
- mailboxnc = (char *)mailbox_context;
- }
- if (newmsgs) {
- if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
- return -1;
- }
- if (oldmsgs) {
- if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
- return -1;
- }
- return 0;
-}
-
-
-static int has_voicemail(const char *mailbox, const char *folder)
-{
- char tmp[256], *tmp2, *mbox, *context;
- ast_copy_string(tmp, mailbox, sizeof(tmp));
- tmp2 = tmp;
- if (strchr(tmp2, ',')) {
- while ((mbox = strsep(&tmp2, ","))) {
- if (!ast_strlen_zero(mbox)) {
- if (has_voicemail(mbox, folder))
- return 1;
- }
- }
- }
- if ((context= strchr(tmp, '@')))
- *context++ = '\0';
- else
- context = "default";
- return messagecount(context, tmp, folder) ? 1 : 0;
-}
-
-static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
-{
- struct vm_state *sendvms = NULL, *destvms = NULL;
- char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
- if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
- ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
- return -1;
- }
- if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
- ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
- return -1;
- }
- snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
- if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
- return 0;
- ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
- return -1;
-}
-
-#endif
-#ifndef IMAP_STORAGE
-/* copy message only used by file storage */
-static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
-{
- char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
- const char *frombox = mbox(imbox);
- int recipmsgnum;
-
- ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
-
- create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
-
- if (!dir)
- make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
- else
- ast_copy_string(fromdir, dir, sizeof(fromdir));
-
- make_file(frompath, sizeof(frompath), fromdir, msgnum);
- make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
-
- if (vm_lock_path(todir))
- return ERROR_LOCK_PATH;
-
- recipmsgnum = last_message_index(recip, todir) + 1;
- if (recipmsgnum < recip->maxmsg) {
- make_file(topath, sizeof(topath), todir, recipmsgnum);
- COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
- } else {
- ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
- }
- ast_unlock_path(todir);
- notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
-
- return 0;
-}
-#endif
-#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
-static int messagecount(const char *context, const char *mailbox, const char *folder)
-{
- return __has_voicemail(context, mailbox, folder, 0);
-}
-
-
-static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
-{
- DIR *dir;
- struct dirent *de;
- char fn[256];
- int ret = 0;
-
- /* If no mailbox, return immediately */
- if (ast_strlen_zero(mailbox))
- return 0;
-
- if (ast_strlen_zero(folder))
- folder = "INBOX";
- if (ast_strlen_zero(context))
- context = "default";
-
- snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
-
- if (!(dir = opendir(fn)))
- return 0;
-
- while ((de = readdir(dir))) {
- if (!strncasecmp(de->d_name, "msg", 3)) {
- if (shortcircuit) {
- ret = 1;
- break;
- } else if (!strncasecmp(de->d_name + 8, "txt", 3))
- ret++;
- }
- }
-
- closedir(dir);
-
- return ret;
-}
-
-
-static int has_voicemail(const char *mailbox, const char *folder)
-{
- char tmp[256], *tmp2 = tmp, *mbox, *context;
- ast_copy_string(tmp, mailbox, sizeof(tmp));
- while ((mbox = strsep(&tmp2, ","))) {
- if ((context = strchr(mbox, '@')))
- *context++ = '\0';
- else
- context = "default";
- if (__has_voicemail(context, mbox, folder, 1))
- return 1;
- }
- return 0;
-}
-
-
-static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
-{
- char tmp[256];
- char *context;
-
- /* If no mailbox, return immediately */
- if (ast_strlen_zero(mailbox))
- return 0;
-
- if (newmsgs)
- *newmsgs = 0;
- if (oldmsgs)
- *oldmsgs = 0;
-
- if (strchr(mailbox, ',')) {
- int tmpnew, tmpold;
- char *mb, *cur;
-
- ast_copy_string(tmp, mailbox, sizeof(tmp));
- mb = tmp;
- while ((cur = strsep(&mb, ", "))) {
- if (!ast_strlen_zero(cur)) {
- if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
- return -1;
- else {
- if (newmsgs)
- *newmsgs += tmpnew;
- if (oldmsgs)
- *oldmsgs += tmpold;
- }
- }
- }
- return 0;
- }
-
- ast_copy_string(tmp, mailbox, sizeof(tmp));
-
- if ((context = strchr(tmp, '@')))
- *context++ = '\0';
- else
- context = "default";
-
- if (newmsgs)
- *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
- if (oldmsgs)
- *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
-
- return 0;
-}
-
-#endif
-
-static void run_externnotify(char *context, char *extension)
-{
- char arguments[255];
- char ext_context[256] = "";
- int newvoicemails = 0, oldvoicemails = 0;
- struct ast_smdi_mwi_message *mwi_msg;
-
- if (!ast_strlen_zero(context))
- snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
- else
- ast_copy_string(ext_context, extension, sizeof(ext_context));
-
- if (smdi_iface) {
- if (ast_app_has_voicemail(ext_context, NULL))
- ast_smdi_mwi_set(smdi_iface, extension);
- else
- ast_smdi_mwi_unset(smdi_iface, extension);
-
- if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
- ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
- if (!strncmp(mwi_msg->cause, "INV", 3))
- ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
- else if (!strncmp(mwi_msg->cause, "BLK", 3))
- ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
- ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
- ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
- } else {
- ast_debug(1, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
- }
- }
-
- if (!ast_strlen_zero(externnotify)) {
- if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
- ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
- } else {
- snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
- ast_debug(1, "Executing %s\n", arguments);
- ast_safe_system(arguments);
- }
- }
-}
-
-struct leave_vm_options {
- unsigned int flags;
- signed char record_gain;
- char *exitcontext;
-};
-
-static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
-{
-#ifdef IMAP_STORAGE
- int newmsgs, oldmsgs;
- struct vm_state *vms = NULL;
-#endif
- char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
- char callerid[256];
- FILE *txt;
- char date[256];
- int txtdes;
- int res = 0;
- int msgnum;
- int duration = 0;
- int ausemacro = 0;
- int ousemacro = 0;
- int ouseexten = 0;
- int rtmsgid = 0;
- char tmpid[16];
- char tmpdur[16];
- char priority[16];
- char origtime[16];
- char dir[PATH_MAX], tmpdir[PATH_MAX];
- char fn[PATH_MAX];
- char prefile[PATH_MAX] = "";
- char tempfile[PATH_MAX] = "";
- char ext_context[256] = "";
- char fmt[80];
- char *context;
- char ecodes[17] = "#";
- char tmp[1024] = "", *tmpptr;
- struct ast_vm_user *vmu;
- struct ast_vm_user svm;
- const char *category = NULL, *code, *alldtmf = "0123456789ABCD*#";
-
- ast_copy_string(tmp, ext, sizeof(tmp));
- ext = tmp;
- if ((context = strchr(tmp, '@'))) {
- *context++ = '\0';
- tmpptr = strchr(context, '&');
- } else {
- tmpptr = strchr(ext, '&');
- }
-
- if (tmpptr)
- *tmpptr++ = '\0';
-
- category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
-
- ast_debug(3, "Before find_user\n");
- if (!(vmu = find_user(&svm, context, ext))) {
- ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- return res;
- }
- /* Setup pre-file if appropriate */
- if (strcmp(vmu->context, "default"))
- snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
- else
- ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
- if (ast_test_flag(options, OPT_BUSY_GREETING)) {
- snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
- } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
- snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
- }
- snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
- if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
- ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
- return -1;
- }
- RETRIEVE(tempfile, -1, ext, context);
- if (ast_fileexists(tempfile, NULL, NULL) > 0)
- ast_copy_string(prefile, tempfile, sizeof(prefile));
- DISPOSE(tempfile, -1);
- /* It's easier just to try to make it than to check for its existence */
- create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
-
- /* Check current or macro-calling context for special extensions */
- if (ast_test_flag(vmu, VM_OPERATOR)) {
- if (!ast_strlen_zero(vmu->exit)) {
- if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
- strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
- ouseexten = 1;
- }
- } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
- strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
- ouseexten = 1;
- }
- else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
- strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
- ousemacro = 1;
- }
- }
-
- if (!ast_strlen_zero(vmu->exit)) {
- if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
- strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
- } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
- strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
- else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
- strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
- ausemacro = 1;
- }
-
- if (ast_test_flag(options, OPT_DTMFEXIT)) {
- for (code = alldtmf; *code; code++) {
- char e[2] = "";
- e[0] = *code;
- if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
- strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
- }
- }
-
- /* Play the beginning intro if desired */
- if (!ast_strlen_zero(prefile)) {
- res = play_greeting(chan, vmu, prefile, ecodes);
- if (res == -2) {
- /* The file did not exist */
- ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
- res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
- }
- if (res < 0) {
- ast_debug(1, "Hang up during prefile playback\n");
- free_user(vmu);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- return -1;
- }
- }
- if (res == '#') {
- /* On a '#' we skip the instructions */
- ast_set_flag(options, OPT_SILENT);
- res = 0;
- }
- if (!res && !ast_test_flag(options, OPT_SILENT)) {
- res = ast_stream_and_wait(chan, INTRO, ecodes);
- if (res == '#') {
- ast_set_flag(options, OPT_SILENT);
- res = 0;
- }
- }
- if (res > 0)
- ast_stopstream(chan);
- /* Check for a '*' here in case the caller wants to escape from voicemail to something
- other than the operator -- an automated attendant or mailbox login for example */
- if (!ast_strlen_zero(vmu->exit) && (res == '*')) {
- chan->exten[0] = 'a';
- chan->exten[1] = '\0';
- if (!ast_strlen_zero(vmu->exit)) {
- ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
- } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
- ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
- }
- chan->priority = 0;
- free_user(vmu);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
- return 0;
- }
-
- /* Check for a '0' here */
- if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
- transfer:
- if (ouseexten || ousemacro) {
- chan->exten[0] = 'o';
- chan->exten[1] = '\0';
- if (!ast_strlen_zero(vmu->exit)) {
- ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
- } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
- ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
- }
- ast_play_and_wait(chan, "transfer");
- chan->priority = 0;
- free_user(vmu);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
- }
- return 0;
- }
-
- /* Allow all other digits to exit Voicemail and return to the dialplan */
- if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
- if (!ast_strlen_zero(options->exitcontext))
- ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
- free_user(vmu);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
- return res;
- }
-
- if (res < 0) {
- free_user(vmu);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- return -1;
- }
- /* The meat of recording the message... All the announcements and beeps have been played*/
- ast_copy_string(fmt, vmfmts, sizeof(fmt));
- if (!ast_strlen_zero(fmt)) {
- msgnum = 0;
-
-#ifdef IMAP_STORAGE
- /* Is ext a mailbox? */
- /* must open stream for this user to get info! */
- res = inboxcount(ext_context, &newmsgs, &oldmsgs);
- if (res < 0) {
- ast_log(LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
- return -1;
- }
- if (!(vms = get_vm_state_by_mailbox(ext, 0))) {
- /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
- * rarely be used*/
- if (!(vms = ast_calloc(1, sizeof(*vms)))) {
- ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
- return -1;
- }
- ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
- ast_copy_string(vms->username, ext, sizeof(vms->username));
- vms->mailstream = NIL;
- ast_debug(3, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
- vms->updated = 1;
- ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
- init_vm_state(vms);
- vmstate_insert(vms);
- vms = get_vm_state_by_mailbox(ext, 0);
- }
- vms->newmessages++;
-
- /* here is a big difference! We add one to it later */
- msgnum = newmsgs + oldmsgs;
- ast_debug(3, "Messagecount set to %d\n", msgnum);
- snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
- /* set variable for compatibility */
- pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
-
- /* Check if mailbox is full */
- check_quota(vms, imapfolder);
- if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
- ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
- ast_play_and_wait(chan, "vm-mailboxfull");
- return -1;
- }
-
- /* Check if we have exceeded maxmsg */
- if (msgnum >= vmu->maxmsg) {
- ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
- ast_play_and_wait(chan, "vm-mailboxfull");
- return -1;
- }
-
-#else
- if (count_messages(vmu, dir) >= vmu->maxmsg) {
- res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
- ast_log(LOG_WARNING, "No more messages possible\n");
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- goto leave_vm_out;
- }
-
-#endif
- snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
- txtdes = mkstemp(tmptxtfile);
- chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
- if (txtdes < 0) {
- res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
- ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- goto leave_vm_out;
- }
-
- /* Now play the beep once we have the message number for our next message. */
- if (res >= 0) {
- /* Unless we're *really* silent, try to send the beep */
- res = ast_stream_and_wait(chan, "beep", "");
- }
-
- /* Store information in real-time storage */
- if (ast_check_realtime("voicemail_data")) {
- snprintf(priority, sizeof(priority), "%d", chan->priority);
- snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
- get_date(date, sizeof(date));
- rtmsgid = ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", category ? category : "", NULL);
- }
-
- /* Store information */
- txt = fdopen(txtdes, "w+");
- if (txt) {
- get_date(date, sizeof(date));
- fprintf(txt,
- ";\n"
- "; Message Information file\n"
- ";\n"
- "[message]\n"
- "origmailbox=%s\n"
- "context=%s\n"
- "macrocontext=%s\n"
- "exten=%s\n"
- "priority=%d\n"
- "callerchan=%s\n"
- "callerid=%s\n"
- "origdate=%s\n"
- "origtime=%ld\n"
- "category=%s\n",
- ext,
- chan->context,
- chan->macrocontext,
- chan->exten,
- chan->priority,
- chan->name,
- ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
- date, (long)time(NULL),
- category ? category : "");
- } else
- ast_log(LOG_WARNING, "Error opening text file for output\n");
-#ifdef IMAP_STORAGE
- res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
-#else
- res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
-#endif
-
- if (txt) {
- if (duration < vmminsecs) {
- fclose(txt);
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
- ast_filedelete(tmptxtfile, NULL);
- unlink(tmptxtfile);
- if (ast_check_realtime("voicemail_data")) {
- snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
- ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
- }
- } else {
- fprintf(txt, "duration=%d\n", duration);
- fclose(txt);
- if (vm_lock_path(dir)) {
- ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
- /* Delete files */
- ast_filedelete(tmptxtfile, NULL);
- unlink(tmptxtfile);
- } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
- ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
- unlink(tmptxtfile);
- ast_unlock_path(dir);
- if (ast_check_realtime("voicemail_data")) {
- snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
- ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
- }
- } else {
-#ifndef IMAP_STORAGE
- msgnum = last_message_index(vmu, dir) + 1;
-#endif
- make_file(fn, sizeof(fn), dir, msgnum);
-
- /* assign a variable with the name of the voicemail file */
-#ifndef IMAP_STORAGE
- pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
-#else
- pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
-#endif
-
- snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
- ast_filerename(tmptxtfile, fn, NULL);
- rename(tmptxtfile, txtfile);
-
- /* Properly set permissions on voicemail text descriptor file.
- Unfortunately mkstemp() makes this file 0600 on most unix systems. */
- if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
- ast_log(LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
-
- ast_unlock_path(dir);
- if (ast_check_realtime("voicemail_data")) {
- snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
- snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
- ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
- }
- /* We must store the file first, before copying the message, because
- * ODBC storage does the entire copy with SQL.
- */
- if (ast_fileexists(fn, NULL, NULL) > 0) {
- STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
- }
-
- /* Are there to be more recipients of this message? */
- while (tmpptr) {
- struct ast_vm_user recipu, *recip;
- char *exten, *context;
-
- exten = strsep(&tmpptr, "&");
- context = strchr(exten, '@');
- if (context) {
- *context = '\0';
- context++;
- }
- if ((recip = find_user(&recipu, context, exten))) {
- copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
- free_user(recip);
- }
- }
- /* Notification and disposal needs to happen after the copy, though. */
- if (ast_fileexists(fn, NULL, NULL)) {
- notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
- DISPOSE(dir, msgnum);
- }
- }
- }
- }
- if (res == '0') {
- goto transfer;
- } else if (res > 0)
- res = 0;
-
- if (duration < vmminsecs)
- /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- else
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
- } else
- ast_log(LOG_WARNING, "No format for saving voicemail?\n");
-leave_vm_out:
- free_user(vmu);
-
- return res;
-}
-
-#ifndef IMAP_STORAGE
-static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
-{
- /* we know max messages, so stop process when number is hit */
-
- int x, dest;
- char sfn[PATH_MAX];
- char dfn[PATH_MAX];
-
- if (vm_lock_path(dir))
- return ERROR_LOCK_PATH;
-
- for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
- make_file(sfn, sizeof(sfn), dir, x);
- if (EXISTS(dir, x, sfn, NULL)) {
-
- if (x != dest) {
- make_file(dfn, sizeof(dfn), dir, dest);
- RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
- }
-
- dest++;
- }
- }
- ast_unlock_path(dir);
-
- return 0;
-}
-#endif
-
-static int say_and_wait(struct ast_channel *chan, int num, const char *language)
-{
- int d;
- d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
- return d;
-}
-
-static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
-{
-#ifdef IMAP_STORAGE
- /* we must use mbox(x) folder names, and copy the message there */
- /* simple. huh? */
- long res;
- char sequence[10];
-
- /* if save to Old folder, just leave in INBOX */
- if (box == 1) return 10;
- /* get the real IMAP message number for this message */
- snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
- ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
- res = mail_copy(vms->mailstream, sequence, (char *)mbox(box));
- if (res == 1) return 0;
- return 1;
-#else
- char *dir = vms->curdir;
- char *username = vms->username;
- char *context = vmu->context;
- char sfn[PATH_MAX];
- char dfn[PATH_MAX];
- char ddir[PATH_MAX];
- const char *dbox = mbox(box);
- int x, i;
- create_dirpath(ddir, sizeof(ddir), context, username, dbox);
-
- if (vm_lock_path(ddir))
- return ERROR_LOCK_PATH;
-
- x = last_message_index(vmu, ddir) + 1;
-
- if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
- x--;
- for (i = 1; i <= x; i++) {
- /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
- make_file(sfn, sizeof(sfn), ddir, i);
- make_file(dfn, sizeof(dfn), ddir, i - 1);
- if (EXISTS(ddir, i, sfn, NULL)) {
- RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
- } else
- break;
- }
- } else {
- if (x >= vmu->maxmsg) {
- ast_unlock_path(ddir);
- return -1;
- }
- }
- make_file(sfn, sizeof(sfn), dir, msg);
- make_file(dfn, sizeof(dfn), ddir, x);
- if (strcmp(sfn, dfn)) {
- COPY(dir, msg, ddir, x, username, context, sfn, dfn);
- }
- ast_unlock_path(ddir);
-#endif
- return 0;
-}
-
-static int adsi_logo(unsigned char *buf)
-{
- int bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
- return bytes;
-}
-
-static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
-{
- unsigned char buf[256];
- int bytes = 0;
- int x;
- char num[5];
-
- *useadsi = 0;
- bytes += ast_adsi_data_mode(buf + bytes);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-
- bytes = 0;
- bytes += adsi_logo(buf);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
-#ifdef DISPLAY
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
-#endif
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_data_mode(buf + bytes);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-
- if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
- return 0;
- }
-
-#ifdef DISPLAY
- /* Add a dot */
- bytes = 0;
- bytes += ast_adsi_logo(buf);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
- bytes = 0;
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
-
-#ifdef DISPLAY
- /* Add another dot */
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
-
- bytes = 0;
- /* These buttons we load but don't use yet */
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
-
-#ifdef DISPLAY
- /* Add another dot */
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
-
- bytes = 0;
- for (x = 0; x < 5; x++) {
- snprintf(num, sizeof(num), "%d", x);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
- }
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
-
-#ifdef DISPLAY
- /* Add another dot */
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
-
- if (ast_adsi_end_download(chan)) {
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
- return 0;
- }
- bytes = 0;
- bytes += ast_adsi_download_disconnect(buf + bytes);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
-
- ast_debug(1, "Done downloading scripts...\n");
-
-#ifdef DISPLAY
- /* Add last dot */
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-#endif
- ast_debug(1, "Restarting session...\n");
-
- bytes = 0;
- /* Load the session now */
- if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
- *useadsi = 1;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
- } else
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
- return 0;
-}
-
-static void adsi_begin(struct ast_channel *chan, int *useadsi)
-{
- int x;
- if (!ast_adsi_available(chan))
- return;
- x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
- if (x < 0)
- return;
- if (!x) {
- if (adsi_load_vmail(chan, useadsi)) {
- ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
- return;
- }
- } else
- *useadsi = 1;
-}
-
-static void adsi_login(struct ast_channel *chan)
-{
- unsigned char buf[256];
- int bytes = 0;
- unsigned char keys[8];
- int x;
- if (!ast_adsi_available(chan))
- return;
-
- for (x = 0; x < 8; x++)
- keys[x] = 0;
- /* Set one key for next */
- keys[3] = ADSI_KEY_APPS + 3;
-
- bytes += adsi_logo(buf + bytes);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
- bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
-
-static void adsi_password(struct ast_channel *chan)
-{
- unsigned char buf[256];
- int bytes = 0;
- unsigned char keys[8];
- int x;
- if (!ast_adsi_available(chan))
- return;
-
- for (x = 0; x < 8; x++)
- keys[x] = 0;
- /* Set one key for next */
- keys[3] = ADSI_KEY_APPS + 3;
-
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
- bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
-
-static void adsi_folders(struct ast_channel *chan, int start, char *label)
-{
- unsigned char buf[256];
- int bytes = 0;
- unsigned char keys[8];
- int x, y;
-
- if (!ast_adsi_available(chan))
- return;
-
- for (x = 0; x < 5; x++) {
- y = ADSI_KEY_APPS + 12 + start + x;
- if (y > ADSI_KEY_APPS + 12 + 4)
- y = 0;
- keys[x] = ADSI_KEY_SKT | y;
- }
- keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
- keys[6] = 0;
- keys[7] = 0;
-
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
-
-static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
-{
- int bytes = 0;
- unsigned char buf[256];
- char buf1[256], buf2[256];
- char fn2[PATH_MAX];
-
- char cid[256] = "";
- char *val;
- char *name, *num;
- char datetime[21] = "";
- FILE *f;
-
- unsigned char keys[8];
-
- int x;
-
- if (!ast_adsi_available(chan))
- return;
-
- /* Retrieve important info */
- snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
- f = fopen(fn2, "r");
- if (f) {
- while (!feof(f)) {
- fgets((char *)buf, sizeof(buf), f);
- if (!feof(f)) {
- char *stringp = NULL;
- stringp = (char *)buf;
- strsep(&stringp, "=");
- val = strsep(&stringp, "=");
- if (!ast_strlen_zero(val)) {
- if (!strcmp((char *)buf, "callerid"))
- ast_copy_string(cid, val, sizeof(cid));
- if (!strcmp((char *)buf, "origdate"))
- ast_copy_string(datetime, val, sizeof(datetime));
- }
- }
- }
- fclose(f);
- }
- /* New meaning for keys */
- for (x = 0; x < 5; x++)
- keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
- keys[6] = 0x0;
- keys[7] = 0x0;
-
- if (!vms->curmsg) {
- /* No prev key, provide "Folder" instead */
- keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
- }
- if (vms->curmsg >= vms->lastmsg) {
- /* If last message ... */
- if (vms->curmsg) {
- /* but not only message, provide "Folder" instead */
- keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- } else {
- /* Otherwise if only message, leave blank */
- keys[3] = 1;
- }
- }
-
- if (!ast_strlen_zero(cid)) {
- ast_callerid_parse(cid, &name, &num);
- if (!name)
- name = num;
- } else
- name = "Unknown Caller";
-
- /* If deleted, show "undeleted" */
-
- if (vms->deleted[vms->curmsg])
- keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
-
- /* Except "Exit" */
- keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
- snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
- strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
- snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
-
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
-
-static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
-{
- int bytes = 0;
- unsigned char buf[256];
- unsigned char keys[8];
-
- int x;
-
- if (!ast_adsi_available(chan))
- return;
-
- /* New meaning for keys */
- for (x = 0; x < 5; x++)
- keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
-
- keys[6] = 0x0;
- keys[7] = 0x0;
-
- if (!vms->curmsg) {
- /* No prev key, provide "Folder" instead */
- keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
- }
- if (vms->curmsg >= vms->lastmsg) {
- /* If last message ... */
- if (vms->curmsg) {
- /* but not only message, provide "Folder" instead */
- keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
- } else {
- /* Otherwise if only message, leave blank */
- keys[3] = 1;
- }
- }
-
- /* If deleted, show "undeleted" */
- if (vms->deleted[vms->curmsg])
- keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
-
- /* Except "Exit" */
- keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
-
-static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
-{
- unsigned char buf[256] = "";
- char buf1[256] = "", buf2[256] = "";
- int bytes = 0;
- unsigned char keys[8];
- int x;
-
- char *newm = (vms->newmessages == 1) ? "message" : "messages";
- char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
- if (!ast_adsi_available(chan))
- return;
- if (vms->newmessages) {
- snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
- if (vms->oldmessages) {
- strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
- snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
- } else {
- snprintf(buf2, sizeof(buf2), "%s.", newm);
- }
- } else if (vms->oldmessages) {
- snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
- snprintf(buf2, sizeof(buf2), "%s.", oldm);
- } else {
- strcpy(buf1, "You have no messages.");
- buf2[0] = ' ';
- buf2[1] = '\0';
- }
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-
- for (x = 0; x < 6; x++)
- keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
- keys[6] = 0;
- keys[7] = 0;
-
- /* Don't let them listen if there are none */
- if (vms->lastmsg < 0)
- keys[0] = 1;
- bytes += ast_adsi_set_keys(buf + bytes, keys);
-
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
-
-static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
-{
- unsigned char buf[256] = "";
- char buf1[256] = "", buf2[256] = "";
- int bytes = 0;
- unsigned char keys[8];
- int x;
-
- char *mess = (vms->lastmsg == 0) ? "message" : "messages";
-
- if (!ast_adsi_available(chan))
- return;
-
- /* Original command keys */
- for (x = 0; x < 6; x++)
- keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
-
- keys[6] = 0;
- keys[7] = 0;
-
- if ((vms->lastmsg + 1) < 1)
- keys[0] = 0;
-
- snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
- strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
-
- if (vms->lastmsg + 1)
- snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
- else
- strcpy(buf2, "no messages.");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
-
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-
-}
-
-/*
-static void adsi_clear(struct ast_channel *chan)
-{
- char buf[256];
- int bytes = 0;
- if (!ast_adsi_available(chan))
- return;
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
-*/
-
-static void adsi_goodbye(struct ast_channel *chan)
-{
- unsigned char buf[256];
- int bytes = 0;
-
- if (!ast_adsi_available(chan))
- return;
- bytes += adsi_logo(buf + bytes);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
-
-/*--- get_folder: Folder menu ---*/
-/* Plays "press 1 for INBOX messages" etc
- Should possibly be internationalized
- */
-static int get_folder(struct ast_channel *chan, int start)
-{
- int x;
- int d;
- char fn[PATH_MAX];
- d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
- if (d)
- return d;
- for (x = start; x < 5; x++) { /* For all folders */
- if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
- return d;
- d = ast_play_and_wait(chan, "vm-for"); /* "for" */
- if (d)
- return d;
- snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
- d = vm_play_folder_name(chan, fn);
- if (d)
- return d;
- d = ast_waitfordigit(chan, 500);
- if (d)
- return d;
- }
- d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
- if (d)
- return d;
- d = ast_waitfordigit(chan, 4000);
- return d;
-}
-
-static int get_folder2(struct ast_channel *chan, char *fn, int start)
-{
- int res = 0;
- res = ast_play_and_wait(chan, fn); /* Folder name */
- while (((res < '0') || (res > '9')) &&
- (res != '#') && (res >= 0)) {
- res = get_folder(chan, 0);
- }
- return res;
-}
-
-static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
- char *context, signed char record_gain, long *duration, struct vm_state *vms)
-{
- int cmd = 0;
- int retries = 0;
- signed char zero_gain = 0;
- struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
-
- while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
- if (cmd)
- retries = 0;
- switch (cmd) {
- case '1':
- /* prepend a message to the current message, update the metadata and return */
- {
- char msgfile[PATH_MAX];
- char textfile[PATH_MAX];
- int prepend_duration = 0;
- struct ast_config *msg_cfg;
- const char *duration_str;
-
- make_file(msgfile, sizeof(msgfile), curdir, curmsg);
- strcpy(textfile, msgfile);
- strncat(textfile, ".txt", sizeof(textfile) - 1);
- *duration = 0;
-
- /* if we can't read the message metadata, stop now */
- if (!(msg_cfg = ast_config_load(textfile, config_flags))) {
- cmd = 0;
- break;
- }
-
- if (record_gain)
- ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
-
- cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
- if (record_gain)
- ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
-
-
- if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
- *duration = atoi(duration_str);
-
- if (prepend_duration) {
- struct ast_category *msg_cat;
- /* need enough space for a maximum-length message duration */
- char duration_str[12];
-
- *duration += prepend_duration;
- msg_cat = ast_category_get(msg_cfg, "message");
- snprintf(duration_str, sizeof(duration_str), "%ld", *duration);
- if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
- config_text_file_save(textfile, msg_cfg, "app_voicemail");
- STORE(curdir, vmu->mailbox, context, curmsg, chan, vmu, vmfmts, *duration, vms);
- }
- }
-
- ast_config_destroy(msg_cfg);
-
- break;
- }
- case '2':
- cmd = 't';
- break;
- case '*':
- cmd = '*';
- break;
- default:
- cmd = ast_play_and_wait(chan, "vm-forwardoptions");
- /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-starmain");
- /* "press star to return to the main menu" */
- if (!cmd)
- cmd = ast_waitfordigit(chan, 6000);
- if (!cmd)
- retries++;
- if (retries > 3)
- cmd = 't';
- }
- }
- if (cmd == 't' || cmd == 'S')
- cmd = 0;
- return cmd;
-}
-
-static void queue_mwi_event(const char *mbox, int new, int old)
-{
- struct ast_event *event;
- char *mailbox, *context;
-
- /* Strip off @default */
- context = mailbox = ast_strdupa(mbox);
- strsep(&context, "@");
- if (ast_strlen_zero(context))
- context = "default";
-
- if (!(event = ast_event_new(AST_EVENT_MWI,
- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
- AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, new,
- AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
- AST_EVENT_IE_END))) {
- return;
- }
-
- ast_event_queue_and_cache(event,
- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR,
- AST_EVENT_IE_END);
-}
-
-static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
-{
- char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
- int newmsgs = 0, oldmsgs = 0;
- const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
- char *myserveremail = serveremail;
-
- make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
- make_file(fn, sizeof(fn), todir, msgnum);
- snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
-
- if (!ast_strlen_zero(vmu->attachfmt)) {
- if (strstr(fmt, vmu->attachfmt))
- fmt = vmu->attachfmt;
- else
- ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
- }
-
- /* Attach only the first format */
- fmt = ast_strdupa(fmt);
- stringp = fmt;
- strsep(&stringp, "|");
-
- if (!ast_strlen_zero(vmu->serveremail))
- myserveremail = vmu->serveremail;
-
- if (!ast_strlen_zero(vmu->email)) {
- int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
- if (!attach_user_voicemail)
- attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
-
- if (attach_user_voicemail)
- RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
-
- /*XXX possible imap issue, should category be NULL XXX*/
- sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
-
- if (attach_user_voicemail)
- DISPOSE(todir, msgnum);
- }
-
- if (!ast_strlen_zero(vmu->pager))
- sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
-
- if (ast_test_flag(vmu, VM_DELETE))
- DELETE(todir, msgnum, fn);
-
-#ifdef IMAP_STORAGE
- DELETE(todir, msgnum, fn);
-#endif
- /* Leave voicemail for someone */
- if (ast_app_has_voicemail(ext_context, NULL))
- ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
-
- queue_mwi_event(ext_context, newmsgs, oldmsgs);
-
- manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
- run_externnotify(vmu->context, vmu->mailbox);
- return 0;
-}
-
-static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int flag, signed char record_gain)
-{
-#ifdef IMAP_STORAGE
- BODY *body;
- char *header_content;
- char *temp;
- char todir[256];
- int todircount = 0;
- struct vm_state *dstvms;
-#endif
- char username[70] = "";
- char fn[PATH_MAX]; /* for playback of name greeting */
- char ecodes[16] = "#";
- int res = 0, cmd = 0;
- struct ast_vm_user *receiver = NULL, *vmtmp;
- AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
- char *stringp;
- const char *s;
- int saved_messages = 0, found = 0;
- int valid_extensions = 0;
- char *dir;
- int curmsg;
-
- if (vms == NULL) return -1;
- dir = vms->curdir;
- curmsg = vms->curmsg;
-
- while (!res && !valid_extensions) {
- int use_directory = 0;
- if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
- int done = 0;
- int retries = 0;
- cmd = 0;
- while ((cmd >= 0) && !done) {
- if (cmd)
- retries = 0;
- switch (cmd) {
- case '1':
- use_directory = 0;
- done = 1;
- break;
- case '2':
- use_directory = 1;
- done = 1;
- break;
- case '*':
- cmd = 't';
- done = 1;
- break;
- default:
- /* Press 1 to enter an extension press 2 to use the directory */
- cmd = ast_play_and_wait(chan, "vm-forward");
- if (!cmd)
- cmd = ast_waitfordigit(chan, 3000);
- if (!cmd)
- retries++;
- if (retries > 3)
- {
- cmd = 't';
- done = 1;
- }
-
- }
- }
- if (cmd < 0 || cmd == 't')
- break;
- }
-
- if (use_directory) {
- /* use app_directory */
-
- char old_context[sizeof(chan->context)];
- char old_exten[sizeof(chan->exten)];
- int old_priority;
- struct ast_app* app;
-
-
- app = pbx_findapp("Directory");
- if (app) {
- char vmcontext[256];
- /* make backup copies */
- memcpy(old_context, chan->context, sizeof(chan->context));
- memcpy(old_exten, chan->exten, sizeof(chan->exten));
- old_priority = chan->priority;
-
- /* call the the Directory, changes the channel */
- snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
- res = pbx_exec(chan, app, vmcontext);
-
- ast_copy_string(username, chan->exten, sizeof(username));
-
- /* restore the old context, exten, and priority */
- memcpy(chan->context, old_context, sizeof(chan->context));
- memcpy(chan->exten, old_exten, sizeof(chan->exten));
- chan->priority = old_priority;
-
- } else {
- ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
- ast_clear_flag((&globalflags), VM_DIRECFORWARD);
- }
- } else {
- /* Ask for an extension */
- res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
- if (res)
- break;
- if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
- break;
- }
-
- /* start all over if no username */
- if (ast_strlen_zero(username))
- continue;
- stringp = username;
- s = strsep(&stringp, "*");
- /* start optimistic */
- valid_extensions = 1;
- while (s) {
- /* Don't forward to ourselves but allow leaving a message for ourselves (flag == 1). find_user is going to malloc since we have a NULL as first argument */
- if ((flag == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
- AST_LIST_INSERT_HEAD(&extensions, receiver, list);
- found++;
- } else {
- valid_extensions = 0;
- break;
- }
-
- /* play name if available, else play extension number */
- snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
- RETRIEVE(fn, -1, s, receiver->context);
- if (ast_fileexists(fn, NULL, NULL) > 0) {
- res = ast_stream_and_wait(chan, fn, ecodes);
- if (res) {
- DISPOSE(fn, -1);
- return res;
- }
- } else {
- /* Dispose just in case */
- DISPOSE(fn, -1);
- res = ast_say_digit_str(chan, s, ecodes, chan->language);
- }
-
- s = strsep(&stringp, "*");
- }
- /* break from the loop of reading the extensions */
- if (valid_extensions)
- break;
- /* "I am sorry, that's not a valid extension. Please try again." */
- res = ast_play_and_wait(chan, "pbx-invalid");
- }
- /* check if we're clear to proceed */
- if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
- return res;
- if (flag==1) {
- struct leave_vm_options leave_options;
- char mailbox[AST_MAX_EXTENSION * 2 + 2];
- snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
-
- /* Send VoiceMail */
- memset(&leave_options, 0, sizeof(leave_options));
- leave_options.record_gain = record_gain;
- cmd = leave_voicemail(chan, mailbox, &leave_options);
- } else {
-
- /* Forward VoiceMail */
- long duration = 0;
-#ifdef IMAP_STORAGE
- char *myserveremail = serveremail;
- char buf[1024] = "";
- int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
-#endif
- RETRIEVE(dir, curmsg, sender->mailbox, context);
- cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, vms);
- if (!cmd) {
- AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
-#ifdef IMAP_STORAGE
- /* Need to get message content */
- ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", vms->curmsg, vms->msgArray[vms->curmsg]);
- if (!vms->msgArray[vms->curmsg]) {
- ast_log(LOG_WARNING, "Trying to access unknown message\n");
- return -1;
- }
-
- /* This will only work for new messages... */
- header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
- /* empty string means no valid header */
- if (ast_strlen_zero(header_content)) {
- ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[vms->curmsg]);
- return -1;
- }
- /* Get header info needed by sendmail */
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))))
- duration = atoi(temp);
- else
- duration = 0;
-
- /* Attach only the first format */
- if ((fmt = ast_strdupa(fmt))) {
- stringp = fmt;
- strsep(&stringp, "|");
- } else {
- ast_log(LOG_ERROR, "audio format not set. Default to WAV\n");
- fmt = "WAV";
- }
- if (!strcasecmp(fmt, "wav49"))
- fmt = "WAV";
- ast_debug(3, "**** format set to %s, vmfmts set to %s\n", fmt, vmfmts);
- /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
- /* if (!ast_strlen_zero(fmt)) { */
- snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
- make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
- ast_debug(3, "Before mail_fetchstructure, message number is %ld, filename is:%s\n", vms->msgArray[vms->curmsg], vms->fn);
- /*mail_fetchstructure (mailstream, vmArray[0], &body); */
- mail_fetchstructure (vms->mailstream, vms->msgArray[vms->curmsg], &body);
- save_body(body, vms, "3", "gsm");
- /* should not assume "fmt" here! */
- save_body(body, vms, "2", fmt);
-
- /* get destination mailbox */
- dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, 0);
- if (dstvms) {
- init_mailstream(dstvms, 0);
- if (!dstvms->mailstream) {
- ast_log(LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
- } else {
- STORE(todir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
- run_externnotify(vmtmp->context, vmtmp->mailbox);
- }
- } else {
- ast_log(LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
- }
-
- if (!ast_strlen_zero(vmtmp->serveremail))
- myserveremail = vmtmp->serveremail;
- attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
- /* NULL category for IMAP storage */
- sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vms->fn, fmt, duration, attach_user_voicemail, chan, NULL);
-
-#else
- copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir);
-#endif
- saved_messages++;
- AST_LIST_REMOVE_CURRENT(list);
- free_user(vmtmp);
- if (res)
- break;
- }
- AST_LIST_TRAVERSE_SAFE_END;
- if (saved_messages > 0) {
- /* give confirmation that the message was saved */
- /* commented out since we can't forward batches yet
- if (saved_messages == 1)
- res = ast_play_and_wait(chan, "vm-message");
- else
- res = ast_play_and_wait(chan, "vm-messages");
- if (!res)
- res = ast_play_and_wait(chan, "vm-saved"); */
- res = ast_play_and_wait(chan, "vm-msgsaved");
- }
- }
- }
-
- /* If anything failed above, we still have this list to free */
- while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
- free_user(vmtmp);
- return res ? res : cmd;
-}
-
-static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
-{
- int res;
- if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
- ast_log(LOG_WARNING, "Unable to play message %s\n", file);
- return res;
-}
-
-static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
-{
- return ast_control_streamfile(chan, file, listen_control_forward_key, listen_control_reverse_key, listen_control_stop_key, listen_control_pause_key, listen_control_restart_key, skipms, NULL);
-}
-
-static int play_message_category(struct ast_channel *chan, const char *category)
-{
- int res = 0;
-
- if (!ast_strlen_zero(category))
- res = ast_play_and_wait(chan, category);
-
- if (res) {
- ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
- res = 0;
- }
-
- return res;
-}
-
-static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
-{
- int res = 0;
- struct vm_zone *the_zone = NULL;
- time_t t;
-
- if (ast_get_time_t(origtime, &t, 0, NULL)) {
- ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
- return 0;
- }
-
- /* Does this user have a timezone specified? */
- if (!ast_strlen_zero(vmu->zonetag)) {
- /* Find the zone in the list */
- struct vm_zone *z;
- AST_LIST_LOCK(&zones);
- AST_LIST_TRAVERSE(&zones, z, list) {
- if (!strcmp(z->name, vmu->zonetag)) {
- the_zone = z;
- break;
- }
- }
- AST_LIST_UNLOCK(&zones);
- }
-
-/* No internal variable parsing for now, so we'll comment it out for the time being */
-#if 0
- /* Set the DIFF_* variables */
- ast_localtime(&t, &time_now, NULL);
- tv_now = ast_tvnow();
- ast_localtime(&tv_now, &time_then, NULL);
-
- /* Day difference */
- if (time_now.tm_year == time_then.tm_year)
- snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
- else
- snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
- pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
-
- /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
-#endif
- if (the_zone)
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
- else if (!strcasecmp(chan->language, "pl")) /* POLISH syntax */
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
- else if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
- else if (!strcasecmp(chan->language, "no")) /* NORWEGIAN syntax */
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
- else if (!strcasecmp(chan->language, "de")) /* GERMAN syntax */
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
- else if (!strcasecmp(chan->language, "nl")) /* DUTCH syntax */
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
- else if (!strcasecmp(chan->language, "it")) /* ITALIAN syntax */
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
- else if (!strcasecmp(chan->language, "gr"))
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
- else if (!strcasecmp(chan->language, "pt_BR"))
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
- else if (!strcasecmp(chan->language, "tw")) /* CHINESE (Taiwan) syntax */
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
- else
- res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
-#if 0
- pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
-#endif
- return res;
-}
-
-
-
-static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
-{
- int res = 0;
- int i;
- char *callerid, *name;
- char prefile[PATH_MAX] = "";
-
-
- /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
- /* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
- if ((cid == NULL)||(context == NULL))
- return res;
-
- /* Strip off caller ID number from name */
- ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
- ast_callerid_parse(cid, &name, &callerid);
- if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
- /* Check for internal contexts and only */
- /* say extension when the call didn't come from an internal context in the list */
- for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++) {
- ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
- if ((strcmp(cidinternalcontexts[i], context) == 0))
- break;
- }
- if (i != MAX_NUM_CID_CONTEXTS) { /* internal context? */
- if (!res) {
- snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
- if (!ast_strlen_zero(prefile)) {
- /* See if we can find a recorded name for this person instead of their extension number */
- if (ast_fileexists(prefile, NULL, NULL) > 0) {
- ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
- if (!callback)
- res = wait_file2(chan, vms, "vm-from");
- res = ast_stream_and_wait(chan, prefile, "");
- } else {
- ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
- /* BB: Say "from extension" as one saying to sound smoother */
- if (!callback)
- res = wait_file2(chan, vms, "vm-from-extension");
- res = ast_say_digit_str(chan, callerid, "", chan->language);
- }
- }
- }
- }
-
- else if (!res) {
- ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
- /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
- if (!callback)
- res = wait_file2(chan, vms, "vm-from-phonenumber");
- res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
- }
- } else {
- /* Number unknown */
- ast_debug(1, "VM-CID: From an unknown number\n");
- /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
- res = wait_file2(chan, vms, "vm-unknown-caller");
- }
- return res;
-}
-
-static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
-{
- int res = 0;
- int durationm;
- int durations;
- /* Verify that we have a duration for the message */
- if (duration == NULL)
- return res;
-
- /* Convert from seconds to minutes */
- durations = atoi(duration);
- durationm = durations / 60;
-
- ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
-
- if ((!res) && (durationm >= minduration)) {
- res = wait_file2(chan, vms, "vm-duration");
-
- /* POLISH syntax */
- if (!strcasecmp(chan->language, "pl")) {
- div_t num = div(durationm, 10);
-
- if (durationm == 1) {
- res = ast_play_and_wait(chan, "digits/1z");
- res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
- } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
- if (num.rem == 2) {
- if (!num.quot) {
- res = ast_play_and_wait(chan, "digits/2-ie");
- } else {
- res = say_and_wait(chan, durationm - 2 , chan->language);
- res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
- }
- } else {
- res = say_and_wait(chan, durationm, chan->language);
- }
- res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
- } else {
- res = say_and_wait(chan, durationm, chan->language);
- res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
- }
- /* DEFAULT syntax */
- } else {
- res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
- res = wait_file2(chan, vms, "vm-minutes");
- }
- }
- return res;
-}
-
-#ifdef IMAP_STORAGE
-static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
-{
- BODY *body;
- char *header_content;
- char cid[256], cidN[256];
- char context[256];
- char origtime[32];
- char duration[16];
- char category[32];
- char todir[PATH_MAX];
- int res = 0;
- char *attachedfilefmt;
- char *temp;
- char buf[1024];
-
- vms->starting = 0;
- ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", vms->curmsg, vms->msgArray[vms->curmsg]);
-
- if (!vms->msgArray[vms->curmsg]) {
- ast_log(LOG_WARNING, "Trying to access unknown message\n");
- return -1;
- }
-
- /* This will only work for new messages... */
- header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
- /* empty string means no valid header */
- if (ast_strlen_zero(header_content)) {
- ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[vms->curmsg]);
- return -1;
- }
- snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
- make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
-
- mail_fetchstructure (vms->mailstream, vms->msgArray[vms->curmsg], &body);
-
- /* We have the body, now we extract the file name of the first attachment. */
- if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
- attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
- } else {
- ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
- return -1;
- }
-
-
- /* Find the format of the attached file */
-
- strsep(&attachedfilefmt, ".");
- if (!attachedfilefmt) {
- ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
- return -1;
- }
- save_body(body, vms, "2", attachedfilefmt);
-
- adsi_message(chan, vms);
- if (!vms->curmsg)
- res = wait_file2(chan, vms, "vm-first"); /* "First" */
- else if (vms->curmsg == vms->lastmsg)
- res = wait_file2(chan, vms, "vm-last"); /* "last" */
-
- if (!res) {
- res = wait_file2(chan, vms, "vm-message"); /* "message" */
- if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
- if (!res)
- res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
- }
- }
-
- /* Get info from headers!! */
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))))
- ast_copy_string(cidN, temp, sizeof(cidN));
- else
- cidN[0] = '\0';
-
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))))
- snprintf(cid, sizeof(cid), "\"%s\" <%s>", cidN, temp);
- else
- cid[0] = '\0';
-
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))))
- ast_copy_string(context, temp, sizeof(context));
- else
- context[0] = '\0';
-
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))))
- ast_copy_string(origtime, temp, sizeof(origtime));
- else
- origtime[0] = '\0';
-
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))))
- ast_copy_string(duration, temp, sizeof(duration));
- else
- duration[0] = '\0';
-
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))))
- ast_copy_string(category, temp, sizeof(category));
- else
- category[0] = '\0';
-
- /*if (!strncasecmp("macro", context, 5)) Macro names in contexts are useless for our needs */
- /* context = ast_variable_retrieve(msg_cfg, "message", "macrocontext"); */
- if (res == '1')
- res = 0;
-
- if ((!res) && !ast_strlen_zero(category)) {
- res = play_message_category(chan, category);
- }
-
- if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)) && origtime[0] != '\0')
- res = play_message_datetime(chan, vmu, origtime, "IMAP_STORAGE");
- if ((!res) && (ast_test_flag(vmu, VM_SAYCID)) && cid[0] != '\0' && context[0] != '\0')
- res = play_message_callerid(chan, vms, cid, context, 0);
-
- if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)) && duration[0] != '\0')
- res = play_message_duration(chan, vms, duration, vmu->saydurationm);
-
- /* Allow pressing '1' to skip envelope / callerid */
- /* if (res == '1')
- res = 0;
- */
- /*ast_config_destroy(msg_cfg);*/
- res = 0;
-
- if (!res) {
- vms->heard[vms->curmsg] = 1;
- res = wait_file(chan, vms, vms->fn);
- }
- DISPOSE(vms->curdir, vms->curmsg);
- DELETE(0, 0, vms->fn);
- return res;
-}
-#else
-static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
-{
- int res = 0;
- char filename[256], *cid;
- const char *origtime, *context, *category, *duration;
- struct ast_config *msg_cfg;
- struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
-
- vms->starting = 0;
- make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
- adsi_message(chan, vms);
- if (!vms->curmsg)
- res = wait_file2(chan, vms, "vm-first"); /* "First" */
- else if (vms->curmsg == vms->lastmsg)
- res = wait_file2(chan, vms, "vm-last"); /* "last" */
- if (!res) {
- /* POLISH syntax */
- if (!strcasecmp(chan->language, "pl")) {
- if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
- int ten, one;
- char nextmsg[256];
- ten = (vms->curmsg + 1) / 10;
- one = (vms->curmsg + 1) % 10;
-
- if (vms->curmsg < 20) {
- snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
- res = wait_file2(chan, vms, nextmsg);
- } else {
- snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
- res = wait_file2(chan, vms, nextmsg);
- if (one > 0) {
- if (!res) {
- snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
- res = wait_file2(chan, vms, nextmsg);
- }
- }
- }
- }
- if (!res)
- res = wait_file2(chan, vms, "vm-message");
- } else {
- if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
- res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
- else /* DEFAULT syntax */
- res = wait_file2(chan, vms, "vm-message");
- if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
- if (!res)
- res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
- }
- }
- }
-
- /* Retrieve info from VM attribute file */
- make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
- snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
- RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
- msg_cfg = ast_config_load(filename, config_flags);
- if (!msg_cfg) {
- ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
- return 0;
- }
-
- if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
- ast_log(LOG_WARNING, "No origtime?!\n");
- DISPOSE(vms->curdir, vms->curmsg);
- ast_config_destroy(msg_cfg);
- return 0;
- }
-
- cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
- duration = ast_variable_retrieve(msg_cfg, "message", "duration");
- category = ast_variable_retrieve(msg_cfg, "message", "category");
-
- context = ast_variable_retrieve(msg_cfg, "message", "context");
- if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
- context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
- if (!res)
- res = play_message_category(chan, category);
- if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
- res = play_message_datetime(chan, vmu, origtime, filename);
- if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
- res = play_message_callerid(chan, vms, cid, context, 0);
- if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
- res = play_message_duration(chan, vms, duration, vmu->saydurationm);
- /* Allow pressing '1' to skip envelope / callerid */
- if (res == '1')
- res = 0;
- ast_config_destroy(msg_cfg);
-
- if (!res) {
- make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
- vms->heard[vms->curmsg] = 1;
- if ((res = wait_file(chan, vms, vms->fn)) < 0) {
- ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
- res = 0;
- }
- }
- DISPOSE(vms->curdir, vms->curmsg);
- return res;
-}
-#endif
-
-#ifdef IMAP_STORAGE
-static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
-{
- char tmp[256], *t = tmp;
- size_t left = sizeof(tmp);
-
- if (box == OLD_FOLDER) {
- ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
- snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(1));
- } else {
- ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
- snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
- }
-
- /* Build up server information */
- ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
-
- /* Add authentication user if present */
- if (!ast_strlen_zero(authuser))
- ast_build_string(&t, &left, "/authuser=%s", authuser);
-
- /* Add flags if present */
- if (!ast_strlen_zero(imapflags))
- ast_build_string(&t, &left, "/%s", imapflags);
-
- /* End with username */
- ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
-
- if (box == NEW_FOLDER || box == OLD_FOLDER)
- snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
- else if (box == GREETINGS_FOLDER)
- sprintf(spec, "%s%s", tmp, greetingfolder);
- else
- snprintf(spec, len, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
-}
-
-static int init_mailstream(struct vm_state *vms, int box)
-{
- MAILSTREAM *stream = NIL;
- long debug;
- char tmp[256];
-
- if (!vms) {
- ast_log(LOG_ERROR, "vm_state is NULL!\n");
- return -1;
- }
- ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
- if (vms->mailstream == NIL || !vms->mailstream) {
- ast_debug(1, "mailstream not set.\n");
- } else {
- stream = vms->mailstream;
- }
- /* debug = T; user wants protocol telemetry? */
- debug = NIL; /* NO protocol telemetry? */
-
- if (delimiter == '\0') { /* did not probe the server yet */
- char *cp;
-#ifdef USE_SYSTEM_IMAP
-#include <imap/linkage.c>
-#else
-#include "linkage.c"
-#endif
- /* Connect to mailbox to get mailstream so we can get delimiter */
- imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
- stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
- if (stream == NIL) {
- ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
- return -1;
- }
- get_mailbox_delimiter(stream);
- /* update delimiter in imapfolder */
- for (cp = imapfolder; *cp; cp++) {
- if (*cp == '/')
- *cp = delimiter;
- }
- }
- /* Now connect to the target folder */
- imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
- ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
- vms->mailstream = mail_open(stream, tmp, debug ? OP_DEBUG : NIL);
- if (vms->mailstream == NIL) {
- return -1;
- } else {
- return 0;
- }
-}
-
-static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
-{
- SEARCHPGM *pgm;
- SEARCHHEADER *hdr;
- int ret;
-
- ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
- ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
-
- if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
- ast_log(LOG_ERROR, "Could not initialize mailstream\n");
- return -1;
- }
-
- /* Check Quota */
- if (box == 0) {
- ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
- check_quota(vms, (char *)mbox(box));
- }
-
- pgm = mail_newsearchpgm();
-
- /* Check IMAP folder for Asterisk messages only... */
- hdr = mail_newsearchheader("X-Asterisk-VM-Extension", vmu->mailbox);
- pgm->header = hdr;
- pgm->deleted = 0;
- pgm->undeleted = 1;
-
- /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
- if (box == NEW_FOLDER) {
- pgm->unseen = 1;
- pgm->seen = 0;
- } else if (box == OLD_FOLDER) {
- pgm->seen = 1;
- pgm->unseen = 0;
- }
-
- ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
-
- vms->vmArrayIndex = 0;
- mail_search_full (vms->mailstream, NULL, pgm, NIL);
- vms->lastmsg = vms->vmArrayIndex - 1;
- mail_free_searchpgm(&pgm);
-
- return 0;
-}
-
-static int imap_remove_file(char *dir, int msgnum)
-{
- char fn[PATH_MAX];
- char full_fn[PATH_MAX];
- char msgnums[80];
-
- if (msgnum > -1) {
- snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
- make_file(fn, sizeof(fn), dir, msgnum);
- } else
- ast_copy_string(fn, dir, sizeof(fn));
-
- if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
- ast_filedelete(fn, NULL);
- snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
- unlink(full_fn);
- }
- return 0;
-}
-
-static int imap_retrieve_file (char *dir, int msgnum, const char *mailbox, char *context)
-{
- struct ast_vm_user *vmu;
- struct vm_state *vms_p;
- char *file, *filename;
- char *attachment;
- /*char *mb, *cur;*/
- int ret = 0, i;
- BODY *body;
-
- /* This function is only used for retrieval of IMAP greetings
- * regular messages are not retrieved this way, nor are greetings
- * if they are stored locally*/
- if (msgnum > -1 || !imapgreetings) {
- return 0;
- } else {
- file = strrchr(ast_strdupa(dir), '/');
- if (file)
- *file++ = '\0';
- else {
- ast_debug(1, "Failed to procure file name from directory passed.\n");
- return -1;
- }
- }
- /* We have to get the user before we can open the stream! */
- /* ast_log(LOG_DEBUG, "Before find_user, context is %s and mailbox is %s\n", context, mailbox); */
- vmu = find_user(NULL, context, mailbox);
- if (!vmu) {
- ast_log(LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
- return -1;
- } else {
- /* No IMAP account available */
- if (vmu->imapuser[0] == '\0') {
- ast_log(LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
- free_user(vmu);
- return -1;
- }
- }
-
- /* check if someone is accessing this box right now... */
- vms_p = get_vm_state_by_mailbox(mailbox, 0);
- if (!vms_p) {
- ast_log(LOG_ERROR, "Voicemail state not found!\n");
- return -1;
- }
-
- ret = init_mailstream(vms_p, GREETINGS_FOLDER);
- if (!vms_p->mailstream) {
- ast_log(LOG_ERROR, "IMAP mailstream is NULL\n");
- free_user(vmu);
- return -1;
- }
-
- for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
- mail_fetchstructure(vms_p->mailstream, i + 1, &body);
- /* We have the body, now we extract the file name of the first attachment. */
- if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
- attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
- } else {
- ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
- return -1;
- }
- filename = strsep(&attachment, ".");
- if (!strcmp(filename, file)) {
- ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
- vms_p->msgArray[vms_p->curmsg] = i + 1;
- save_body(body, vms_p, "2", attachment);
- free_user(vmu);
- return 0;
- }
- }
-
- free_user(vmu);
- return -1;
-}
-
-static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
-{
- char *file, *filename;
- char *attachment;
- char arg[10];
- int i;
- BODY* body;
-
-
- file = strrchr(ast_strdupa(dir), '/');
- if (file)
- *file++ = '\0';
- else {
- ast_log(LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
- return -1;
- }
-
- for (i = 0; i < vms->mailstream->nmsgs; i++) {
- mail_fetchstructure(vms->mailstream, i + 1, &body);
- /* We have the body, now we extract the file name of the first attachment. */
- if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
- attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
- } else {
- ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
- return -1;
- }
- filename = strsep(&attachment, ".");
- if (!strcmp(filename, file)) {
- sprintf (arg, "%d", i + 1);
- mail_setflag(vms->mailstream, arg, "\\DELETED");
- }
- }
- mail_expunge(vms->mailstream);
- return 0;
-}
-
-#else
-static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
-{
- int res = 0;
- int count_msg, last_msg;
-
- ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
-
- /* Rename the member vmbox HERE so that we don't try to return before
- * we know what's going on.
- */
- snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
-
- /* Faster to make the directory than to check if it exists. */
- create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
-
- count_msg = count_messages(vmu, vms->curdir);
- if (count_msg < 0)
- return count_msg;
- else
- vms->lastmsg = count_msg - 1;
-
- /*
- The following test is needed in case sequencing gets messed up.
- There appears to be more than one way to mess up sequence, so
- we will not try to find all of the root causes--just fix it when
- detected.
- */
-
- if (vm_lock_path(vms->curdir)) {
- ast_log(LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
- return -1;
- }
-
- last_msg = last_message_index(vmu, vms->curdir);
- ast_unlock_path(vms->curdir);
-
- if (last_msg < 0)
- return last_msg;
- else if (vms->lastmsg != last_msg)
- {
- ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
- res = resequence_mailbox(vmu, vms->curdir);
- if (res)
- return res;
- }
-
- return 0;
-}
-#endif
-
-static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
-{
- int x = 0;
-#ifndef IMAP_STORAGE
- int res = 0, nummsg;
-#endif
-
- if (vms->lastmsg <= -1)
- goto done;
-
- vms->curmsg = -1;
-#ifndef IMAP_STORAGE
- /* Get the deleted messages fixed */
- if (vm_lock_path(vms->curdir))
- return ERROR_LOCK_PATH;
-
- for (x = 0; x < vmu->maxmsg; x++) {
- if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
- /* Save this message. It's not in INBOX or hasn't been heard */
- make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
- if (!EXISTS(vms->curdir, x, vms->fn, NULL))
- break;
- vms->curmsg++;
- make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
- if (strcmp(vms->fn, vms->fn2)) {
- RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
- }
- } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
- /* Move to old folder before deleting */
- res = save_to_folder(vmu, vms, x, 1);
- if (res == ERROR_LOCK_PATH) {
- /* If save failed do not delete the message */
- vms->deleted[x] = 0;
- vms->heard[x] = 0;
- --x;
- }
- } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
- /* Move to deleted folder */
- res = save_to_folder(vmu, vms, x, 10);
- if (res == ERROR_LOCK_PATH) {
- /* If save failed do not delete the message */
- vms->deleted[x] = 0;
- vms->heard[x] = 0;
- --x;
- }
- } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
- /* If realtime storage enabled - we should explicitly delete this message,
- cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
- make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
- if (EXISTS(vms->curdir, x, vms->fn, NULL))
- DELETE(vms->curdir, x, vms->fn);
- }
- }
-
- /* Delete ALL remaining messages */
- nummsg = x - 1;
- for (x = vms->curmsg + 1; x <= nummsg; x++) {
- make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
- if (EXISTS(vms->curdir, x, vms->fn, NULL))
- DELETE(vms->curdir, x, vms->fn);
- }
- ast_unlock_path(vms->curdir);
-#else
- if (vms->deleted) {
- for (x = 0; x < vmu->maxmsg; x++) {
- if (vms->deleted[x]) {
- ast_debug(3, "IMAP delete of %d\n", x);
- IMAP_DELETE(vms->curdir, x, vms->fn, vms);
- }
- }
- }
-#endif
-
-done:
- if (vms->deleted)
- memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
- if (vms->heard)
- memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
-
- return 0;
-}
-
-/* In Greek even though we CAN use a syntax like "friends messages"
- * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
- * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
- * syntax for the above three categories which is more elegant.
- */
-
-static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
-{
- int cmd;
- char *buf;
-
- buf = alloca(strlen(mbox) + 2);
- strcpy(buf, mbox);
- strcat(buf, "s");
-
- if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
- cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
- return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
- } else {
- cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
- return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
- }
-}
-
-static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
-{
- int cmd;
-
- if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
- if (!strcasecmp(mbox, "vm-INBOX"))
- cmd = ast_play_and_wait(chan, "vm-new-e");
- else
- cmd = ast_play_and_wait(chan, "vm-old-e");
- return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
- } else {
- cmd = ast_play_and_wait(chan, "vm-messages");
- return cmd ? cmd : ast_play_and_wait(chan, mbox);
- }
-}
-
-static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
-{
- int cmd;
-
- if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")) {
- cmd = ast_play_and_wait(chan, "vm-messages");
- return cmd ? cmd : ast_play_and_wait(chan, mbox);
- } else {
- cmd = ast_play_and_wait(chan, mbox);
- return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
- }
-}
-
-static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
-{
- int cmd;
-
- if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
- cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
- return cmd ? cmd : ast_play_and_wait(chan, mbox);
- } else if (!strcasecmp(chan->language, "gr")) {
- return vm_play_folder_name_gr(chan, mbox);
- } else if (!strcasecmp(chan->language, "pl")) {
- return vm_play_folder_name_pl(chan, mbox);
- } else if (!strcasecmp(chan->language, "ua")) { /* Ukrainian syntax */
- return vm_play_folder_name_ua(chan, mbox);
- } else { /* Default English */
- cmd = ast_play_and_wait(chan, mbox);
- return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
- }
-}
-
-/* GREEK SYNTAX
- In greek the plural for old/new is
- different so we need the following files
- We also need vm-denExeteMynhmata because
- this syntax is different.
-
- -> vm-Olds.wav : "Palia"
- -> vm-INBOXs.wav : "Nea"
- -> vm-denExeteMynhmata : "den exete mynhmata"
-*/
-
-
-static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
-{
- int res = 0;
-
- if (vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res)
- res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
- if (!res) {
- if ((vms->newmessages == 1)) {
- res = ast_play_and_wait(chan, "vm-INBOX");
- if (!res)
- res = ast_play_and_wait(chan, "vm-message");
- } else {
- res = ast_play_and_wait(chan, "vm-INBOXs");
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- }
- }
- } else if (vms->oldmessages) {
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res)
- res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
- if ((vms->oldmessages == 1)) {
- res = ast_play_and_wait(chan, "vm-Old");
- if (!res)
- res = ast_play_and_wait(chan, "vm-message");
- } else {
- res = ast_play_and_wait(chan, "vm-Olds");
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- }
- } else if (!vms->oldmessages && !vms->newmessages)
- res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
- return res;
-}
-
-/* Default English syntax */
-static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
-{
- int res;
-
- /* Introduce messages they have */
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res) {
- if (vms->newmessages) {
- res = say_and_wait(chan, vms->newmessages, chan->language);
- if (!res)
- res = ast_play_and_wait(chan, "vm-INBOX");
- if (vms->oldmessages && !res)
- res = ast_play_and_wait(chan, "vm-and");
- else if (!res) {
- if ((vms->newmessages == 1))
- res = ast_play_and_wait(chan, "vm-message");
- else
- res = ast_play_and_wait(chan, "vm-messages");
- }
-
- }
- if (!res && vms->oldmessages) {
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- if (!res)
- res = ast_play_and_wait(chan, "vm-Old");
- if (!res) {
- if (vms->oldmessages == 1)
- res = ast_play_and_wait(chan, "vm-message");
- else
- res = ast_play_and_wait(chan, "vm-messages");
- }
- }
- if (!res) {
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-no");
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- }
- }
- }
- return res;
-}
-
-/* ITALIAN syntax */
-static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
-{
- /* Introduce messages they have */
- int res;
- if (!vms->oldmessages && !vms->newmessages)
- res = ast_play_and_wait(chan, "vm-no") ||
- ast_play_and_wait(chan, "vm-message");
- else
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res && vms->newmessages) {
- res = (vms->newmessages == 1) ?
- ast_play_and_wait(chan, "digits/un") ||
- ast_play_and_wait(chan, "vm-nuovo") ||
- ast_play_and_wait(chan, "vm-message") :
- /* 2 or more new messages */
- say_and_wait(chan, vms->newmessages, chan->language) ||
- ast_play_and_wait(chan, "vm-nuovi") ||
- ast_play_and_wait(chan, "vm-messages");
- if (!res && vms->oldmessages)
- res = ast_play_and_wait(chan, "vm-and");
- }
- if (!res && vms->oldmessages) {
- res = (vms->oldmessages == 1) ?
- ast_play_and_wait(chan, "digits/un") ||
- ast_play_and_wait(chan, "vm-vecchio") ||
- ast_play_and_wait(chan, "vm-message") :
- /* 2 or more old messages */
- say_and_wait(chan, vms->oldmessages, chan->language) ||
- ast_play_and_wait(chan, "vm-vecchi") ||
- ast_play_and_wait(chan, "vm-messages");
- }
- return res ? -1 : 0;
-}
-
-/* POLISH syntax */
-static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
-{
- /* Introduce messages they have */
- int res;
- div_t num;
-
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-no");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- return res;
- } else {
- res = ast_play_and_wait(chan, "vm-youhave");
- }
-
- if (vms->newmessages) {
- num = div(vms->newmessages, 10);
- if (vms->newmessages == 1) {
- res = ast_play_and_wait(chan, "digits/1-a");
- res = res ? res : ast_play_and_wait(chan, "vm-new-a");
- res = res ? res : ast_play_and_wait(chan, "vm-message");
- } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
- if (num.rem == 2) {
- if (!num.quot) {
- res = ast_play_and_wait(chan, "digits/2-ie");
- } else {
- res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
- res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
- }
- } else {
- res = say_and_wait(chan, vms->newmessages, chan->language);
- }
- res = res ? res : ast_play_and_wait(chan, "vm-new-e");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- } else {
- res = say_and_wait(chan, vms->newmessages, chan->language);
- res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- }
- if (!res && vms->oldmessages)
- res = ast_play_and_wait(chan, "vm-and");
- }
- if (!res && vms->oldmessages) {
- num = div(vms->oldmessages, 10);
- if (vms->oldmessages == 1) {
- res = ast_play_and_wait(chan, "digits/1-a");
- res = res ? res : ast_play_and_wait(chan, "vm-old-a");
- res = res ? res : ast_play_and_wait(chan, "vm-message");
- } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
- if (num.rem == 2) {
- if (!num.quot) {
- res = ast_play_and_wait(chan, "digits/2-ie");
- } else {
- res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
- res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
- }
- } else {
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- }
- res = res ? res : ast_play_and_wait(chan, "vm-old-e");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- } else {
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- }
- }
-
- return res;
-}
-
-/* SWEDISH syntax */
-static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
-{
- /* Introduce messages they have */
- int res;
-
- res = ast_play_and_wait(chan, "vm-youhave");
- if (res)
- return res;
-
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-no");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- return res;
- }
-
- if (vms->newmessages) {
- if ((vms->newmessages == 1)) {
- res = ast_play_and_wait(chan, "digits/ett");
- res = res ? res : ast_play_and_wait(chan, "vm-nytt");
- res = res ? res : ast_play_and_wait(chan, "vm-message");
- } else {
- res = say_and_wait(chan, vms->newmessages, chan->language);
- res = res ? res : ast_play_and_wait(chan, "vm-nya");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- }
- if (!res && vms->oldmessages)
- res = ast_play_and_wait(chan, "vm-and");
- }
- if (!res && vms->oldmessages) {
- if (vms->oldmessages == 1) {
- res = ast_play_and_wait(chan, "digits/ett");
- res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
- res = res ? res : ast_play_and_wait(chan, "vm-message");
- } else {
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- res = res ? res : ast_play_and_wait(chan, "vm-gamla");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- }
- }
-
- return res;
-}
-
-/* NORWEGIAN syntax */
-static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
-{
- /* Introduce messages they have */
- int res;
-
- res = ast_play_and_wait(chan, "vm-youhave");
- if (res)
- return res;
-
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-no");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- return res;
- }
-
- if (vms->newmessages) {
- if ((vms->newmessages == 1)) {
- res = ast_play_and_wait(chan, "digits/1");
- res = res ? res : ast_play_and_wait(chan, "vm-ny");
- res = res ? res : ast_play_and_wait(chan, "vm-message");
- } else {
- res = say_and_wait(chan, vms->newmessages, chan->language);
- res = res ? res : ast_play_and_wait(chan, "vm-nye");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- }
- if (!res && vms->oldmessages)
- res = ast_play_and_wait(chan, "vm-and");
- }
- if (!res && vms->oldmessages) {
- if (vms->oldmessages == 1) {
- res = ast_play_and_wait(chan, "digits/1");
- res = res ? res : ast_play_and_wait(chan, "vm-gamel");
- res = res ? res : ast_play_and_wait(chan, "vm-message");
- } else {
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- res = res ? res : ast_play_and_wait(chan, "vm-gamle");
- res = res ? res : ast_play_and_wait(chan, "vm-messages");
- }
- }
-
- return res;
-}
-
-/* GERMAN syntax */
-static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
-{
- /* Introduce messages they have */
- int res;
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res) {
- if (vms->newmessages) {
- if ((vms->newmessages == 1))
- res = ast_play_and_wait(chan, "digits/1F");
- else
- res = say_and_wait(chan, vms->newmessages, chan->language);
- if (!res)
- res = ast_play_and_wait(chan, "vm-INBOX");
- if (vms->oldmessages && !res)
- res = ast_play_and_wait(chan, "vm-and");
- else if (!res) {
- if ((vms->newmessages == 1))
- res = ast_play_and_wait(chan, "vm-message");
- else
- res = ast_play_and_wait(chan, "vm-messages");
- }
-
- }
- if (!res && vms->oldmessages) {
- if (vms->oldmessages == 1)
- res = ast_play_and_wait(chan, "digits/1F");
- else
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- if (!res)
- res = ast_play_and_wait(chan, "vm-Old");
- if (!res) {
- if (vms->oldmessages == 1)
- res = ast_play_and_wait(chan, "vm-message");
- else
- res = ast_play_and_wait(chan, "vm-messages");
- }
- }
- if (!res) {
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-no");
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- }
- }
- }
- return res;
-}
-
-/* SPANISH syntax */
-static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
-{
- /* Introduce messages they have */
- int res;
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-youhaveno");
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- } else {
- res = ast_play_and_wait(chan, "vm-youhave");
- }
- if (!res) {
- if (vms->newmessages) {
- if (!res) {
- if ((vms->newmessages == 1)) {
- res = ast_play_and_wait(chan, "digits/1M");
- if (!res)
- res = ast_play_and_wait(chan, "vm-message");
- if (!res)
- res = ast_play_and_wait(chan, "vm-INBOXs");
- } else {
- res = say_and_wait(chan, vms->newmessages, chan->language);
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- if (!res)
- res = ast_play_and_wait(chan, "vm-INBOX");
- }
- }
- if (vms->oldmessages && !res)
- res = ast_play_and_wait(chan, "vm-and");
- }
- if (vms->oldmessages) {
- if (!res) {
- if (vms->oldmessages == 1) {
- res = ast_play_and_wait(chan, "digits/1M");
- if (!res)
- res = ast_play_and_wait(chan, "vm-message");
- if (!res)
- res = ast_play_and_wait(chan, "vm-Olds");
- } else {
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- if (!res)
- res = ast_play_and_wait(chan, "vm-Old");
- }
- }
- }
- }
-return res;
-}
-
-/* BRAZILIAN PORTUGUESE syntax */
-static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
- /* Introduce messages they have */
- int res;
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-nomessages");
- return res;
- }
- else {
- res = ast_play_and_wait(chan, "vm-youhave");
- }
- if (vms->newmessages) {
- if (!res)
- res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
- if ((vms->newmessages == 1)) {
- if (!res)
- res = ast_play_and_wait(chan, "vm-message");
- if (!res)
- res = ast_play_and_wait(chan, "vm-INBOXs");
- }
- else {
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- if (!res)
- res = ast_play_and_wait(chan, "vm-INBOX");
- }
- if (vms->oldmessages && !res)
- res = ast_play_and_wait(chan, "vm-and");
- }
- if (vms->oldmessages) {
- if (!res)
- res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
- if (vms->oldmessages == 1) {
- if (!res)
- res = ast_play_and_wait(chan, "vm-message");
- if (!res)
- res = ast_play_and_wait(chan, "vm-Olds");
- }
- else {
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- if (!res)
- res = ast_play_and_wait(chan, "vm-Old");
- }
- }
- return res;
-}
-
-/* FRENCH syntax */
-static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
-{
- /* Introduce messages they have */
- int res;
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res) {
- if (vms->newmessages) {
- res = say_and_wait(chan, vms->newmessages, chan->language);
- if (!res)
- res = ast_play_and_wait(chan, "vm-INBOX");
- if (vms->oldmessages && !res)
- res = ast_play_and_wait(chan, "vm-and");
- else if (!res) {
- if ((vms->newmessages == 1))
- res = ast_play_and_wait(chan, "vm-message");
- else
- res = ast_play_and_wait(chan, "vm-messages");
- }
-
- }
- if (!res && vms->oldmessages) {
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- if (!res)
- res = ast_play_and_wait(chan, "vm-Old");
- if (!res) {
- if (vms->oldmessages == 1)
- res = ast_play_and_wait(chan, "vm-message");
- else
- res = ast_play_and_wait(chan, "vm-messages");
- }
- }
- if (!res) {
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-no");
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- }
- }
- }
- return res;
-}
-
-/* DUTCH syntax */
-static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
-{
- /* Introduce messages they have */
- int res;
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res) {
- if (vms->newmessages) {
- res = say_and_wait(chan, vms->newmessages, chan->language);
- if (!res) {
- if (vms->newmessages == 1)
- res = ast_play_and_wait(chan, "vm-INBOXs");
- else
- res = ast_play_and_wait(chan, "vm-INBOX");
- }
- if (vms->oldmessages && !res)
- res = ast_play_and_wait(chan, "vm-and");
- else if (!res) {
- if ((vms->newmessages == 1))
- res = ast_play_and_wait(chan, "vm-message");
- else
- res = ast_play_and_wait(chan, "vm-messages");
- }
-
- }
- if (!res && vms->oldmessages) {
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- if (!res) {
- if (vms->oldmessages == 1)
- res = ast_play_and_wait(chan, "vm-Olds");
- else
- res = ast_play_and_wait(chan, "vm-Old");
- }
- if (!res) {
- if (vms->oldmessages == 1)
- res = ast_play_and_wait(chan, "vm-message");
- else
- res = ast_play_and_wait(chan, "vm-messages");
- }
- }
- if (!res) {
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-no");
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- }
- }
- }
- return res;
-}
-
-/* PORTUGUESE syntax */
-static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
-{
- /* Introduce messages they have */
- int res;
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res) {
- if (vms->newmessages) {
- res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
- if (!res) {
- if ((vms->newmessages == 1)) {
- res = ast_play_and_wait(chan, "vm-message");
- if (!res)
- res = ast_play_and_wait(chan, "vm-INBOXs");
- } else {
- res = ast_play_and_wait(chan, "vm-messages");
- if (!res)
- res = ast_play_and_wait(chan, "vm-INBOX");
- }
- }
- if (vms->oldmessages && !res)
- res = ast_play_and_wait(chan, "vm-and");
- }
- if (!res && vms->oldmessages) {
- res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
- if (!res) {
- if (vms->oldmessages == 1) {
- res = ast_play_and_wait(chan, "vm-message");
- if (!res)
- res = ast_play_and_wait(chan, "vm-Olds");
- } else {
- res = ast_play_and_wait(chan, "vm-messages");
- if (!res)
- res = ast_play_and_wait(chan, "vm-Old");
- }
- }
- }
- if (!res) {
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-no");
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- }
- }
- }
- return res;
-}
-
-
-/* CZECH syntax */
-/* in czech there must be declension of word new and message
- * czech : english : czech : english
- * --------------------------------------------------------
- * vm-youhave : you have
- * vm-novou : one new : vm-zpravu : message
- * vm-nove : 2-4 new : vm-zpravy : messages
- * vm-novych : 5-infinite new : vm-zprav : messages
- * vm-starou : one old
- * vm-stare : 2-4 old
- * vm-starych : 5-infinite old
- * jednu : one - falling 4.
- * vm-no : no ( no messages )
- */
-
-static int vm_intro_cz(struct ast_channel *chan, struct vm_state *vms)
-{
- int res;
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res) {
- if (vms->newmessages) {
- if (vms->newmessages == 1) {
- res = ast_play_and_wait(chan, "digits/jednu");
- } else {
- res = say_and_wait(chan, vms->newmessages, chan->language);
- }
- if (!res) {
- if ((vms->newmessages == 1))
- res = ast_play_and_wait(chan, "vm-novou");
- if ((vms->newmessages) > 1 && (vms->newmessages < 5))
- res = ast_play_and_wait(chan, "vm-nove");
- if (vms->newmessages > 4)
- res = ast_play_and_wait(chan, "vm-novych");
- }
- if (vms->oldmessages && !res)
- res = ast_play_and_wait(chan, "vm-and");
- else if (!res) {
- if ((vms->newmessages == 1))
- res = ast_play_and_wait(chan, "vm-zpravu");
- if ((vms->newmessages) > 1 && (vms->newmessages < 5))
- res = ast_play_and_wait(chan, "vm-zpravy");
- if (vms->newmessages > 4)
- res = ast_play_and_wait(chan, "vm-zprav");
- }
- }
- if (!res && vms->oldmessages) {
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- if (!res) {
- if ((vms->oldmessages == 1))
- res = ast_play_and_wait(chan, "vm-starou");
- if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
- res = ast_play_and_wait(chan, "vm-stare");
- if (vms->oldmessages > 4)
- res = ast_play_and_wait(chan, "vm-starych");
- }
- if (!res) {
- if ((vms->oldmessages == 1))
- res = ast_play_and_wait(chan, "vm-zpravu");
- if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
- res = ast_play_and_wait(chan, "vm-zpravy");
- if (vms->oldmessages > 4)
- res = ast_play_and_wait(chan, "vm-zprav");
- }
- }
- if (!res) {
- if (!vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-no");
- if (!res)
- res = ast_play_and_wait(chan, "vm-zpravy");
- }
- }
- }
- return res;
-}
-
-static int get_lastdigits(int num)
-{
- num %= 100;
- return (num < 20) ? num : num % 10;
-}
-
-static int vm_intro_ru(struct ast_channel *chan, struct vm_state *vms)
-{
- int res;
- int lastnum = 0;
- int dcnum;
-
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res && vms->newmessages) {
- lastnum = get_lastdigits(vms->newmessages);
- dcnum = vms->newmessages - lastnum;
- if (dcnum)
- res = say_and_wait(chan, dcnum, chan->language);
- if (!res && lastnum) {
- if (lastnum == 1)
- res = ast_play_and_wait(chan, "digits/ru/odno");
- else
- res = say_and_wait(chan, lastnum, chan->language);
- }
-
- if (!res)
- res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
-
- if (!res && vms->oldmessages)
- res = ast_play_and_wait(chan, "vm-and");
- }
-
- if (!res && vms->oldmessages) {
- lastnum = get_lastdigits(vms->oldmessages);
- dcnum = vms->oldmessages - lastnum;
- if (dcnum)
- res = say_and_wait(chan, dcnum, chan->language);
- if (!res && lastnum) {
- if (lastnum == 1)
- res = ast_play_and_wait(chan, "digits/ru/odno");
- else
- res = say_and_wait(chan, lastnum, chan->language);
- }
-
- if (!res)
- res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
- }
-
- if (!res && !vms->newmessages && !vms->oldmessages) {
- lastnum = 0;
- res = ast_play_and_wait(chan, "vm-no");
- }
-
- if (!res) {
- switch (lastnum) {
- case 1:
- res = ast_play_and_wait(chan, "vm-soobshenie");
- break;
- case 2:
- case 3:
- case 4:
- res = ast_play_and_wait(chan, "vm-soobsheniya");
- break;
- default:
- res = ast_play_and_wait(chan, "vm-soobsheniy");
- break;
- }
- }
-
- return res;
-}
-
-/* CHINESE (Taiwan) syntax */
-static int vm_intro_tw(struct ast_channel *chan, struct vm_state *vms)
-{
- int res;
- /* Introduce messages they have */
- res = ast_play_and_wait(chan, "vm-you");
-
- if (!res && vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-have");
- if (!res)
- res = say_and_wait(chan, vms->newmessages, chan->language);
- if (!res)
- res = ast_play_and_wait(chan, "vm-tong");
- if (!res)
- res = ast_play_and_wait(chan, "vm-INBOX");
- if (vms->oldmessages && !res)
- res = ast_play_and_wait(chan, "vm-and");
- else if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
-
- }
- if (!res && vms->oldmessages) {
- res = ast_play_and_wait(chan, "vm-have");
- if (!res)
- res = say_and_wait(chan, vms->oldmessages, chan->language);
- if (!res)
- res = ast_play_and_wait(chan, "vm-tong");
- if (!res)
- res = ast_play_and_wait(chan, "vm-Old");
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- }
- if (!res && !vms->oldmessages && !vms->newmessages) {
- res = ast_play_and_wait(chan, "vm-haveno");
- if (!res)
- res = ast_play_and_wait(chan, "vm-messages");
- }
- return res;
-}
-
-/* UKRAINIAN syntax */
-/* in ukrainian the syntax is different so we need the following files
- * --------------------------------------------------------
- * /digits/ua/1e 'odne'
- * vm-nove 'nove'
- * vm-stare 'stare'
- */
-static int vm_intro_ua(struct ast_channel *chan, struct vm_state *vms)
-{
- int res;
- int lastnum = 0;
- int dcnum;
-
- res = ast_play_and_wait(chan, "vm-youhave");
- if (!res && vms->newmessages) {
- lastnum = get_lastdigits(vms->newmessages);
- dcnum = vms->newmessages - lastnum;
- if (dcnum)
- res = say_and_wait(chan, dcnum, chan->language);
- if (!res && lastnum) {
- if (lastnum == 1)
- res = ast_play_and_wait(chan, "digits/ua/1e");
- else
- res = say_and_wait(chan, lastnum, chan->language);
- }
-
- if (!res)
- res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-nove" : "vm-INBOX");
-
- if (!res && vms->oldmessages)
- res = ast_play_and_wait(chan, "vm-and");
- }
-
- if (!res && vms->oldmessages) {
- lastnum = get_lastdigits(vms->oldmessages);
- dcnum = vms->oldmessages - lastnum;
- if (dcnum)
- res = say_and_wait(chan, dcnum, chan->language);
- if (!res && lastnum) {
- if (lastnum == 1)
- res = ast_play_and_wait(chan, "digits/ua/1e");
- else
- res = say_and_wait(chan, lastnum, chan->language);
- }
-
- if (!res)
- res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-stare" : "vm-Old");
- }
-
- if (!res && !vms->newmessages && !vms->oldmessages) {
- lastnum = 0;
- res = ast_play_and_wait(chan, "vm-no");
- }
-
- if (!res) {
- switch (lastnum) {
- case 1:
- case 2:
- case 3:
- case 4:
- res = ast_play_and_wait(chan, "vm-message");
- break;
- default:
- res = ast_play_and_wait(chan, "vm-messages");
- break;
- }
- }
-
- return res;
-}
-
-static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
-{
- char prefile[256];
-
- /* Notify the user that the temp greeting is set and give them the option to remove it */
- snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
- if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
- if (ast_fileexists(prefile, NULL, NULL) > 0)
- ast_play_and_wait(chan, "vm-tempgreetactive");
- }
-
- /* Play voicemail intro - syntax is different for different languages */
- if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
- return vm_intro_de(chan, vms);
- } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
- return vm_intro_es(chan, vms);
- } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
- return vm_intro_it(chan, vms);
- } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
- return vm_intro_fr(chan, vms);
- } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
- return vm_intro_nl(chan, vms);
- } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
- return vm_intro_pt(chan, vms);
- } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
- return vm_intro_pt_BR(chan, vms);
- } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
- return vm_intro_cz(chan, vms);
- } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
- return vm_intro_gr(chan, vms);
- } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
- return vm_intro_pl(chan, vms);
- } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
- return vm_intro_se(chan, vms);
- } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
- return vm_intro_no(chan, vms);
- } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
- return vm_intro_ru(chan, vms);
- } else if (!strcasecmp(chan->language, "tw")) { /* CHINESE (Taiwan) syntax */
- return vm_intro_tw(chan, vms);
- } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
- return vm_intro_ua(chan, vms);
- } else { /* Default to ENGLISH */
- return vm_intro_en(chan, vms);
- }
-}
-
-static int vm_instructions_en(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
-{
- int res = 0;
- /* Play instructions and wait for new command */
- while (!res) {
- if (vms->starting) {
- if (vms->lastmsg > -1) {
- res = ast_play_and_wait(chan, "vm-onefor");
- if (!res)
- res = vm_play_folder_name(chan, vms->vmbox);
- }
- if (!res)
- res = ast_play_and_wait(chan, "vm-opts");
- } else {
- if (vms->curmsg)
- res = ast_play_and_wait(chan, "vm-prev");
- if (!res && !skipadvanced)
- res = ast_play_and_wait(chan, "vm-advopts");
- if (!res)
- res = ast_play_and_wait(chan, "vm-repeat");
- if (!res && (vms->curmsg != vms->lastmsg))
- res = ast_play_and_wait(chan, "vm-next");
- if (!res) {
- if (!vms->deleted[vms->curmsg])
- res = ast_play_and_wait(chan, "vm-delete");
- else
- res = ast_play_and_wait(chan, "vm-undelete");
- if (!res)
- res = ast_play_and_wait(chan, "vm-toforward");
- if (!res)
- res = ast_play_and_wait(chan, "vm-savemessage");
- }
- }
- if (!res)
- res = ast_play_and_wait(chan, "vm-helpexit");
- if (!res)
- res = ast_waitfordigit(chan, 6000);
- if (!res) {
- vms->repeats++;
- if (vms->repeats > 2) {
- res = 't';
- }
- }
- }
- return res;
-}
-
-static int vm_instructions_tw(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
-{
- int res = 0;
- /* Play instructions and wait for new command */
- while (!res) {
- if (vms->lastmsg > -1) {
- res = ast_play_and_wait(chan, "vm-listen");
- if (!res)
- res = vm_play_folder_name(chan, vms->vmbox);
- if (!res)
- res = ast_play_and_wait(chan, "press");
- if (!res)
- res = ast_play_and_wait(chan, "digits/1");
- }
- if (!res)
- res = ast_play_and_wait(chan, "vm-opts");
- if (!res) {
- vms->starting = 0;
- return vm_instructions_en(chan, vms, skipadvanced);
- }
- }
- return res;
-}
-
-static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
-{
- if (vms->starting && !strcasecmp(chan->language, "tw")) { /* CHINESE (Taiwan) syntax */
- return vm_instructions_tw(chan, vms, skipadvanced);
- } else { /* Default to ENGLISH */
- return vm_instructions_en(chan, vms, skipadvanced);
- }
-}
-
-
-static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
-{
- int cmd = 0;
- int duration = 0;
- int tries = 0;
- char newpassword[80] = "";
- char newpassword2[80] = "";
- char prefile[PATH_MAX] = "";
- unsigned char buf[256];
- int bytes = 0;
-
- if (ast_adsi_available(chan)) {
- bytes += adsi_logo(buf + bytes);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
- }
-
- /* First, have the user change their password
- so they won't get here again */
- for (;;) {
- newpassword[1] = '\0';
- newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
- if (cmd == '#')
- newpassword[0] = '\0';
- if (cmd < 0 || cmd == 't' || cmd == '#')
- return cmd;
- cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
- if (cmd < 0 || cmd == 't' || cmd == '#')
- return cmd;
- newpassword2[1] = '\0';
- newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
- if (cmd == '#')
- newpassword2[0] = '\0';
- if (cmd < 0 || cmd == 't' || cmd == '#')
- return cmd;
- cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
- if (cmd < 0 || cmd == 't' || cmd == '#')
- return cmd;
- if (!strcmp(newpassword, newpassword2))
- break;
- ast_log(LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
- cmd = ast_play_and_wait(chan, vm_mismatch);
- if (++tries == 3)
- return -1;
- }
- if (pwdchange & PWDCHANGE_INTERNAL)
- vm_change_password(vmu, newpassword);
- if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
- vm_change_password_shell(vmu, newpassword);
-
- ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int)strlen(newpassword));
- cmd = ast_play_and_wait(chan, vm_passchanged);
-
- /* If forcename is set, have the user record their name */
- if (ast_test_flag(vmu, VM_FORCENAME)) {
- snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
- if (ast_fileexists(prefile, NULL, NULL) < 1) {
-#ifndef IMAP_STORAGE
- cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
-#else
- cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
-#endif
- if (cmd < 0 || cmd == 't' || cmd == '#')
- return cmd;
- }
- }
-
- /* If forcegreetings is set, have the user record their greetings */
- if (ast_test_flag(vmu, VM_FORCEGREET)) {
- snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
- if (ast_fileexists(prefile, NULL, NULL) < 1) {
-#ifndef IMAP_STORAGE
- cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
-#else
- cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
-#endif
- if (cmd < 0 || cmd == 't' || cmd == '#')
- return cmd;
- }
-
- snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
- if (ast_fileexists(prefile, NULL, NULL) < 1) {
-#ifndef IMAP_STORAGE
- cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
-#else
- cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
-#endif
- if (cmd < 0 || cmd == 't' || cmd == '#')
- return cmd;
- }
- }
-
- return cmd;
-}
-
-static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
-{
- int cmd = 0;
- int retries = 0;
- int duration = 0;
- char newpassword[80] = "";
- char newpassword2[80] = "";
- char prefile[PATH_MAX] = "";
- unsigned char buf[256];
- int bytes = 0;
-
- if (ast_adsi_available(chan))
- {
- bytes += adsi_logo(buf + bytes);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
- }
- while ((cmd >= 0) && (cmd != 't')) {
- if (cmd)
- retries = 0;
- switch (cmd) {
- case '1':
- snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
-#ifndef IMAP_STORAGE
- cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
-#else
- cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
-#endif
- break;
- case '2':
- snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
-#ifndef IMAP_STORAGE
- cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
-#else
- cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
-#endif
- break;
- case '3':
- snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
-#ifndef IMAP_STORAGE
- cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
-#else
- cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
-#endif
- break;
- case '4':
- cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
- break;
- case '5':
- if (vmu->password[0] == '-') {
- cmd = ast_play_and_wait(chan, "vm-no");
- break;
- }
- newpassword[1] = '\0';
- newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
- if (cmd == '#')
- newpassword[0] = '\0';
- else {
- if (cmd < 0)
- break;
- if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
- break;
- }
- }
- newpassword2[1] = '\0';
- newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
- if (cmd == '#')
- newpassword2[0] = '\0';
- else {
- if (cmd < 0)
- break;
-
- if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#"))) {
- break;
- }
- }
- if (strcmp(newpassword, newpassword2)) {
- ast_log(LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
- cmd = ast_play_and_wait(chan, vm_mismatch);
- break;
- }
- if (pwdchange & PWDCHANGE_INTERNAL)
- vm_change_password(vmu, newpassword);
- if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
- vm_change_password_shell(vmu, newpassword);
-
- ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int)strlen(newpassword));
- cmd = ast_play_and_wait(chan, vm_passchanged);
- break;
- case '*':
- cmd = 't';
- break;
- default:
- cmd = ast_play_and_wait(chan, "vm-options");
- if (!cmd)
- cmd = ast_waitfordigit(chan, 6000);
- if (!cmd)
- retries++;
- if (retries > 3)
- cmd = 't';
- }
- }
- if (cmd == 't')
- cmd = 0;
- return cmd;
-}
-
-static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
-{
- int cmd = 0;
- int retries = 0;
- int duration = 0;
- char prefile[PATH_MAX] = "";
- unsigned char buf[256];
- int bytes = 0;
-
- if (ast_adsi_available(chan)) {
- bytes += adsi_logo(buf + bytes);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
- }
-
- snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
- while ((cmd >= 0) && (cmd != 't')) {
- if (cmd)
- retries = 0;
- RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
- if (ast_fileexists(prefile, NULL, NULL) <= 0) {
-#ifndef IMAP_STORAGE
- play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
-#else
- play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
-#endif
- cmd = 't';
- } else {
- switch (cmd) {
- case '1':
-#ifndef IMAP_STORAGE
- cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
-#else
- cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
-#endif
- break;
- case '2':
- DELETE(prefile, -1, prefile);
- ast_play_and_wait(chan, "vm-tempremoved");
- cmd = 't';
- break;
- case '*':
- cmd = 't';
- break;
- default:
- cmd = ast_play_and_wait(chan,
- ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
- "vm-tempgreeting2" : "vm-tempgreeting");
- if (!cmd)
- cmd = ast_waitfordigit(chan, 6000);
- if (!cmd)
- retries++;
- if (retries > 3)
- cmd = 't';
- }
- }
- DISPOSE(prefile, -1);
- }
- if (cmd == 't')
- cmd = 0;
- return cmd;
-}
-
-/* GREEK SYNTAX */
-
-static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
-{
- int cmd = 0;
-
- if (vms->lastmsg > -1) {
- cmd = play_message(chan, vmu, vms);
- } else {
- cmd = ast_play_and_wait(chan, "vm-youhaveno");
- if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")) {
- if (!cmd) {
- snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
- cmd = ast_play_and_wait(chan, vms->fn);
- }
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-messages");
- } else {
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-messages");
- if (!cmd) {
- snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
- cmd = ast_play_and_wait(chan, vms->fn);
- }
- }
- }
- return cmd;
-}
-
-/* Default English syntax */
-static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
-{
- int cmd = 0;
-
- if (vms->lastmsg > -1) {
- cmd = play_message(chan, vmu, vms);
- } else {
- cmd = ast_play_and_wait(chan, "vm-youhave");
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-no");
- if (!cmd) {
- snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
- cmd = ast_play_and_wait(chan, vms->fn);
- }
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-messages");
- }
- return cmd;
-}
-
-/* ITALIAN syntax */
-static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
-{
- int cmd = 0;
-
- if (vms->lastmsg > -1) {
- cmd = play_message(chan, vmu, vms);
- } else {
- cmd = ast_play_and_wait(chan, "vm-no");
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-message");
- if (!cmd) {
- snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
- cmd = ast_play_and_wait(chan, vms->fn);
- }
- }
- return cmd;
-}
-
-/* SPANISH syntax */
-static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
-{
- int cmd = 0;
-
- if (vms->lastmsg > -1) {
- cmd = play_message(chan, vmu, vms);
- } else {
- cmd = ast_play_and_wait(chan, "vm-youhaveno");
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-messages");
- if (!cmd) {
- snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
- cmd = ast_play_and_wait(chan, vms->fn);
- }
- }
- return cmd;
-}
-
-/* PORTUGUESE syntax */
-static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
-{
- int cmd = 0;
-
- if (vms->lastmsg > -1) {
- cmd = play_message(chan, vmu, vms);
- } else {
- cmd = ast_play_and_wait(chan, "vm-no");
- if (!cmd) {
- snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
- cmd = ast_play_and_wait(chan, vms->fn);
- }
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-messages");
- }
- return cmd;
-}
-
-/* Chinese (Taiwan)syntax */
-static int vm_browse_messages_tw(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
-{
- int cmd = 0;
-
- if (vms->lastmsg > -1) {
- cmd = play_message(chan, vmu, vms);
- } else {
- cmd = ast_play_and_wait(chan, "vm-you");
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-haveno");
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-messages");
- if (!cmd) {
- snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
- cmd = ast_play_and_wait(chan, vms->fn);
- }
- }
- return cmd;
-}
-
-static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
-{
- if (!strcasecmp(chan->language, "es")) { /* SPANISH */
- return vm_browse_messages_es(chan, vms, vmu);
- } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
- return vm_browse_messages_it(chan, vms, vmu);
- } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* PORTUGUESE */
- return vm_browse_messages_pt(chan, vms, vmu);
- } else if (!strcasecmp(chan->language, "gr")) {
- return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
- } else if (!strcasecmp(chan->language, "tw")) {
- return vm_browse_messages_tw(chan, vms, vmu); /* CHINESE (Taiwan) */
- } else { /* Default to English syntax */
- return vm_browse_messages_en(chan, vms, vmu);
- }
-}
-
-static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
- struct ast_vm_user *res_vmu, const char *context, const char *prefix,
- int skipuser, int maxlogins, int silent)
-{
- int useadsi = 0, valid = 0, logretries = 0;
- char password[AST_MAX_EXTENSION] = "", *passptr;
- struct ast_vm_user vmus, *vmu = NULL;
-
- /* If ADSI is supported, setup login screen */
- adsi_begin(chan, &useadsi);
- if (!skipuser && useadsi)
- adsi_login(chan);
- if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
- ast_log(LOG_WARNING, "Couldn't stream login file\n");
- return -1;
- }
-
- /* Authenticate them and get their mailbox/password */
-
- while (!valid && (logretries < maxlogins)) {
- /* Prompt for, and read in the username */
- if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
- ast_log(LOG_WARNING, "Couldn't read username\n");
- return -1;
- }
- if (ast_strlen_zero(mailbox)) {
- if (chan->cid.cid_num) {
- ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
- } else {
- ast_verb(3, "Username not entered\n");
- return -1;
- }
- }
- if (useadsi)
- adsi_password(chan);
-
- if (!ast_strlen_zero(prefix)) {
- char fullusername[80] = "";
- ast_copy_string(fullusername, prefix, sizeof(fullusername));
- strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
- ast_copy_string(mailbox, fullusername, mailbox_size);
- }
-
- ast_debug(1, "Before find user for mailbox %s\n", mailbox);
- vmu = find_user(&vmus, context, mailbox);
- if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
- /* saved password is blank, so don't bother asking */
- password[0] = '\0';
- } else {
- if (ast_streamfile(chan, vm_password, chan->language)) {
- ast_log(LOG_WARNING, "Unable to stream password file\n");
- return -1;
- }
- if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
- ast_log(LOG_WARNING, "Unable to read password\n");
- return -1;
- }
- }
-
- if (vmu) {
- passptr = vmu->password;
- if (passptr[0] == '-') passptr++;
- }
- if (vmu && !strcmp(passptr, password))
- valid++;
- else {
- ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
- if (!ast_strlen_zero(prefix))
- mailbox[0] = '\0';
- }
- logretries++;
- if (!valid) {
- if (skipuser || logretries >= maxlogins) {
- if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
- ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
- return -1;
- }
- } else {
- if (useadsi)
- adsi_login(chan);
- if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
- ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
- return -1;
- }
- }
- if (ast_waitstream(chan, "")) /* Channel is hung up */
- return -1;
- }
- }
- if (!valid && (logretries >= maxlogins)) {
- ast_stopstream(chan);
- ast_play_and_wait(chan, "vm-goodbye");
- return -1;
- }
- if (vmu && !skipuser) {
- memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
- }
- return 0;
-}
-
-static int vm_execmain(struct ast_channel *chan, void *data)
-{
- /* XXX This is, admittedly, some pretty horrendous code. For some
- reason it just seemed a lot easier to do with GOTO's. I feel
- like I'm back in my GWBASIC days. XXX */
- int res = -1;
- int cmd = 0;
- int valid = 0;
- char prefixstr[80] = "";
- char ext_context[256] = "";
- int box;
- int useadsi = 0;
- int skipuser = 0;
- struct vm_state vms;
- struct ast_vm_user *vmu = NULL, vmus;
- char *context = NULL;
- int silentexit = 0;
- struct ast_flags flags = { 0 };
- signed char record_gain = 0;
- int play_auto = 0;
- int play_folder = 0;
-#ifdef IMAP_STORAGE
- int deleted = 0;
-#endif
-
- /* Add the vm_state to the active list and keep it active */
- memset(&vms, 0, sizeof(vms));
- vms.lastmsg = -1;
-
- memset(&vmus, 0, sizeof(vmus));
-
- if (chan->_state != AST_STATE_UP) {
- ast_debug(1, "Before ast_answer\n");
- ast_answer(chan);
- }
-
- if (!ast_strlen_zero(data)) {
- char *opts[OPT_ARG_ARRAY_SIZE];
- char *parse;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(argv0);
- AST_APP_ARG(argv1);
- );
-
- parse = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (args.argc == 2) {
- if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
- return -1;
- if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
- int gain;
- if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
- if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
- ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
- return -1;
- } else {
- record_gain = (signed char) gain;
- }
- } else {
- ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
- }
- }
- if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
- play_auto = 1;
- if (opts[OPT_ARG_PLAYFOLDER]) {
- if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
- ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
- }
- } else {
- ast_log(LOG_WARNING, "Invalid folder set with option a\n");
- }
- if (play_folder > 9 || play_folder < 0) {
- ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
- play_folder = 0;
- }
- }
- } else {
- /* old style options parsing */
- while (*(args.argv0)) {
- if (*(args.argv0) == 's')
- ast_set_flag(&flags, OPT_SILENT);
- else if (*(args.argv0) == 'p')
- ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
- else
- break;
- (args.argv0)++;
- }
-
- }
-
- valid = ast_test_flag(&flags, OPT_SILENT);
-
- if ((context = strchr(args.argv0, '@')))
- *context++ = '\0';
-
- if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
- ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
- else
- ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
-
- if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context, vms.username)))
- skipuser++;
- else
- valid = 0;
- }
-
- if (!valid)
- res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
-
- ast_debug(1, "After vm_authenticate\n");
- if (!res) {
- valid = 1;
- if (!skipuser)
- vmu = &vmus;
- } else {
- res = 0;
- }
-
- /* If ADSI is supported, setup login screen */
- adsi_begin(chan, &useadsi);
-
-#ifdef IMAP_STORAGE
- vms.interactive = 1;
- vms.updated = 1;
- vmstate_insert(&vms);
- init_vm_state(&vms);
-#endif
- if (!valid)
- goto out;
-
- if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
- ast_log(LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
- cmd = ast_play_and_wait(chan, "an-error-has-occured");
- return -1;
- }
- if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
- ast_log(LOG_ERROR, "Could not allocate memory for heard message storage!\n");
- cmd = ast_play_and_wait(chan, "an-error-has-occured");
- return -1;
- }
-
- /* Set language from config to override channel language */
- if (!ast_strlen_zero(vmu->language))
- ast_string_field_set(chan, language, vmu->language);
- create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
- /* Retrieve old and new message counts */
- ast_debug(1, "Before open_mailbox\n");
- res = open_mailbox(&vms, vmu, OLD_FOLDER);
- if (res == ERROR_LOCK_PATH)
- goto out;
- vms.oldmessages = vms.lastmsg + 1;
- ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
- /* Start in INBOX */
- res = open_mailbox(&vms, vmu, NEW_FOLDER);
- if (res == ERROR_LOCK_PATH)
- goto out;
- vms.newmessages = vms.lastmsg + 1;
- ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
-
- /* Select proper mailbox FIRST!! */
- if (play_auto) {
- res = open_mailbox(&vms, vmu, play_folder);
- if (res == ERROR_LOCK_PATH)
- goto out;
-
- /* If there are no new messages, inform the user and hangup */
- if (vms.lastmsg == -1) {
- cmd = vm_browse_messages(chan, &vms, vmu);
- res = 0;
- goto out;
- }
- } else {
- if (!vms.newmessages && vms.oldmessages) {
- /* If we only have old messages start here */
- res = open_mailbox(&vms, vmu, OLD_FOLDER);
- play_folder = 1;
- if (res == ERROR_LOCK_PATH)
- goto out;
- }
- }
-
- if (useadsi)
- adsi_status(chan, &vms);
- res = 0;
-
- /* Check to see if this is a new user */
- if (!strcasecmp(vmu->mailbox, vmu->password) &&
- (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
- if (ast_play_and_wait(chan, "vm-newuser") == -1)
- ast_log(LOG_WARNING, "Couldn't stream new user file\n");
- cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
- if ((cmd == 't') || (cmd == '#')) {
- /* Timeout */
- res = 0;
- goto out;
- } else if (cmd < 0) {
- /* Hangup */
- res = -1;
- goto out;
- }
- }
-#ifdef IMAP_STORAGE
- ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
- if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
- ast_debug(1, "*** QUOTA EXCEEDED!!\n");
- cmd = ast_play_and_wait(chan, "vm-mailboxfull");
- }
- ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
- if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
- ast_log(LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
- cmd = ast_play_and_wait(chan, "vm-mailboxfull");
- }
-#endif
- if (play_auto) {
- cmd = '1';
- } else {
- cmd = vm_intro(chan, vmu, &vms);
- }
-
- vms.repeats = 0;
- vms.starting = 1;
- while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
- /* Run main menu */
- switch (cmd) {
- case '1':
- vms.curmsg = 0;
- /* Fall through */
- case '5':
- cmd = vm_browse_messages(chan, &vms, vmu);
- break;
- case '2': /* Change folders */
- if (useadsi)
- adsi_folders(chan, 0, "Change to folder...");
- cmd = get_folder2(chan, "vm-changeto", 0);
- if (cmd == '#') {
- cmd = 0;
- } else if (cmd > 0) {
- cmd = cmd - '0';
- res = close_mailbox(&vms, vmu);
- if (res == ERROR_LOCK_PATH)
- goto out;
- res = open_mailbox(&vms, vmu, cmd);
- if (res == ERROR_LOCK_PATH)
- goto out;
- play_folder = cmd;
- cmd = 0;
- }
- if (useadsi)
- adsi_status2(chan, &vms);
-
- if (!cmd)
- cmd = vm_play_folder_name(chan, vms.vmbox);
-
- vms.starting = 1;
- break;
- case '3': /* Advanced options */
- cmd = 0;
- vms.repeats = 0;
- while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
- switch (cmd) {
- case '1': /* Reply */
- if (vms.lastmsg > -1 && !vms.starting) {
- cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
- if (cmd == ERROR_LOCK_PATH) {
- res = cmd;
- goto out;
- }
- } else
- cmd = ast_play_and_wait(chan, "vm-sorry");
- cmd = 't';
- break;
- case '2': /* Callback */
- if (!vms.starting)
- ast_verb(3, "Callback Requested\n");
- if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
- cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
- if (cmd == 9) {
- silentexit = 1;
- goto out;
- } else if (cmd == ERROR_LOCK_PATH) {
- res = cmd;
- goto out;
- }
- }
- else
- cmd = ast_play_and_wait(chan, "vm-sorry");
- cmd = 't';
- break;
- case '3': /* Envelope */
- if (vms.lastmsg > -1 && !vms.starting) {
- cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
- if (cmd == ERROR_LOCK_PATH) {
- res = cmd;
- goto out;
- }
- } else
- cmd = ast_play_and_wait(chan, "vm-sorry");
- cmd = 't';
- break;
- case '4': /* Dialout */
- if (!ast_strlen_zero(vmu->dialout)) {
- cmd = dialout(chan, vmu, NULL, vmu->dialout);
- if (cmd == 9) {
- silentexit = 1;
- goto out;
- }
- }
- else
- cmd = ast_play_and_wait(chan, "vm-sorry");
- cmd = 't';
- break;
-
- case '5': /* Leave VoiceMail */
- if (ast_test_flag(vmu, VM_SVMAIL)) {
- cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
- if (cmd == ERROR_LOCK_PATH) {
- res = cmd;
- ast_log(LOG_WARNING, "forward_message failed to lock path.\n");
- goto out;
- }
- } else
- cmd = ast_play_and_wait(chan, "vm-sorry");
- cmd = 't';
- break;
-
- case '*': /* Return to main menu */
- cmd = 't';
- break;
-
- default:
- cmd = 0;
- if (!vms.starting) {
- cmd = ast_play_and_wait(chan, "vm-toreply");
- }
- if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
- cmd = ast_play_and_wait(chan, "vm-tocallback");
- }
- if (!cmd && !vms.starting) {
- cmd = ast_play_and_wait(chan, "vm-tohearenv");
- }
- if (!ast_strlen_zero(vmu->dialout) && !cmd) {
- cmd = ast_play_and_wait(chan, "vm-tomakecall");
- }
- if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
- cmd = ast_play_and_wait(chan, "vm-leavemsg");
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-starmain");
- if (!cmd)
- cmd = ast_waitfordigit(chan, 6000);
- if (!cmd)
- vms.repeats++;
- if (vms.repeats > 3)
- cmd = 't';
- }
- }
- if (cmd == 't') {
- cmd = 0;
- vms.repeats = 0;
- }
- break;
- case '4':
- if (vms.curmsg > 0) {
- vms.curmsg--;
- cmd = play_message(chan, vmu, &vms);
- } else {
- cmd = ast_play_and_wait(chan, "vm-nomore");
- }
- break;
- case '6':
- if (vms.curmsg < vms.lastmsg) {
- vms.curmsg++;
- cmd = play_message(chan, vmu, &vms);
- } else {
- cmd = ast_play_and_wait(chan, "vm-nomore");
- }
- break;
- case '7':
- if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
- vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
- if (useadsi)
- adsi_delete(chan, &vms);
- if (vms.deleted[vms.curmsg]) {
- if (play_folder == 0)
- vms.newmessages--;
- else if (play_folder == 1)
- vms.oldmessages--;
- cmd = ast_play_and_wait(chan, "vm-deleted");
- }
- else {
- if (play_folder == 0)
- vms.newmessages++;
- else if (play_folder == 1)
- vms.oldmessages++;
- cmd = ast_play_and_wait(chan, "vm-undeleted");
- }
- if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
- if (vms.curmsg < vms.lastmsg) {
- vms.curmsg++;
- cmd = play_message(chan, vmu, &vms);
- } else {
- cmd = ast_play_and_wait(chan, "vm-nomore");
- }
- }
- } else /* Delete not valid if we haven't selected a message */
- cmd = 0;
-#ifdef IMAP_STORAGE
- deleted = 1;
-#endif
- break;
-
- case '8':
- if (vms.lastmsg > -1) {
- cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
- if (cmd == ERROR_LOCK_PATH) {
- res = cmd;
- goto out;
- }
- } else
- cmd = ast_play_and_wait(chan, "vm-nomore");
- break;
- case '9':
- if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
- /* No message selected */
- cmd = 0;
- break;
- }
- if (useadsi)
- adsi_folders(chan, 1, "Save to folder...");
- cmd = get_folder2(chan, "vm-savefolder", 1);
- box = 0; /* Shut up compiler */
- if (cmd == '#') {
- cmd = 0;
- break;
- } else if (cmd > 0) {
- box = cmd = cmd - '0';
- cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
- if (cmd == ERROR_LOCK_PATH) {
- res = cmd;
- goto out;
-#ifdef IMAP_STORAGE
- } else if (cmd == 10) {
- goto out;
-#endif
- } else if (!cmd) {
- vms.deleted[vms.curmsg] = 1;
- } else {
- vms.deleted[vms.curmsg] = 0;
- vms.heard[vms.curmsg] = 0;
- }
- }
- make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
- if (useadsi)
- adsi_message(chan, &vms);
- snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
- if (!cmd) {
- cmd = ast_play_and_wait(chan, "vm-message");
- if (!cmd)
- cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-savedto");
- if (!cmd)
- cmd = vm_play_folder_name(chan, vms.fn);
- } else {
- cmd = ast_play_and_wait(chan, "vm-mailboxfull");
- }
- if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
- if (vms.curmsg < vms.lastmsg) {
- vms.curmsg++;
- cmd = play_message(chan, vmu, &vms);
- } else {
- cmd = ast_play_and_wait(chan, "vm-nomore");
- }
- }
- break;
- case '*':
- if (!vms.starting) {
- cmd = ast_play_and_wait(chan, "vm-onefor");
- if (!cmd)
- cmd = vm_play_folder_name(chan, vms.vmbox);
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-opts");
- if (!cmd)
- cmd = vm_instructions(chan, &vms, 1);
- } else
- cmd = 0;
- break;
- case '0':
- cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
- if (useadsi)
- adsi_status(chan, &vms);
- break;
- default: /* Nothing */
- cmd = vm_instructions(chan, &vms, 0);
- break;
- }
- }
- if ((cmd == 't') || (cmd == '#')) {
- /* Timeout */
- res = 0;
- } else {
- /* Hangup */
- res = -1;
- }
-
-out:
- if (res > -1) {
- ast_stopstream(chan);
- adsi_goodbye(chan);
- if (valid) {
- if (silentexit)
- res = ast_play_and_wait(chan, "vm-dialout");
- else
- res = ast_play_and_wait(chan, "vm-goodbye");
- if (res > 0)
- res = 0;
- }
- if (useadsi)
- ast_adsi_unload_session(chan);
- }
- if (vmu)
- close_mailbox(&vms, vmu);
- if (valid) {
- int new = 0, old = 0;
- snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
- manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
- run_externnotify(vmu->context, vmu->mailbox);
- ast_app_inboxcount(ext_context, &new, &old);
- queue_mwi_event(ext_context, new, old);
- }
-#ifdef IMAP_STORAGE
- /* expunge message - use UID Expunge if supported on IMAP server*/
- ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
- if (vmu && deleted == 1 && expungeonhangup == 1) {
-#ifdef HAVE_IMAP_TK2006
- if (LEVELUIDPLUS (vms.mailstream)) {
- mail_expunge_full(vms.mailstream, NIL, EX_UID);
- } else
-#endif
- mail_expunge(vms.mailstream);
- }
- /* before we delete the state, we should copy pertinent info
- * back to the persistent model */
- vmstate_delete(&vms);
-#endif
- if (vmu)
- free_user(vmu);
- if (vms.deleted)
- ast_free(vms.deleted);
- if (vms.heard)
- ast_free(vms.heard);
-
- return res;
-}
-
-static int vm_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- char *tmp;
- struct leave_vm_options leave_options;
- struct ast_flags flags = { 0 };
- char *opts[OPT_ARG_ARRAY_SIZE];
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(argv0);
- AST_APP_ARG(argv1);
- );
-
- memset(&leave_options, 0, sizeof(leave_options));
-
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- if (!ast_strlen_zero(data)) {
- tmp = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, tmp);
- if (args.argc == 2) {
- if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
- return -1;
- ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_DTMFEXIT);
- if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
- int gain;
-
- if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
- ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
- return -1;
- } else {
- leave_options.record_gain = (signed char) gain;
- }
- }
- if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
- if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
- leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
- }
- }
- } else {
- char tmp[256];
- res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
- if (res < 0)
- return res;
- if (ast_strlen_zero(tmp))
- return 0;
- args.argv0 = ast_strdupa(tmp);
- }
-
- res = leave_voicemail(chan, args.argv0, &leave_options);
-
- if (res == ERROR_LOCK_PATH) {
- ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- res = 0;
- }
-
- return res;
-}
-
-static struct ast_vm_user *find_or_create(const char *context, const char *mbox)
-{
- struct ast_vm_user *vmu;
-
- AST_LIST_TRAVERSE(&users, vmu, list) {
- if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
- break;
- if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
- break;
- }
-
- if (vmu)
- return vmu;
-
- if (!(vmu = ast_calloc(1, sizeof(*vmu))))
- return NULL;
-
- ast_copy_string(vmu->context, context, sizeof(vmu->context));
- ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
-
- AST_LIST_INSERT_TAIL(&users, vmu, list);
-
- return vmu;
-}
-
-static int append_mailbox(const char *context, const char *mbox, const char *data)
-{
- /* Assumes lock is already held */
- char *tmp;
- char *stringp;
- char *s;
- struct ast_vm_user *vmu;
- char *mailbox_full;
- int new = 0, old = 0;
-
- tmp = ast_strdupa(data);
-
- if (!(vmu = find_or_create(context, mbox)))
- return -1;
-
- populate_defaults(vmu);
-
- stringp = tmp;
- if ((s = strsep(&stringp, ",")))
- ast_copy_string(vmu->password, s, sizeof(vmu->password));
- if (stringp && (s = strsep(&stringp, ",")))
- ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
- if (stringp && (s = strsep(&stringp, ",")))
- ast_copy_string(vmu->email, s, sizeof(vmu->email));
- if (stringp && (s = strsep(&stringp, ",")))
- ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
- if (stringp && (s = strsep(&stringp, ",")))
- apply_options(vmu, s);
-
- mailbox_full = alloca(strlen(mbox) + strlen(context) + 1);
- strcpy(mailbox_full, mbox);
- strcat(mailbox_full, "@");
- strcat(mailbox_full, context);
-
- inboxcount(mailbox_full, &new, &old);
- queue_mwi_event(mailbox_full, new, old);
-
- return 0;
-}
-
-static int vm_box_exists(struct ast_channel *chan, void *data)
-{
- struct ast_vm_user svm;
- char *context, *box;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(mbox);
- AST_APP_ARG(options);
- );
- static int dep_warning = 0;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
- return -1;
- }
-
- if (!dep_warning) {
- dep_warning = 1;
- ast_log(LOG_WARNING, "MailboxExists is deprecated. Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
- }
-
- box = ast_strdupa(data);
-
- AST_STANDARD_APP_ARGS(args, box);
-
- if (args.options) {
- }
-
- if ((context = strchr(args.mbox, '@'))) {
- *context = '\0';
- context++;
- }
-
- if (find_user(&svm, context, args.mbox)) {
- pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
- } else
- pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
-
- return 0;
-}
-
-static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
-{
- struct ast_vm_user svm;
- AST_DECLARE_APP_ARGS(arg,
- AST_APP_ARG(mbox);
- AST_APP_ARG(context);
- );
-
- AST_NONSTANDARD_APP_ARGS(arg, args, '@');
-
- ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
- return 0;
-}
-
-static struct ast_custom_function mailbox_exists_acf = {
- .name = "MAILBOX_EXISTS",
- .synopsis = "Tell if a mailbox is configured",
- .desc =
-"Returns a boolean of whether the corresponding mailbox exists. If context\n"
-"is not specified, defaults to the \"default\" context.\n",
- .syntax = "MAILBOX_EXISTS(<vmbox>[@<context>])",
- .read = acf_mailbox_exists,
-};
-
-static int vmauthenticate(struct ast_channel *chan, void *data)
-{
- char *s = data, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
- struct ast_vm_user vmus;
- char *options = NULL;
- int silent = 0, skipuser = 0;
- int res = -1;
-
- if (s) {
- s = ast_strdupa(s);
- user = strsep(&s, ",");
- options = strsep(&s, ",");
- if (user) {
- s = user;
- user = strsep(&s, "@");
- context = strsep(&s, "");
- if (!ast_strlen_zero(user))
- skipuser++;
- ast_copy_string(mailbox, user, sizeof(mailbox));
- }
- }
-
- if (options) {
- silent = (strchr(options, 's')) != NULL;
- }
-
- if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
- pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
- pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
- ast_play_and_wait(chan, "auth-thankyou");
- res = 0;
- }
-
- return res;
-}
-
-static char *show_users_realtime(int fd, const char *context)
-{
- struct ast_config *cfg;
- const char *cat = NULL;
-
- if (!(cfg = ast_load_realtime_multientry("voicemail",
- "context", context, NULL))) {
- return CLI_FAILURE;
- }
-
- ast_cli(fd, "\n"
- "=============================================================\n"
- "=== Configured Voicemail Users ==============================\n"
- "=============================================================\n"
- "===\n");
-
- while ((cat = ast_category_browse(cfg, cat))) {
- struct ast_variable *var = NULL;
- ast_cli(fd, "=== Mailbox ...\n"
- "===\n");
- for (var = ast_variable_browse(cfg, cat); var; var = var->next)
- ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
- ast_cli(fd, "===\n"
- "=== ---------------------------------------------------------\n"
- "===\n");
- }
-
- ast_cli(fd, "=============================================================\n"
- "\n");
-
- return CLI_SUCCESS;
-}
-
-static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
-{
- int which = 0;
- int wordlen;
- struct ast_vm_user *vmu;
- const char *context = "";
-
- /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
- if (pos > 4)
- return NULL;
- if (pos == 3)
- return (state == 0) ? ast_strdup("for") : NULL;
- wordlen = strlen(word);
- AST_LIST_TRAVERSE(&users, vmu, list) {
- if (!strncasecmp(word, vmu->context, wordlen)) {
- if (context && strcmp(context, vmu->context) && ++which > state)
- return ast_strdup(vmu->context);
- /* ignore repeated contexts ? */
- context = vmu->context;
- }
- }
- return NULL;
-}
-
-/*! \brief Show a list of voicemail users in the CLI */
-static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_vm_user *vmu;
- char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
- const char *context = NULL;
- int users_counter = 0;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "voicemail show users";
- e->usage =
- "Usage: voicemail show users [for <context>]\n"
- " Lists all mailboxes currently set up\n";
- return NULL;
- case CLI_GENERATE:
- return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
- }
-
- if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
- return CLI_SHOWUSAGE;
- if (a->argc == 5) {
- if (strcmp(a->argv[3], "for"))
- return CLI_SHOWUSAGE;
- context = a->argv[4];
- }
-
- if (ast_check_realtime("voicemail")) {
- if (!context) {
- ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
- return CLI_SHOWUSAGE;
- }
- return show_users_realtime(a->fd, context);
- }
-
- AST_LIST_LOCK(&users);
- if (AST_LIST_EMPTY(&users)) {
- ast_cli(a->fd, "There are no voicemail users currently defined\n");
- AST_LIST_UNLOCK(&users);
- return CLI_FAILURE;
- }
- if (a->argc == 3)
- ast_cli(a->fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
- else {
- int count = 0;
- AST_LIST_TRAVERSE(&users, vmu, list) {
- if (!strcmp(context, vmu->context))
- count++;
- }
- if (count) {
- ast_cli(a->fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
- } else {
- ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
- AST_LIST_UNLOCK(&users);
- return CLI_FAILURE;
- }
- }
- AST_LIST_TRAVERSE(&users, vmu, list) {
- int newmsgs = 0, oldmsgs = 0;
- char count[12], tmp[256] = "";
-
- if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
- snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
- inboxcount(tmp, &newmsgs, &oldmsgs);
- snprintf(count, sizeof(count), "%d", newmsgs);
- ast_cli(a->fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
- users_counter++;
- }
- }
- AST_LIST_UNLOCK(&users);
- ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
- return CLI_SUCCESS;
-}
-
-/*! \brief Show a list of voicemail zones in the CLI */
-static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct vm_zone *zone;
- char *output_format = "%-15s %-20s %-45s\n";
- char *res = CLI_SUCCESS;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "voicemail show zones";
- e->usage =
- "Usage: voicemail show zones\n"
- " Lists zone message formats\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
-
- AST_LIST_LOCK(&zones);
- if (!AST_LIST_EMPTY(&zones)) {
- ast_cli(a->fd, output_format, "Zone", "Timezone", "Message Format");
- AST_LIST_TRAVERSE(&zones, zone, list) {
- ast_cli(a->fd, output_format, zone->name, zone->timezone, zone->msg_format);
- }
- } else {
- ast_cli(a->fd, "There are no voicemail zones currently defined\n");
- res = CLI_FAILURE;
- }
- AST_LIST_UNLOCK(&zones);
-
- return res;
-}
-
-/*! \brief Reload voicemail configuration from the CLI */
-static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- switch (cmd) {
- case CLI_INIT:
- e->command = "voicemail reload";
- e->usage =
- "Usage: voicemail reload\n"
- " Reload voicemail configuration\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc != 2)
- return CLI_SHOWUSAGE;
-
- ast_cli(a->fd, "Reloading voicemail configuration...\n");
- load_config(1);
-
- return CLI_SUCCESS;
-}
-
-static struct ast_cli_entry cli_voicemail[] = {
- AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
- AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
- AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
-};
-
-static void poll_subscribed_mailboxes(void)
-{
- struct mwi_sub *mwi_sub;
-
- AST_RWLIST_RDLOCK(&mwi_subs);
- AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
- int new = 0, old = 0;
-
- if (ast_strlen_zero(mwi_sub->mailbox))
- continue;
-
- inboxcount(mwi_sub->mailbox, &new, &old);
-
- if (new != mwi_sub->old_new || old != mwi_sub->old_old) {
- mwi_sub->old_new = new;
- mwi_sub->old_old = old;
- queue_mwi_event(mwi_sub->mailbox, new, old);
- }
- }
- AST_RWLIST_UNLOCK(&mwi_subs);
-}
-
-static void *mb_poll_thread(void *data)
-{
- while (poll_thread_run) {
- struct timespec ts = { 0, };
- struct timeval tv;
-
- tv = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
- ts.tv_sec = tv.tv_sec;
- ts.tv_nsec = tv.tv_usec * 1000;
-
- ast_mutex_lock(&poll_lock);
- ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
- ast_mutex_unlock(&poll_lock);
-
- if (!poll_thread_run)
- break;
-
- poll_subscribed_mailboxes();
- }
-
- return NULL;
-}
-
-static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
-{
- ast_free(mwi_sub);
-}
-
-static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
-{
- uint32_t uniqueid;
- struct mwi_sub *mwi_sub;
-
- if (ast_event_get_type(event) != AST_EVENT_UNSUB)
- return;
-
- if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
- return;
-
- uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
-
- AST_RWLIST_WRLOCK(&mwi_subs);
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
- if (mwi_sub->uniqueid == uniqueid) {
- AST_LIST_REMOVE_CURRENT(entry);
- break;
- }
- }
- AST_RWLIST_TRAVERSE_SAFE_END
- AST_RWLIST_UNLOCK(&mwi_subs);
-
- if (mwi_sub)
- mwi_sub_destroy(mwi_sub);
-}
-
-static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
-{
- const char *mailbox;
- uint32_t uniqueid;
- unsigned int len;
- struct mwi_sub *mwi_sub;
-
- if (ast_event_get_type(event) != AST_EVENT_SUB)
- return;
-
- if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
- return;
-
- mailbox = ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX);
- uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
-
- len = sizeof(*mwi_sub);
- if (!ast_strlen_zero(mailbox))
- len += strlen(mailbox);
-
- if (!(mwi_sub = ast_calloc(1, len)))
- return;
-
- mwi_sub->uniqueid = uniqueid;
- if (!ast_strlen_zero(mailbox))
- strcpy(mwi_sub->mailbox, mailbox);
-
- AST_RWLIST_WRLOCK(&mwi_subs);
- AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
- AST_RWLIST_UNLOCK(&mwi_subs);
-}
-
-static void start_poll_thread(void)
-{
- pthread_attr_t attr;
-
- mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
- AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
- AST_EVENT_IE_END);
-
- mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
- AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
- AST_EVENT_IE_END);
-
- if (mwi_sub_sub)
- ast_event_report_subs(mwi_sub_sub);
-
- poll_thread_run = 1;
-
- pthread_attr_init(&attr);
- ast_pthread_create(&poll_thread, &attr, mb_poll_thread, NULL);
- pthread_attr_destroy(&attr);
-}
-
-static void stop_poll_thread(void)
-{
- poll_thread_run = 0;
-
- if (mwi_sub_sub) {
- ast_event_unsubscribe(mwi_sub_sub);
- mwi_sub_sub = NULL;
- }
-
- if (mwi_unsub_sub) {
- ast_event_unsubscribe(mwi_unsub_sub);
- mwi_unsub_sub = NULL;
- }
-
- ast_mutex_lock(&poll_lock);
- ast_cond_signal(&poll_cond);
- ast_mutex_unlock(&poll_lock);
-
- pthread_join(poll_thread, NULL);
-
- poll_thread = AST_PTHREADT_NULL;
-}
-
-/*! \brief Manager list voicemail users command */
-static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
-{
- struct ast_vm_user *vmu = NULL;
- const char *id = astman_get_header(m, "ActionID");
- char actionid[128] = "";
-
- if (!ast_strlen_zero(id))
- snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
-
- AST_LIST_LOCK(&users);
-
- if (AST_LIST_EMPTY(&users)) {
- astman_send_ack(s, m, "There are no voicemail users currently defined.");
- AST_LIST_UNLOCK(&users);
- return RESULT_SUCCESS;
- }
-
- astman_send_ack(s, m, "Voicemail user list will follow\r\n");
-
- AST_LIST_TRAVERSE(&users, vmu, list) {
- char dirname[256];
-
-#ifdef IMAP_STORAGE
- int new, old;
- inboxcount (vmu->mailbox, &new, &old);
-#endif
-
- make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
- astman_append(s,
- "%s"
- "Event: VoicemailUserEntry\r\n"
- "VMContext: %s\r\n"
- "VoiceMailbox: %s\r\n"
- "Fullname: %s\r\n"
- "Email: %s\r\n"
- "Pager: %s\r\n"
- "ServerEmail: %s\r\n"
- "MailCommand: %s\r\n"
- "Language: %s\r\n"
- "TimeZone: %s\r\n"
- "Callback: %s\r\n"
- "Dialout: %s\r\n"
- "UniqueID: %s\r\n"
- "ExitContext: %s\r\n"
- "SayDurationMinimum: %d\r\n"
- "SayEnvelope: %s\r\n"
- "SayCID: %s\r\n"
- "AttachMessage: %s\r\n"
- "AttachmentFormat: %s\r\n"
- "DeleteMessage: %s\r\n"
- "VolumeGain: %.2f\r\n"
- "CanReview: %s\r\n"
- "CallOperator: %s\r\n"
- "MaxMessageCount: %d\r\n"
- "MaxMessageLength: %d\r\n"
- "NewMessageCount: %d\r\n"
-#ifdef IMAP_STORAGE
- "OldMessageCount: %d\r\n"
- "IMAPUser: %s\r\n"
-#endif
- "\r\n",
- actionid,
- vmu->context,
- vmu->mailbox,
- vmu->fullname,
- vmu->email,
- vmu->pager,
- vmu->serveremail,
- vmu->mailcmd,
- vmu->language,
- vmu->zonetag,
- vmu->callback,
- vmu->dialout,
- vmu->uniqueid,
- vmu->exit,
- vmu->saydurationm,
- ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
- ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
- ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
- vmu->attachfmt,
- ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
- vmu->volgain,
- ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
- ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
- vmu->maxmsg,
- vmu->maxsecs,
-#ifdef IMAP_STORAGE
- new, old, vmu->imapuser
-#else
- count_messages(vmu, dirname)
-#endif
- );
- }
- astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
-
- AST_LIST_UNLOCK(&users);
-
- return RESULT_SUCCESS;
-}
-
-static int load_config(int reload)
-{
- struct ast_vm_user *cur;
- struct vm_zone *zcur;
- struct ast_config *cfg, *ucfg;
- char *cat;
- struct ast_variable *var;
- const char *val;
- const char *s;
- const char *key;
- char *q, *stringp;
- int x;
- int tmpadsi[4];
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
-
- if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
- if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
- ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
- cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
- } else {
- ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
- ucfg = ast_config_load("users.conf", config_flags);
- }
-
- /* set audio control prompts */
- strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
- strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
- strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
- strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
- strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
-
- AST_LIST_LOCK(&users);
- while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
- ast_set_flag(cur, VM_ALLOCED);
- free_user(cur);
- }
-
- AST_LIST_LOCK(&zones);
- while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
- free_zone(zcur);
- AST_LIST_UNLOCK(&zones);
-
- memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
-
- if (cfg) {
- /* General settings */
-
- if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
- val = "default";
- ast_copy_string(userscontext, val, sizeof(userscontext));
- /* Attach voice message to mail message ? */
- if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
- val = "yes";
- ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);
-
- if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
- val = "no";
- ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
-
- volgain = 0.0;
- if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
- sscanf(val, "%lf", &volgain);
-
-#ifdef ODBC_STORAGE
- strcpy(odbc_database, "asterisk");
- if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
- ast_copy_string(odbc_database, val, sizeof(odbc_database));
- }
- strcpy(odbc_table, "voicemessages");
- if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
- ast_copy_string(odbc_table, val, sizeof(odbc_table));
- }
-#endif
- /* Mail command */
- strcpy(mailcmd, SENDMAIL);
- if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
- ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
-
- maxsilence = 0;
- if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
- maxsilence = atoi(val);
- if (maxsilence > 0)
- maxsilence *= 1000;
- }
-
- if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
- maxmsg = MAXMSG;
- } else {
- maxmsg = atoi(val);
- if (maxmsg <= 0) {
- ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
- maxmsg = MAXMSG;
- } else if (maxmsg > MAXMSGLIMIT) {
- ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
- maxmsg = MAXMSGLIMIT;
- }
- }
-
- if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
- maxdeletedmsg = 0;
- } else {
- if (sscanf(val, "%d", &x) == 1)
- maxdeletedmsg = x;
- else if (ast_true(val))
- maxdeletedmsg = MAXMSG;
- else
- maxdeletedmsg = 0;
-
- if (maxdeletedmsg < 0) {
- ast_log(LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
- maxdeletedmsg = MAXMSG;
- } else if (maxdeletedmsg > MAXMSGLIMIT) {
- ast_log(LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
- maxdeletedmsg = MAXMSGLIMIT;
- }
- }
-
- /* Load date format config for voicemail mail */
- if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
- ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
- }
-
- /* External password changing command */
- if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
- ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
- pwdchange = PWDCHANGE_EXTERNAL;
- } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
- ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
- pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
- }
-
-#ifdef IMAP_STORAGE
- /* IMAP server address */
- if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
- ast_copy_string(imapserver, val, sizeof(imapserver));
- } else {
- ast_copy_string(imapserver, "localhost", sizeof(imapserver));
- }
- /* IMAP server port */
- if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
- ast_copy_string(imapport, val, sizeof(imapport));
- } else {
- ast_copy_string(imapport, "143", sizeof(imapport));
- }
- /* IMAP server flags */
- if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
- ast_copy_string(imapflags, val, sizeof(imapflags));
- }
- /* IMAP server master username */
- if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
- ast_copy_string(authuser, val, sizeof(authuser));
- }
- /* IMAP server master password */
- if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
- ast_copy_string(authpassword, val, sizeof(authpassword));
- }
- /* Expunge on exit */
- if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
- if (ast_false(val))
- expungeonhangup = 0;
- else
- expungeonhangup = 1;
- } else {
- expungeonhangup = 1;
- }
- /* IMAP voicemail folder */
- if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
- ast_copy_string(imapfolder, val, sizeof(imapfolder));
- } else {
- ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
- }
- if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
- imapgreetings = ast_true(val);
- } else {
- imapgreetings = 0;
- }
- if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
- ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
- } else {
- ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
- }
-
- /* There is some very unorthodox casting done here. This is due
- * to the way c-client handles the argument passed in. It expects a
- * void pointer and casts the pointer directly to a long without
- * first dereferencing it. */
- if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
- mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
- } else {
- mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
- }
-
- if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
- mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
- } else {
- mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
- }
-
- if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
- mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
- } else {
- mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
- }
-
- if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
- mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
- } else {
- mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
- }
-
-#endif
- /* External voicemail notify application */
- if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
- ast_copy_string(externnotify, val, sizeof(externnotify));
- ast_debug(1, "found externnotify: %s\n", externnotify);
- } else {
- externnotify[0] = '\0';
- }
-
- /* SMDI voicemail notification */
- if ((s = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(s)) {
- ast_debug(1, "Enabled SMDI voicemail notification\n");
- if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
- smdi_iface = ast_smdi_interface_find(val);
- } else {
- ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
- smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
- }
- if (!smdi_iface) {
- ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
- } else {
- ast_debug(1, "Using SMDI port %s\n", smdi_iface->name);
- }
- }
-
- /* Silence treshold */
- silencethreshold = 256;
- if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
- silencethreshold = atoi(val);
-
- if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
- val = ASTERISK_USERNAME;
- ast_copy_string(serveremail, val, sizeof(serveremail));
-
- vmmaxsecs = 0;
- if ((s = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
- if (sscanf(s, "%d", &x) == 1) {
- vmmaxsecs = x;
- } else {
- ast_log(LOG_WARNING, "Invalid max message time length\n");
- }
- } else if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
- static int maxmessage_deprecate = 0;
- if (maxmessage_deprecate == 0) {
- maxmessage_deprecate = 1;
- ast_log(LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
- }
- if (sscanf(s, "%d", &x) == 1) {
- vmmaxsecs = x;
- } else {
- ast_log(LOG_WARNING, "Invalid max message time length\n");
- }
- }
-
- vmminsecs = 0;
- if ((s = ast_variable_retrieve(cfg, "general", "minsecs"))) {
- if (sscanf(s, "%d", &x) == 1) {
- vmminsecs = x;
- if (maxsilence <= vmminsecs)
- ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
- } else {
- ast_log(LOG_WARNING, "Invalid min message time length\n");
- }
- } else if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
- static int maxmessage_deprecate = 0;
- if (maxmessage_deprecate == 0) {
- maxmessage_deprecate = 1;
- ast_log(LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
- }
- if (sscanf(s, "%d", &x) == 1) {
- vmminsecs = x;
- if (maxsilence <= vmminsecs)
- ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
- } else {
- ast_log(LOG_WARNING, "Invalid min message time length\n");
- }
- }
-
- val = ast_variable_retrieve(cfg, "general", "format");
- if (!val)
- val = "wav";
- ast_copy_string(vmfmts, val, sizeof(vmfmts));
-
- skipms = 3000;
- if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
- if (sscanf(s, "%d", &x) == 1) {
- maxgreet = x;
- } else {
- ast_log(LOG_WARNING, "Invalid max message greeting length\n");
- }
- }
-
- if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
- if (sscanf(s, "%d", &x) == 1) {
- skipms = x;
- } else {
- ast_log(LOG_WARNING, "Invalid skipms value\n");
- }
- }
-
- maxlogins = 3;
- if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
- if (sscanf(s, "%d", &x) == 1) {
- maxlogins = x;
- } else {
- ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
- }
- }
-
- /* Force new user to record name ? */
- if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
- val = "no";
- ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
-
- /* Force new user to record greetings ? */
- if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
- val = "no";
- ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
-
- if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
- ast_debug(1, "VM_CID Internal context string: %s\n", s);
- stringp = ast_strdupa(s);
- for (x = 0; x < MAX_NUM_CID_CONTEXTS; x++) {
- if (!ast_strlen_zero(stringp)) {
- q = strsep(&stringp, ",");
- while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
- q++;
- ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
- ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
- } else {
- cidinternalcontexts[x][0] = '\0';
- }
- }
- }
- if (!(val = ast_variable_retrieve(cfg, "general", "review"))) {
- ast_debug(1, "VM Review Option disabled globally\n");
- val = "no";
- }
- ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
-
- /*Temporary greeting reminder */
- if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
- ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
- val = "no";
- } else {
- ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
- }
- ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
-
- if (!(val = ast_variable_retrieve(cfg, "general", "operator"))) {
- ast_debug(1, "VM Operator break disabled globally\n");
- val = "no";
- }
- ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
-
- if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
- ast_debug(1, "VM CID Info before msg disabled globally\n");
- val = "no";
- }
- ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
-
- if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))) {
- ast_debug(1, "Send Voicemail msg disabled globally\n");
- val = "no";
- }
- ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
-
- if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
- ast_debug(1, "ENVELOPE before msg enabled globally\n");
- val = "yes";
- }
- ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
-
- if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
- ast_debug(1, "Move Heard enabled globally\n");
- val = "yes";
- }
- ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
-
- if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
- ast_debug(1, "Duration info before msg enabled globally\n");
- val = "yes";
- }
- ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
-
- saydurationminfo = 2;
- if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
- if (sscanf(val, "%d", &x) == 1) {
- saydurationminfo = x;
- } else {
- ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
- }
- }
-
- if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
- ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
- val = "no";
- }
- ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
-
- if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
- ast_copy_string(dialcontext, val, sizeof(dialcontext));
- ast_debug(1, "found dialout context: %s\n", dialcontext);
- } else {
- dialcontext[0] = '\0';
- }
-
- if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
- ast_copy_string(callcontext, val, sizeof(callcontext));
- ast_debug(1, "found callback context: %s\n", callcontext);
- } else {
- callcontext[0] = '\0';
- }
-
- if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
- ast_copy_string(exitcontext, val, sizeof(exitcontext));
- ast_debug(1, "found operator context: %s\n", exitcontext);
- } else {
- exitcontext[0] = '\0';
- }
-
- /* load password sounds configuration */
- if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
- ast_copy_string(vm_password, val, sizeof(vm_password));
- if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
- ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
- if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
- ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
- if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
- ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
- if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
- ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
- /* load configurable audio prompts */
- if ((key = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(key))
- ast_copy_string(listen_control_forward_key, key, sizeof(listen_control_forward_key));
- if ((key = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(key))
- ast_copy_string(listen_control_reverse_key, key, sizeof(listen_control_reverse_key));
- if ((key = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(key))
- ast_copy_string(listen_control_pause_key, key, sizeof(listen_control_pause_key));
- if ((key = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(key))
- ast_copy_string(listen_control_restart_key, key, sizeof(listen_control_restart_key));
- if ((key = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(key))
- ast_copy_string(listen_control_stop_key, key, sizeof(listen_control_stop_key));
-
- if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
- val = "no";
- ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);
-
- poll_freq = DEFAULT_POLL_FREQ;
- if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
- if (sscanf(val, "%u", &poll_freq) != 1) {
- poll_freq = DEFAULT_POLL_FREQ;
- ast_log(LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
- }
- }
-
- poll_mailboxes = 0;
- if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
- poll_mailboxes = ast_true(val);
-
- if (ucfg) {
- for (cat = ast_category_browse(ucfg, NULL); cat; cat = ast_category_browse(ucfg, cat)) {
- if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
- continue;
- if ((cur = find_or_create(userscontext, cat))) {
- populate_defaults(cur);
- apply_options_full(cur, ast_variable_browse(ucfg, cat));
- ast_copy_string(cur->context, userscontext, sizeof(cur->context));
- }
- }
- ast_config_destroy(ucfg);
- }
- cat = ast_category_browse(cfg, NULL);
- while (cat) {
- if (strcasecmp(cat, "general")) {
- var = ast_variable_browse(cfg, cat);
- if (strcasecmp(cat, "zonemessages")) {
- /* Process mailboxes in this context */
- while (var) {
- append_mailbox(cat, var->name, var->value);
- var = var->next;
- }
- } else {
- /* Timezones in this context */
- while (var) {
- struct vm_zone *z;
- if ((z = ast_malloc(sizeof(*z)))) {
- char *msg_format, *timezone;
- msg_format = ast_strdupa(var->value);
- timezone = strsep(&msg_format, "|");
- if (msg_format) {
- ast_copy_string(z->name, var->name, sizeof(z->name));
- ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
- ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
- AST_LIST_LOCK(&zones);
- AST_LIST_INSERT_HEAD(&zones, z, list);
- AST_LIST_UNLOCK(&zones);
- } else {
- ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
- ast_free(z);
- }
- } else {
- AST_LIST_UNLOCK(&users);
- ast_config_destroy(cfg);
- return -1;
- }
- var = var->next;
- }
- }
- }
- cat = ast_category_browse(cfg, cat);
- }
- memset(fromstring, 0, sizeof(fromstring));
- memset(pagerfromstring, 0, sizeof(pagerfromstring));
- memset(emailtitle, 0, sizeof(emailtitle));
- strcpy(charset, "ISO-8859-1");
- if (emailbody) {
- ast_free(emailbody);
- emailbody = NULL;
- }
- if (emailsubject) {
- ast_free(emailsubject);
- emailsubject = NULL;
- }
- if (pagerbody) {
- ast_free(pagerbody);
- pagerbody = NULL;
- }
- if (pagersubject) {
- ast_free(pagersubject);
- pagersubject = NULL;
- }
- if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
- ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
- if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
- ast_copy_string(fromstring, s, sizeof(fromstring));
- if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
- ast_copy_string(pagerfromstring, s, sizeof(pagerfromstring));
- if ((s = ast_variable_retrieve(cfg, "general", "charset")))
- ast_copy_string(charset, s, sizeof(charset));
- if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
- sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
- for (x = 0; x < 4; x++) {
- memcpy(&adsifdn[x], &tmpadsi[x], 1);
- }
- }
- if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
- sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
- for (x = 0; x < 4; x++) {
- memcpy(&adsisec[x], &tmpadsi[x], 1);
- }
- }
- if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
- if (atoi(s)) {
- adsiver = atoi(s);
- }
- if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
- ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
- ast_copy_string(emailtitle, s, sizeof(emailtitle));
- }
- if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
- emailsubject = ast_strdup(s);
- if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
- char *tmpread, *tmpwrite;
- emailbody = ast_strdup(s);
-
- /* substitute strings \t and \n into the appropriate characters */
- tmpread = tmpwrite = emailbody;
- while ((tmpwrite = strchr(tmpread, '\\'))) {
- switch (tmpwrite[1]) {
- case 'r':
- memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
- *tmpwrite = '\r';
- break;
- case 'n':
- memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
- *tmpwrite = '\n';
- break;
- case 't':
- memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
- *tmpwrite = '\t';
- break;
- default:
- ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
- }
- tmpread = tmpwrite + 1;
- }
- }
- if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
- pagersubject = ast_strdup(s);
- if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
- char *tmpread, *tmpwrite;
- pagerbody = ast_strdup(s);
-
- /* substitute strings \t and \n into the appropriate characters */
- tmpread = tmpwrite = pagerbody;
- while ((tmpwrite = strchr(tmpread, '\\'))) {
- switch (tmpwrite[1]) {
- case 'r':
- memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
- *tmpwrite = '\r';
- break;
- case 'n':
- memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
- *tmpwrite = '\n';
- break;
- case 't':
- memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
- *tmpwrite = '\t';
- break;
- default:
- ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
- }
- tmpread = tmpwrite + 1;
- }
- }
- AST_LIST_UNLOCK(&users);
- ast_config_destroy(cfg);
-
- if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
- start_poll_thread();
- if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
- stop_poll_thread();;
-
- return 0;
- } else {
- AST_LIST_UNLOCK(&users);
- ast_log(LOG_WARNING, "Failed to load configuration file.\n");
- if (ucfg)
- ast_config_destroy(ucfg);
- return 0;
- }
-}
-
-static int reload(void)
-{
- return load_config(1);
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(app);
- res |= ast_unregister_application(app2);
- res |= ast_unregister_application(app3);
- res |= ast_unregister_application(app4);
- res |= ast_custom_function_unregister(&mailbox_exists_acf);
- res |= ast_manager_unregister("VoicemailUsersList");
- ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
- ast_uninstall_vm_functions();
-
- if (poll_thread != AST_PTHREADT_NULL)
- stop_poll_thread();
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
- my_umask = umask(0);
- umask(my_umask);
-
- /* compute the location of the voicemail spool directory */
- snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
-
- if ((res = load_config(0)))
- return res;
-
- res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
- res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
- res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
- res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
- res |= ast_custom_function_register(&mailbox_exists_acf);
- res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
- if (res)
- return res;
-
- ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
-
- ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
-
- return res;
-}
-
-static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
-{
- int cmd = 0;
- char destination[80] = "";
- int retries = 0;
-
- if (!num) {
- ast_verb(3, "Destination number will be entered manually\n");
- while (retries < 3 && cmd != 't') {
- destination[1] = '\0';
- destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
- if (!cmd)
- destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
- if (!cmd)
- destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
- if (!cmd) {
- cmd = ast_waitfordigit(chan, 6000);
- if (cmd)
- destination[0] = cmd;
- }
- if (!cmd) {
- retries++;
- } else {
-
- if (cmd < 0)
- return 0;
- if (cmd == '*') {
- ast_verb(3, "User hit '*' to cancel outgoing call\n");
- return 0;
- }
- if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination)-1, 6000, 10000, "#")) < 0)
- retries++;
- else
- cmd = 't';
- }
- }
- if (retries >= 3) {
- return 0;
- }
-
- } else {
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
- ast_copy_string(destination, num, sizeof(destination));
- }
-
- if (!ast_strlen_zero(destination)) {
- if (destination[strlen(destination) -1 ] == '*')
- return 0;
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
- ast_copy_string(chan->exten, destination, sizeof(chan->exten));
- ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
- chan->priority = 0;
- return 9;
- }
- return 0;
-}
-
-static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
-{
- int res = 0;
-#ifdef IMAP_STORAGE
- char origtimeS[256], cidN[256], cid[256], contextS[256];
- char *header_content, *temp;
- char buf[1024];
-#else
- char *cid;
- struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
-#endif
- char filename[PATH_MAX];
- struct ast_config *msg_cfg = NULL;
- const char *origtime, *context;
- char *name, *num;
- int retries = 0;
-
- vms->starting = 0;
-#ifdef IMAP_STORAGE
- /* START HERE */
-
- /* get the message info!! */
- ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", vms->curmsg, vms->msgArray[vms->curmsg]);
-
- if (!vms->msgArray[vms->curmsg]) {
- ast_log(LOG_WARNING, "Trying to access unknown message\n");
- return -1;
- }
-
- /* This will only work for new messages... */
- if (!(header_content = mail_fetchheader(vms->mailstream, vms->msgArray[vms->curmsg])) || ast_strlen_zero(header_content)) {
- ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[vms->curmsg]);
- return -1;
- }
-
- /* Get info from headers!! */
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))))
- ast_copy_string(cidN, temp, sizeof(cidN));
- else
- cidN[0] = '\0';
-
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))))
- snprintf(cid, sizeof(cid), "\"%s\" <%s>", cidN, temp);
- else
-
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))))
- ast_copy_string(contextS, temp, sizeof(contextS));
- else
- contextS[0] = '\0';
-
- context = &contextS[0];
-
- if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))))
- ast_copy_string(origtimeS, temp, sizeof(origtimeS));
- else
- origtimeS[0] = '\0';
-
- origtime = &origtimeS[0];
-
- ast_copy_string(filename, "IMAP_STORAGE", sizeof(filename));
-#else
- make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
-
- /* Retrieve info from VM attribute file */
-
- make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
- snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
- RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
- msg_cfg = ast_config_load(filename, config_flags);
- DISPOSE(vms->curdir, vms->curmsg);
- if (!msg_cfg) {
- ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
- return 0;
- }
-
- if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
- ast_config_destroy(msg_cfg);
- return 0;
- }
-
- cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
-
- context = ast_variable_retrieve(msg_cfg, "message", "context");
- if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
- context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
-#endif
- switch (option) {
- case 3:
- if (!res)
- res = play_message_datetime(chan, vmu, origtime, filename);
- if (!res)
- res = play_message_callerid(chan, vms, cid, context, 0);
-
- res = 't';
- break;
-
- case 2: /* Call back */
-
- if (ast_strlen_zero(cid))
- break;
-
- ast_callerid_parse(cid, &name, &num);
- while ((res > -1) && (res != 't')) {
- switch (res) {
- case '1':
- if (num) {
- /* Dial the CID number */
- res = dialout(chan, vmu, num, vmu->callback);
- if (res) {
- ast_config_destroy(msg_cfg);
- return 9;
- }
- } else {
- res = '2';
- }
- break;
-
- case '2':
- /* Want to enter a different number, can only do this if there's a dialout context for this user */
- if (!ast_strlen_zero(vmu->dialout)) {
- res = dialout(chan, vmu, NULL, vmu->dialout);
- if (res) {
- ast_config_destroy(msg_cfg);
- return 9;
- }
- } else {
- ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
- res = ast_play_and_wait(chan, "vm-sorry");
- }
- ast_config_destroy(msg_cfg);
- return res;
- case '*':
- res = 't';
- break;
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '0':
-
- res = ast_play_and_wait(chan, "vm-sorry");
- retries++;
- break;
- default:
- if (num) {
- ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
- res = ast_play_and_wait(chan, "vm-num-i-have");
- if (!res)
- res = play_message_callerid(chan, vms, num, vmu->context, 1);
- if (!res)
- res = ast_play_and_wait(chan, "vm-tocallnum");
- /* Only prompt for a caller-specified number if there is a dialout context specified */
- if (!ast_strlen_zero(vmu->dialout)) {
- if (!res)
- res = ast_play_and_wait(chan, "vm-calldiffnum");
- }
- } else {
- res = ast_play_and_wait(chan, "vm-nonumber");
- if (!ast_strlen_zero(vmu->dialout)) {
- if (!res)
- res = ast_play_and_wait(chan, "vm-toenternumber");
- }
- }
- if (!res)
- res = ast_play_and_wait(chan, "vm-star-cancel");
- if (!res)
- res = ast_waitfordigit(chan, 6000);
- if (!res) {
- retries++;
- if (retries > 3)
- res = 't';
- }
- break;
-
- }
- if (res == 't')
- res = 0;
- else if (res == '*')
- res = -1;
- }
- break;
-
- case 1: /* Reply */
- /* Send reply directly to sender */
- if (ast_strlen_zero(cid))
- break;
-
- ast_callerid_parse(cid, &name, &num);
- if (!num) {
- ast_verb(3, "No CID number available, no reply sent\n");
- if (!res)
- res = ast_play_and_wait(chan, "vm-nonumber");
- ast_config_destroy(msg_cfg);
- return res;
- } else {
- struct ast_vm_user vmu2;
- if (find_user(&vmu2, vmu->context, num)) {
- struct leave_vm_options leave_options;
- char mailbox[AST_MAX_EXTENSION * 2 + 2];
- snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
-
- ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
-
- memset(&leave_options, 0, sizeof(leave_options));
- leave_options.record_gain = record_gain;
- res = leave_voicemail(chan, mailbox, &leave_options);
- if (!res)
- res = 't';
- ast_config_destroy(msg_cfg);
- return res;
- } else {
- /* Sender has no mailbox, can't reply */
- ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
- ast_play_and_wait(chan, "vm-nobox");
- res = 't';
- ast_config_destroy(msg_cfg);
- return res;
- }
- }
- res = 0;
-
- break;
- }
-
-#ifndef IMAP_STORAGE
- ast_config_destroy(msg_cfg);
-
- if (!res) {
- make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
- vms->heard[msg] = 1;
- res = wait_file(chan, vms, vms->fn);
- }
-#endif
- return res;
-}
-
-static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
- int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
- signed char record_gain, struct vm_state *vms)
-{
- /* Record message & let caller review or re-record it, or set options if applicable */
- int res = 0;
- int cmd = 0;
- int max_attempts = 3;
- int attempts = 0;
- int recorded = 0;
- int message_exists = 0;
- signed char zero_gain = 0;
- char tempfile[PATH_MAX];
- char *acceptdtmf = "#";
- char *canceldtmf = "";
-
- /* Note that urgent and private are for flagging messages as such in the future */
-
- /* barf if no pointer passed to store duration in */
- if (duration == NULL) {
- ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
- return -1;
- }
-
- if (!outsidecaller)
- snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
- else
- ast_copy_string(tempfile, recordfile, sizeof(tempfile));
-
- cmd = '3'; /* Want to start by recording */
-
- while ((cmd >= 0) && (cmd != 't')) {
- switch (cmd) {
- case '1':
- if (!message_exists) {
- /* In this case, 1 is to record a message */
- cmd = '3';
- break;
- } else {
- /* Otherwise 1 is to save the existing message */
- ast_verb(3, "Saving message as is\n");
- if (!outsidecaller)
- ast_filerename(tempfile, recordfile, NULL);
- ast_stream_and_wait(chan, "vm-msgsaved", "");
- if (!outsidecaller) {
- STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
- DISPOSE(recordfile, -1);
- }
- cmd = 't';
- return res;
- }
- case '2':
- /* Review */
- ast_verb(3, "Reviewing the message\n");
- cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
- break;
- case '3':
- message_exists = 0;
- /* Record */
- if (recorded == 1)
- ast_verb(3, "Re-recording the message\n");
- else
- ast_verb(3, "Recording the message\n");
-
- if (recorded && outsidecaller) {
- cmd = ast_play_and_wait(chan, INTRO);
- cmd = ast_play_and_wait(chan, "beep");
- }
- recorded = 1;
- /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
- if (record_gain)
- ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
- if (ast_test_flag(vmu, VM_OPERATOR))
- canceldtmf = "0";
- cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
- if (record_gain)
- ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
- if (cmd == -1) {
- /* User has hung up, no options to give */
- if (!outsidecaller) {
- /* user was recording a greeting and they hung up, so let's delete the recording. */
- ast_filedelete(tempfile, NULL);
- }
- return cmd;
- }
- if (cmd == '0') {
- break;
- } else if (cmd == '*') {
- break;
- }
-#if 0
- else if (vmu->review && (*duration < 5)) {
- /* Message is too short */
- ast_verb(3, "Message too short\n");
- cmd = ast_play_and_wait(chan, "vm-tooshort");
- cmd = ast_filedelete(tempfile, NULL);
- break;
- }
- else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
- /* Message is all silence */
- ast_verb(3, "Nothing recorded\n");
- cmd = ast_filedelete(tempfile, NULL);
- cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-speakup");
- break;
- }
-#endif
- else {
- /* If all is well, a message exists */
- message_exists = 1;
- cmd = 0;
- }
- break;
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '*':
- case '#':
- cmd = ast_play_and_wait(chan, "vm-sorry");
- break;
-#if 0
-/* XXX Commented out for the moment because of the dangers of deleting
- a message while recording (can put the message numbers out of sync) */
- case '*':
- /* Cancel recording, delete message, offer to take another message*/
- cmd = ast_play_and_wait(chan, "vm-deleted");
- cmd = ast_filedelete(tempfile, NULL);
- if (outsidecaller) {
- res = vm_exec(chan, NULL);
- return res;
- }
- else
- return 1;
-#endif
- case '0':
- if (!ast_test_flag(vmu, VM_OPERATOR)) {
- cmd = ast_play_and_wait(chan, "vm-sorry");
- break;
- }
- if (message_exists || recorded) {
- cmd = ast_play_and_wait(chan, "vm-saveoper");
- if (!cmd)
- cmd = ast_waitfordigit(chan, 3000);
- if (cmd == '1') {
- ast_play_and_wait(chan, "vm-msgsaved");
- cmd = '0';
- } else {
- ast_play_and_wait(chan, "vm-deleted");
- DELETE(recordfile, -1, recordfile);
- cmd = '0';
- }
- }
- return cmd;
- default:
- /* If the caller is an ouside caller, and the review option is enabled,
- allow them to review the message, but let the owner of the box review
- their OGM's */
- if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
- return cmd;
- if (message_exists) {
- cmd = ast_play_and_wait(chan, "vm-review");
- }
- else {
- cmd = ast_play_and_wait(chan, "vm-torerecord");
- if (!cmd)
- cmd = ast_waitfordigit(chan, 600);
- }
-
- if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
- cmd = ast_play_and_wait(chan, "vm-reachoper");
- if (!cmd)
- cmd = ast_waitfordigit(chan, 600);
- }
-#if 0
- if (!cmd)
- cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
-#endif
- if (!cmd)
- cmd = ast_waitfordigit(chan, 6000);
- if (!cmd) {
- attempts++;
- }
- if (attempts > max_attempts) {
- cmd = 't';
- }
- }
- }
- if (outsidecaller)
- ast_play_and_wait(chan, "vm-goodbye");
- if (cmd == 't')
- cmd = 0;
- return cmd;
-}
-
-#ifdef IMAP_STORAGE
-
-static void write_file(char *filename, char *buffer, unsigned long len)
-{
- FILE *output;
-
- output = fopen (filename, "w");
- fwrite (buffer, len, 1, output);
- fclose (output);
-}
-
-static void update_messages_by_imapuser(const char *user, unsigned long number)
-{
- struct vmstate *vlist = NULL;
-
- AST_LIST_LOCK(&vmstates);
- AST_LIST_TRAVERSE(&vmstates, vlist, list) {
- if (!vlist->vms) {
- ast_debug(3, "error: vms is NULL for %s\n", user);
- continue;
- }
- if (!vlist->vms->imapuser) {
- ast_debug(3, "error: imapuser is NULL for %s\n", user);
- continue;
- }
- ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vlist->vms->vmArrayIndex, vlist->vms->interactive);
- vlist->vms->msgArray[vlist->vms->vmArrayIndex++] = number;
- }
- AST_LIST_UNLOCK(&vmstates);
-}
-
-void mm_searched(MAILSTREAM *stream, unsigned long number)
-{
- char *mailbox = stream->mailbox, buf[1024] = "", *user;
-
- if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
- return;
-
- update_messages_by_imapuser(user, number);
-}
-
-static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
-{
- struct ast_variable *var;
- struct ast_vm_user *vmu;
-
- vmu = ast_calloc(1, sizeof *vmu);
- if (!vmu)
- return NULL;
- ast_set_flag(vmu, VM_ALLOCED);
- populate_defaults(vmu);
-
- var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
- if (var) {
- apply_options_full(vmu, var);
- ast_variables_destroy(var);
- return vmu;
- } else {
- ast_free(vmu);
- return NULL;
- }
-}
-
-/* Interfaces to C-client */
-
-void mm_exists(MAILSTREAM * stream, unsigned long number)
-{
- /* mail_ping will callback here if new mail! */
- ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
- if (number == 0) return;
- set_update(stream);
-}
-
-
-void mm_expunged(MAILSTREAM * stream, unsigned long number)
-{
- /* mail_ping will callback here if expunged mail! */
- ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
- if (number == 0) return;
- set_update(stream);
-}
-
-
-void mm_flags(MAILSTREAM * stream, unsigned long number)
-{
- /* mail_ping will callback here if read mail! */
- ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
- if (number == 0) return;
- set_update(stream);
-}
-
-
-void mm_notify(MAILSTREAM * stream, char *string, long errflg)
-{
- mm_log (string, errflg);
-}
-
-
-void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
-{
- if (delimiter == '\0') {
- delimiter = delim;
- }
-
- ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
- if (attributes & LATT_NOINFERIORS)
- ast_debug(5, "no inferiors\n");
- if (attributes & LATT_NOSELECT)
- ast_debug(5, "no select\n");
- if (attributes & LATT_MARKED)
- ast_debug(5, "marked\n");
- if (attributes & LATT_UNMARKED)
- ast_debug(5, "unmarked\n");
-}
-
-
-void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
-{
- ast_debug(5, "Delimiter set to %c and mailbox %s\n", delimiter, mailbox);
- if (attributes & LATT_NOINFERIORS)
- ast_debug(5, "no inferiors\n");
- if (attributes & LATT_NOSELECT)
- ast_debug(5, "no select\n");
- if (attributes & LATT_MARKED)
- ast_debug(5, "marked\n");
- if (attributes & LATT_UNMARKED)
- ast_debug(5, "unmarked\n");
-}
-
-
-void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
-{
- ast_log(LOG_NOTICE, " Mailbox %s", mailbox);
- if (status->flags & SA_MESSAGES)
- ast_log(LOG_NOTICE, ", %lu messages", status->messages);
- if (status->flags & SA_RECENT)
- ast_log(LOG_NOTICE, ", %lu recent", status->recent);
- if (status->flags & SA_UNSEEN)
- ast_log(LOG_NOTICE, ", %lu unseen", status->unseen);
- if (status->flags & SA_UIDVALIDITY)
- ast_log(LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
- if (status->flags & SA_UIDNEXT)
- ast_log(LOG_NOTICE, ", %lu next UID", status->uidnext);
- ast_log(LOG_NOTICE, "\n");
-}
-
-
-void mm_log(char *string, long errflg)
-{
- switch ((short) errflg) {
- case NIL:
- ast_debug(1, "IMAP Info: %s\n", string);
- break;
- case PARSE:
- case WARN:
- ast_log(LOG_WARNING, "IMAP Warning: %s\n", string);
- break;
- case ERROR:
- ast_log(LOG_ERROR, "IMAP Error: %s\n", string);
- break;
- }
-}
-
-
-void mm_dlog(char *string)
-{
- ast_log(LOG_NOTICE, "%s\n", string);
-}
-
-
-void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
-{
- struct ast_vm_user *vmu;
-
- ast_debug(4, "Entering callback mm_login\n");
-
- ast_copy_string(user, mb->user, MAILTMPLEN);
-
- /* We should only do this when necessary */
- if (!ast_strlen_zero(authpassword)) {
- ast_copy_string(pwd, authpassword, MAILTMPLEN);
- } else {
- AST_LIST_TRAVERSE(&users, vmu, list) {
- if (!strcasecmp(mb->user, vmu->imapuser)) {
- ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
- break;
- }
- }
- if (!vmu) {
- if ((vmu = find_user_realtime_imapuser(mb->user))) {
- ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
- free_user(vmu);
- }
- }
- }
-}
-
-
-void mm_critical(MAILSTREAM * stream)
-{
-}
-
-
-void mm_nocritical(MAILSTREAM * stream)
-{
-}
-
-
-long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
-{
- kill (getpid (), SIGSTOP);
- return NIL;
-}
-
-
-void mm_fatal(char *string)
-{
- ast_log(LOG_ERROR, "IMAP access FATAL error: %s\n", string);
-}
-
-/* C-client callback to handle quota */
-static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
-{
- struct vm_state *vms;
- char *mailbox = stream->mailbox, *user;
- char buf[1024] = "";
- unsigned long usage = 0, limit = 0;
-
- while (pquota) {
- usage = pquota->usage;
- limit = pquota->limit;
- pquota = pquota->next;
- }
-
- if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 2))) {
- ast_log(LOG_ERROR, "No state found.\n");
- return;
- }
-
- ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
-
- vms->quota_usage = usage;
- vms->quota_limit = limit;
-}
-
-static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
-{
- char *start, *eol_pnt;
- int taglen;
-
- if (ast_strlen_zero(header) || ast_strlen_zero(tag))
- return NULL;
-
- taglen = strlen(tag) + 1;
- if (taglen < 1)
- return NULL;
-
- if (!(start = strstr(header, tag)))
- return NULL;
-
- /* Since we can be called multiple times we should clear our buffer */
- memset(buf, 0, len);
-
- ast_copy_string(buf, start+taglen, len);
- if ((eol_pnt = strchr(buf, '\r')) || (eol_pnt = strchr(buf, '\n')))
- *eol_pnt = '\0';
- return buf;
-}
-
-static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
-{
- char *start, *quote, *eol_pnt;
-
- if (ast_strlen_zero(mailbox))
- return NULL;
-
- if (!(start = strstr(mailbox, "/user=")))
- return NULL;
-
- ast_copy_string(buf, start + 6, len);
-
- if (!(quote = strchr(buf, '\"'))) {
- if (!(eol_pnt = strchr(buf, '/')))
- eol_pnt = strchr(buf, '}');
- *eol_pnt = '\0';
- return buf;
- } else {
- eol_pnt = strchr(buf + 1, '\"');
- *eol_pnt = '\0';
- return buf + 1;
- }
-}
-
-static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
-{
- struct vmstate *vlist = NULL;
-
- AST_LIST_LOCK(&vmstates);
- AST_LIST_TRAVERSE(&vmstates, vlist, list) {
- if (!vlist->vms) {
- ast_debug(3, "error: vms is NULL for %s\n", user);
- continue;
- }
- if (!vlist->vms->imapuser) {
- ast_debug(3, "error: imapuser is NULL for %s\n", user);
- continue;
- }
-
- if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
- AST_LIST_UNLOCK(&vmstates);
- return vlist->vms;
- }
- }
- AST_LIST_UNLOCK(&vmstates);
-
- ast_debug(3, "%s not found in vmstates\n", user);
-
- return NULL;
-}
-
-static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
-{
-
- struct vmstate *vlist = NULL;
-
- AST_LIST_LOCK(&vmstates);
- AST_LIST_TRAVERSE(&vmstates, vlist, list) {
- if (!vlist->vms) {
- ast_debug(3, "error: vms is NULL for %s\n", mailbox);
- continue;
- }
- if (!vlist->vms->username) {
- ast_debug(3, "error: username is NULL for %s\n", mailbox);
- continue;
- }
-
- ast_debug(3, "comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n", mailbox, interactive, vlist->vms->username, vlist->vms->interactive);
-
- if (!strcmp(vlist->vms->username, mailbox) && vlist->vms->interactive == interactive) {
- ast_debug(3, "Found it!\n");
- AST_LIST_UNLOCK(&vmstates);
- return vlist->vms;
- }
- }
- AST_LIST_UNLOCK(&vmstates);
-
- ast_debug(3, "%s not found in vmstates\n", mailbox);
-
- return NULL;
-}
-
-static void vmstate_insert(struct vm_state *vms)
-{
- struct vmstate *v;
- struct vm_state *altvms;
-
- /* If interactive, it probably already exists, and we should
- use the one we already have since it is more up to date.
- We can compare the username to find the duplicate */
- if (vms->interactive == 1) {
- altvms = get_vm_state_by_mailbox(vms->username, 0);
- if (altvms) {
- ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
- vms->newmessages = altvms->newmessages;
- vms->oldmessages = altvms->oldmessages;
- ast_debug(3, "check_msgArray before memcpy\n");
- check_msgArray(vms);
- /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long) * 256); */
- copy_msgArray(vms, altvms);
- ast_debug(3, "check_msgArray after memcpy\n");
- check_msgArray(vms);
- vms->vmArrayIndex = altvms->vmArrayIndex;
- vms->lastmsg = altvms->lastmsg;
- vms->curmsg = altvms->curmsg;
- /* get a pointer to the persistent store */
- vms->persist_vms = altvms;
- /* Reuse the mailstream? */
- vms->mailstream = altvms->mailstream;
- /* vms->mailstream = NIL; */
- }
- }
-
- if (!(v = ast_calloc(1, sizeof(*v))))
- return;
-
- v->vms = vms;
-
- ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
-
- AST_LIST_LOCK(&vmstates);
- AST_LIST_INSERT_TAIL(&vmstates, v, list);
- AST_LIST_UNLOCK(&vmstates);
-}
-
-static void vmstate_delete(struct vm_state *vms)
-{
- struct vmstate *vc = NULL;
- struct vm_state *altvms = NULL;
-
- /* If interactive, we should copy pertinent info
- back to the persistent state (to make update immediate) */
- if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
- ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
- altvms->newmessages = vms->newmessages;
- altvms->oldmessages = vms->oldmessages;
- altvms->updated = 1;
- }
-
- ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
-
- AST_LIST_LOCK(&vmstates);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
- if (vc->vms == vms) {
- AST_LIST_REMOVE_CURRENT(list);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END
- AST_LIST_UNLOCK(&vmstates);
-
- if (vc)
- ast_free(vc);
- else
- ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
-}
-
-static void set_update(MAILSTREAM * stream)
-{
- struct vm_state *vms;
- char *mailbox = stream->mailbox, *user;
- char buf[1024] = "";
-
- if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
- if (user && option_debug > 2)
- ast_log(LOG_WARNING, "User %s mailbox not found for update.\n", user);
- return;
- }
-
- ast_debug(3, "User %s mailbox set for update.\n", user);
-
- vms->updated = 1; /* Set updated flag since mailbox changed */
-}
-
-static void init_vm_state(struct vm_state *vms)
-{
- int x;
- vms->vmArrayIndex = 0;
- for (x = 0; x < 256; x++) {
- vms->msgArray[x] = 0;
- }
-}
-
-static void check_msgArray(struct vm_state *vms)
-{
- int x;
- for (x = 0; x < 256; x++) {
- if (vms->msgArray[x] != 0) {
- ast_debug(1, "Item %d set to %ld\n", x, vms->msgArray[x]);
- }
- }
-}
-
-static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
-{
- int x;
- for (x = 0; x < 256; x++) {
- dst->msgArray[x] = src->msgArray[x];
- }
-}
-
-static int save_body(BODY *body, struct vm_state *vms, char *section, char *format)
-{
- char *body_content;
- char *body_decoded;
- unsigned long len;
- unsigned long newlen;
- char filename[256];
-
- if (!body || body == NIL)
- return -1;
- body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
- if (body_content != NIL) {
- snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
- /* ast_debug(1, body_content); */
- body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
- write_file (filename, (char *) body_decoded, newlen);
- }
- return 0;
-}
-
-/* get delimiter via mm_list callback */
-static void get_mailbox_delimiter(MAILSTREAM *stream) {
- char tmp[50];
- snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
- mail_list(stream, tmp, "*");
-}
-
-/* Check Quota for user */
-static void check_quota(struct vm_state *vms, char *mailbox) {
- mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
- ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
- if (vms && vms->mailstream != NULL) {
- imap_getquotaroot(vms->mailstream, mailbox);
- } else {
- ast_log(LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
- }
-}
-
-#endif /* IMAP_STORAGE */
-
-/* This is a workaround so that menuselect displays a proper description
- * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
- */
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
diff --git a/trunk/apps/app_waitforring.c b/trunk/apps/app_waitforring.c
deleted file mode 100644
index b4248fee9..000000000
--- a/trunk/apps/app_waitforring.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Wait for Ring Application
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/lock.h"
-
-static char *synopsis = "Wait for Ring Application";
-
-static char *desc = " WaitForRing(timeout):\n"
-"Returns 0 after waiting at least timeout seconds. and\n"
-"only after the next ring has completed. Returns 0 on\n"
-"success or -1 on hangup\n";
-
-static char *app = "WaitForRing";
-
-
-static int waitforring_exec(struct ast_channel *chan, void *data)
-{
- struct ast_frame *f;
- int res = 0;
- double s;
- int ms;
-
- if (!data || (sscanf(data, "%lg", &s) != 1)) {
- ast_log(LOG_WARNING, "WaitForRing requires an argument (minimum seconds)\n");
- return 0;
- }
-
- ms = s*1000.0;
- while(ms > 0) {
- ms = ast_waitfor(chan, ms);
- if (ms < 0) {
- res = ms;
- break;
- }
- if (ms > 0) {
- f = ast_read(chan);
- if (!f) {
- res = -1;
- break;
- }
- if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) {
- ast_verb(3, "Got a ring but still waiting for timeout\n");
- }
- ast_frfree(f);
- }
- }
- /* Now we're really ready for the ring */
- if (!res) {
- ms = 99999999;
- while(ms > 0) {
- ms = ast_waitfor(chan, ms);
- if (ms < 0) {
- res = ms;
- break;
- }
- if (ms > 0) {
- f = ast_read(chan);
- if (!f) {
- res = -1;
- break;
- }
- if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) {
- ast_verb(3, "Got a ring after the timeout\n");
- ast_frfree(f);
- break;
- }
- ast_frfree(f);
- }
- }
- }
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, waitforring_exec, synopsis, desc);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Waits until first ring after time");
diff --git a/trunk/apps/app_waitforsilence.c b/trunk/apps/app_waitforsilence.c
deleted file mode 100644
index e45f24344..000000000
--- a/trunk/apps/app_waitforsilence.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * WaitForSilence Application by David C. Troy <dave@popvox.com>
- * Version 1.11 2006-06-29
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Wait for Silence
- * - Waits for up to 'x' milliseconds of silence, 'y' times \n
- * - WaitForSilence(500,2) will wait for 1/2 second of silence, twice \n
- * - WaitForSilence(1000,1) will wait for 1 second of silence, once \n
- * - WaitForSilence(300,3,10) will wait for 300ms of silence, 3 times, and return after 10sec \n
- *
- * \author David C. Troy <dave@popvox.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/dsp.h"
-#include "asterisk/module.h"
-
-static char *app = "WaitForSilence";
-static char *synopsis = "Waits for a specified amount of silence";
-static char *descrip =
-" WaitForSilence(silencerequired[,iterations][,timeout]):\n"
-"Wait for Silence: Waits for up to 'silencerequired' \n"
-"milliseconds of silence, 'iterations' times or once if omitted.\n"
-"An optional timeout specified the number of seconds to return\n"
-"after, even if we do not receive the specified amount of silence.\n"
-"Use 'timeout' with caution, as it may defeat the purpose of this\n"
-"application, which is to wait indefinitely until silence is detected\n"
-"on the line. This is particularly useful for reverse-911-type\n"
-"call broadcast applications where you need to wait for an answering\n"
-"machine to complete its spiel before playing a message.\n"
-"The timeout parameter is specified only to avoid an infinite loop in\n"
-"cases where silence is never achieved. Typically you will want to\n"
-"include two or more calls to WaitForSilence when dealing with an answering\n"
-"machine; first waiting for the spiel to finish, then waiting for the beep, etc.\n\n"
- "Examples:\n"
-" - WaitForSilence(500,2) will wait for 1/2 second of silence, twice\n"
-" - WaitForSilence(1000) will wait for 1 second of silence, once\n"
-" - WaitForSilence(300,3,10) will wait for 300ms silence, 3 times,\n"
-" and returns after 10 sec, even if silence is not detected\n\n"
-"Sets the channel variable WAITSTATUS with to one of these values:\n"
-"SILENCE - if exited with silence detected\n"
-"TIMEOUT - if exited without silence detected after timeout\n";
-
-static int do_waiting(struct ast_channel *chan, int silencereqd, time_t waitstart, int timeout) {
- struct ast_frame *f;
- int dspsilence = 0;
- static int silencethreshold = 128;
- int rfmt = 0;
- int res = 0;
- struct ast_dsp *sildet; /* silence detector dsp */
- time_t now;
-
- rfmt = chan->readformat; /* Set to linear mode */
- res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
- if (res < 0) {
- ast_log(LOG_WARNING, "Unable to set channel to linear mode, giving up\n");
- return -1;
- }
-
- sildet = ast_dsp_new(); /* Create the silence detector */
- if (!sildet) {
- ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
- return -1;
- }
- ast_dsp_set_threshold(sildet, silencethreshold);
-
- /* Await silence... */
- f = NULL;
- for(;;) {
- /* Start with no silence received */
- dspsilence = 0;
-
- res = ast_waitfor(chan, silencereqd);
-
- /* Must have gotten a hangup; let's exit */
- if (res <= 0) {
- f = NULL;
- break;
- }
-
- /* We waited and got no frame; sounds like digital silence or a muted digital channel */
- if (!res) {
- dspsilence = silencereqd;
- } else {
- /* Looks like we did get a frame, so let's check it out */
- f = ast_read(chan);
- if (!f)
- break;
- if (f && f->frametype == AST_FRAME_VOICE) {
- ast_dsp_silence(sildet, f, &dspsilence);
- ast_frfree(f);
- }
- }
-
- ast_verb(3, "Got %dms silence< %dms required\n", dspsilence, silencereqd);
-
- if (dspsilence >= silencereqd) {
- ast_verb(3, "Exiting with %dms silence >= %dms required\n", dspsilence, silencereqd);
- /* Ended happily with silence */
- res = 1;
- pbx_builtin_setvar_helper(chan, "WAITSTATUS", "SILENCE");
- ast_debug(1, "WAITSTATUS was set to SILENCE\n");
- break;
- }
-
- if ( timeout && (difftime(time(&now),waitstart) >= timeout) ) {
- pbx_builtin_setvar_helper(chan, "WAITSTATUS", "TIMEOUT");
- ast_debug(1, "WAITSTATUS was set to TIMEOUT\n");
- res = 0;
- break;
- }
- }
-
-
- if (rfmt && ast_set_read_format(chan, rfmt)) {
- ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
- }
- ast_dsp_free(sildet);
- return res;
-}
-
-static int waitforsilence_exec(struct ast_channel *chan, void *data)
-{
- int res = 1;
- int silencereqd = 1000;
- int timeout = 0;
- int iterations = 1, i;
- time_t waitstart;
-
- res = ast_answer(chan); /* Answer the channel */
-
- if (!data || ( (sscanf(data, "%d,%d,%d", &silencereqd, &iterations, &timeout) != 3) &&
- (sscanf(data, "%d|%d", &silencereqd, &iterations) != 2) &&
- (sscanf(data, "%d", &silencereqd) != 1) ) ) {
- ast_log(LOG_WARNING, "Using default value of 1000ms, 1 iteration, no timeout\n");
- }
-
- ast_verb(3, "Waiting %d time(s) for %d ms silence with %d timeout\n", iterations, silencereqd, timeout);
-
- time(&waitstart);
- res = 1;
- for (i=0; (i<iterations) && (res == 1); i++) {
- res = do_waiting(chan, silencereqd, waitstart, timeout);
- }
- if (res > 0)
- res = 0;
- return res;
-}
-
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, waitforsilence_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Wait For Silence");
-
diff --git a/trunk/apps/app_waituntil.c b/trunk/apps/app_waituntil.c
deleted file mode 100644
index 1334683ce..000000000
--- a/trunk/apps/app_waituntil.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2007, Redfish Solutions
- *
- * Philip Prindeville <philipp@redfish-solutions.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Sleep until the given epoch
- *
- * \author Philip Prindeville <philipp@redfish-solutions.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/logger.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-
-static char *app = "WaitUntil";
-static char *synopsis = "Wait (sleep) until the current time is the given epoch";
-static char *descrip =
-" WaitUntil(<epoch>): Waits until the given time. Sets WAITUNTILSTATUS to\n"
-"one of the following values:\n"
-" OK Wait succeeded\n"
-" FAILURE Invalid argument\n"
-" HANGUP Channel hung up before time elapsed\n"
-" PAST The time specified was already past\n";
-
-static int waituntil_exec(struct ast_channel *chan, void *data)
-{
- int res;
- double fraction;
- struct timeval future = { 0, };
- struct timeval tv = ast_tvnow();
- int msec;
-
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "WaitUntil requires an argument(epoch)\n");
- pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "FAILURE");
- return 0;
- }
-
- if (sscanf(data, "%ld%lf", (long *)&future.tv_sec, &fraction) == 0) {
- ast_log(LOG_WARNING, "WaitUntil called with non-numeric argument\n");
- pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "FAILURE");
- return 0;
- }
-
- future.tv_usec = fraction * 1000000;
-
- if ((msec = ast_tvdiff_ms(future, tv)) < 0) {
- ast_log(LOG_NOTICE, "WaitUntil called in the past (now %ld, arg %ld)\n", (long)tv.tv_sec, (long)future.tv_sec);
- pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "PAST");
- return 0;
- }
-
- if ((res = ast_safe_sleep(chan, msec)))
- pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "HANGUP");
- else
- pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "OK");
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ast_register_application(app, waituntil_exec, synopsis, descrip);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Wait until specified time");
diff --git a/trunk/apps/app_while.c b/trunk/apps/app_while.c
deleted file mode 100644
index 1c61d966e..000000000
--- a/trunk/apps/app_while.c
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright 2004 - 2005, Anthony Minessale <anthmct@yahoo.com>
- *
- * Anthony Minessale <anthmct@yahoo.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief While Loop Implementation
- *
- * \author Anthony Minessale <anthmct@yahoo.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/channel.h"
-
-static char *start_app = "While";
-static char *start_desc =
-" While(<expr>): Start a While Loop. Execution will return to this\n"
-"point when EndWhile() is called until expr is no longer true.\n";
-
-static char *start_synopsis = "Start a while loop";
-
-
-static char *stop_app = "EndWhile";
-static char *stop_desc =
-" EndWhile(): Return to the previous called While()\n";
-
-static char *stop_synopsis = "End a while loop";
-
-static char *exit_app = "ExitWhile";
-static char *exit_desc =
-" ExitWhile(): Exits a While() loop, whether or not the conditional has been satisfied.\n";
-static char *exit_synopsis = "End a While loop";
-
-static char *continue_app = "ContinueWhile";
-static char *continue_desc =
-" ContinueWhile(): Returns to the top of the while loop and re-evaluates the conditional.\n";
-static char *continue_synopsis = "Restart a While loop";
-
-#define VAR_SIZE 64
-
-
-static const char *get_index(struct ast_channel *chan, const char *prefix, int index) {
- char varname[VAR_SIZE];
-
- snprintf(varname, VAR_SIZE, "%s_%d", prefix, index);
- return pbx_builtin_getvar_helper(chan, varname);
-}
-
-static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
-{
- struct ast_exten *e;
- struct ast_include *i;
- struct ast_context *c2;
-
- for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
- if (ast_extension_match(ast_get_extension_name(e), exten)) {
- int needmatch = ast_get_extension_matchcid(e);
- if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
- (!needmatch)) {
- /* This is the matching extension we want */
- struct ast_exten *p;
- for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
- if (priority != ast_get_extension_priority(p))
- continue;
- return p;
- }
- }
- }
- }
-
- /* No match; run through includes */
- for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
- for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
- if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
- e = find_matching_priority(c2, exten, priority, callerid);
- if (e)
- return e;
- }
- }
- }
- return NULL;
-}
-
-static int find_matching_endwhile(struct ast_channel *chan)
-{
- struct ast_context *c;
- int res=-1;
-
- if (ast_rdlock_contexts()) {
- ast_log(LOG_ERROR, "Failed to lock contexts list\n");
- return -1;
- }
-
- for (c=ast_walk_contexts(NULL); c; c=ast_walk_contexts(c)) {
- struct ast_exten *e;
-
- if (!ast_rdlock_context(c)) {
- if (!strcmp(ast_get_context_name(c), chan->context)) {
- /* This is the matching context we want */
- int cur_priority = chan->priority + 1, level=1;
-
- for (e = find_matching_priority(c, chan->exten, cur_priority, chan->cid.cid_num); e; e = find_matching_priority(c, chan->exten, ++cur_priority, chan->cid.cid_num)) {
- if (!strcasecmp(ast_get_extension_app(e), "WHILE")) {
- level++;
- } else if (!strcasecmp(ast_get_extension_app(e), "ENDWHILE")) {
- level--;
- }
-
- if (level == 0) {
- res = cur_priority;
- break;
- }
- }
- }
- ast_unlock_context(c);
- if (res > 0) {
- break;
- }
- }
- }
- ast_unlock_contexts();
- return res;
-}
-
-static int _while_exec(struct ast_channel *chan, void *data, int end)
-{
- int res=0;
- const char *while_pri = NULL;
- char *my_name = NULL;
- const char *condition = NULL, *label = NULL;
- char varname[VAR_SIZE], end_varname[VAR_SIZE];
- const char *prefix = "WHILE";
- size_t size=0;
- int used_index_i = -1, x=0;
- char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
-
- if (!chan) {
- /* huh ? */
- return -1;
- }
-
- /* dont want run away loops if the chan isn't even up
- this is up for debate since it slows things down a tad ......
- */
- if (ast_waitfordigit(chan,1) < 0)
- return -1;
-
- for (x=0;;x++) {
- if (get_index(chan, prefix, x)) {
- used_index_i = x;
- } else
- break;
- }
-
- snprintf(used_index, VAR_SIZE, "%d", used_index_i);
- snprintf(new_index, VAR_SIZE, "%d", used_index_i + 1);
-
- if (!end)
- condition = ast_strdupa(data);
-
- size = strlen(chan->context) + strlen(chan->exten) + 32;
- my_name = alloca(size);
- memset(my_name, 0, size);
- snprintf(my_name, size, "%s_%s_%d", chan->context, chan->exten, chan->priority);
-
- if (ast_strlen_zero(label)) {
- if (end)
- label = used_index;
- else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
- label = new_index;
- pbx_builtin_setvar_helper(chan, my_name, label);
- }
-
- }
-
- snprintf(varname, VAR_SIZE, "%s_%s", prefix, label);
- while_pri = pbx_builtin_getvar_helper(chan, varname);
-
- if ((while_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
- snprintf(end_varname,VAR_SIZE,"END_%s",varname);
- }
-
-
- if ((!end && !pbx_checkcondition(condition)) || (end == 2)) {
- /* Condition Met (clean up helper vars) */
- const char *goto_str;
- pbx_builtin_setvar_helper(chan, varname, NULL);
- pbx_builtin_setvar_helper(chan, my_name, NULL);
- snprintf(end_varname,VAR_SIZE,"END_%s",varname);
- if ((goto_str=pbx_builtin_getvar_helper(chan, end_varname))) {
- ast_parseable_goto(chan, goto_str);
- pbx_builtin_setvar_helper(chan, end_varname, NULL);
- } else {
- int pri = find_matching_endwhile(chan);
- if (pri > 0) {
- ast_verb(3, "Jumping to priority %d\n", pri);
- chan->priority = pri;
- } else {
- ast_log(LOG_WARNING, "Couldn't find matching EndWhile? (While at %s@%s priority %d)\n", chan->context, chan->exten, chan->priority);
- }
- }
- return res;
- }
-
- if (!end && !while_pri) {
- char *goto_str;
- size = strlen(chan->context) + strlen(chan->exten) + 32;
- goto_str = alloca(size);
- memset(goto_str, 0, size);
- snprintf(goto_str, size, "%s,%s,%d", chan->context, chan->exten, chan->priority);
- pbx_builtin_setvar_helper(chan, varname, goto_str);
- }
-
- else if (end && while_pri) {
- /* END of loop */
- snprintf(end_varname, VAR_SIZE, "END_%s", varname);
- if (! pbx_builtin_getvar_helper(chan, end_varname)) {
- char *goto_str;
- size = strlen(chan->context) + strlen(chan->exten) + 32;
- goto_str = alloca(size);
- memset(goto_str, 0, size);
- snprintf(goto_str, size, "%s,%s,%d", chan->context, chan->exten, chan->priority+1);
- pbx_builtin_setvar_helper(chan, end_varname, goto_str);
- }
- ast_parseable_goto(chan, while_pri);
- }
-
- return res;
-}
-
-static int while_start_exec(struct ast_channel *chan, void *data) {
- return _while_exec(chan, data, 0);
-}
-
-static int while_end_exec(struct ast_channel *chan, void *data) {
- return _while_exec(chan, data, 1);
-}
-
-static int while_exit_exec(struct ast_channel *chan, void *data) {
- return _while_exec(chan, data, 2);
-}
-
-static int while_continue_exec(struct ast_channel *chan, void *data)
-{
- int x;
- const char *prefix = "WHILE", *while_pri=NULL;
-
- for (x = 0; ; x++) {
- const char *tmp = get_index(chan, prefix, x);
- if (tmp)
- while_pri = tmp;
- else
- break;
- }
-
- if (while_pri)
- ast_parseable_goto(chan, while_pri);
-
- return 0;
-}
-
-static int unload_module(void)
-{
- int res;
-
- res = ast_unregister_application(start_app);
- res |= ast_unregister_application(stop_app);
- res |= ast_unregister_application(exit_app);
- res |= ast_unregister_application(continue_app);
-
- return res;
-}
-
-static int load_module(void)
-{
- int res;
-
- res = ast_register_application(start_app, while_start_exec, start_synopsis, start_desc);
- res |= ast_register_application(stop_app, while_end_exec, stop_synopsis, stop_desc);
- res |= ast_register_application(exit_app, while_exit_exec, exit_synopsis, exit_desc);
- res |= ast_register_application(continue_app, while_continue_exec, continue_synopsis, continue_desc);
-
- return res;
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "While Loops and Conditional Execution");
diff --git a/trunk/apps/app_zapateller.c b/trunk/apps/app_zapateller.c
deleted file mode 100644
index 2641e69b4..000000000
--- a/trunk/apps/app_zapateller.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Playback the special information tone to get rid of telemarketers
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/app.h"
-
-static char *app = "Zapateller";
-
-static char *synopsis = "Block telemarketers with SIT";
-
-static char *descrip =
-" Zapateller(options): Generates special information tone to block\n"
-"telemarketers from calling you. Options is a pipe-delimited list of\n"
-"options. The following options are available:\n"
-" 'answer' - causes the line to be answered before playing the tone,\n"
-" 'nocallerid' - causes Zapateller to only play the tone if there is no\n"
-" callerid information available. Options should be\n"
-" separated by , characters\n\n"
-" This application will set the following channel variable upon completion:\n"
-" ZAPATELLERSTATUS - This will contain the last action accomplished by the\n"
-" Zapateller application. Possible values include:\n"
-" NOTHING | ANSWERED | ZAPPED\n\n";
-
-
-static int zapateller_exec(struct ast_channel *chan, void *data)
-{
- int res = 0;
- int i, answer = 0, nocallerid = 0;
- char *parse = ast_strdupa((char *)data);
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(options)[2];
- );
-
- AST_STANDARD_APP_ARGS(args, parse);
-
- for (i = 0; i < args.argc; i++) {
- if (!strcasecmp(args.options[i], "answer"))
- answer = 1;
- else if (!strcasecmp(args.options[i], "nocallerid"))
- nocallerid = 1;
- }
-
- pbx_builtin_setvar_helper(chan, "ZAPATELLERSTATUS", "NOTHING");
- ast_stopstream(chan);
- if (chan->_state != AST_STATE_UP) {
- if (answer) {
- res = ast_answer(chan);
- pbx_builtin_setvar_helper(chan, "ZAPATELLERSTATUS", "ANSWERED");
- }
- if (!res)
- res = ast_safe_sleep(chan, 500);
- }
-
- if (!ast_strlen_zero(chan->cid.cid_num) && nocallerid)
- return res;
-
- if (!res)
- res = ast_tonepair(chan, 950, 0, 330, 0);
- if (!res)
- res = ast_tonepair(chan, 1400, 0, 330, 0);
- if (!res)
- res = ast_tonepair(chan, 1800, 0, 330, 0);
- if (!res)
- res = ast_tonepair(chan, 0, 0, 1000, 0);
-
- pbx_builtin_setvar_helper(chan, "ZAPATELLERSTATUS", "ZAPPED");
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ((ast_register_application(app, zapateller_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Block Telemarketers with Special Information Tone");
diff --git a/trunk/apps/app_zapbarge.c b/trunk/apps/app_zapbarge.c
deleted file mode 100644
index bb8c4cbb3..000000000
--- a/trunk/apps/app_zapbarge.c
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * Special thanks to comphealth.com for sponsoring this
- * GPL application.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Zap Barge support
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \note Special thanks to comphealth.com for sponsoring this
- * GPL application.
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>zaptel</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/zapata.h"
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/config.h"
-#include "asterisk/app.h"
-#include "asterisk/cli.h"
-#include "asterisk/say.h"
-#include "asterisk/utils.h"
-
-static char *app = "ZapBarge";
-
-static char *synopsis = "Barge in (monitor) Zap channel";
-
-static char *descrip =
-" ZapBarge([channel]): Barges in on a specified zap\n"
-"channel or prompts if one is not specified. Returns\n"
-"-1 when caller user hangs up and is independent of the\n"
-"state of the channel being monitored.";
-
-
-#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->tech->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_debug(1, "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_debug(1, "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_debug(1, "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;
- ast_frfree(f);
- 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;
- int retrycnt = 0;
- int confflags = 0;
- int confno = 0;
- char confstr[80] = "";
-
- if (!ast_strlen_zero(data)) {
- if ((sscanf(data, "Zap/%d", &confno) != 1) &&
- (sscanf(data, "%d", &confno) != 1)) {
- ast_log(LOG_WARNING, "ZapBarge Argument (if specified) must be a channel number, not '%s'\n", (char *)data);
- return 0;
- }
- }
-
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- while(!confno && (++retrycnt < 4)) {
- /* Prompt user for conference number */
- confstr[0] = '\0';
- res = ast_app_getdata(chan, "conf-getchannel",confstr, sizeof(confstr) - 1, 0);
- if (res <0) goto out;
- if (sscanf(confstr, "%d", &confno) != 1)
- confno = 0;
- }
- if (confno) {
- /* XXX Should prompt user for pin if pin is required XXX */
- /* Run the conference */
- res = conf_run(chan, confno, confflags);
- }
-out:
- /* Do the conference */
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ((ast_register_application(app, conf_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Barge in on Zap channel application");
diff --git a/trunk/apps/app_zapras.c b/trunk/apps/app_zapras.c
deleted file mode 100644
index 480e5f643..000000000
--- a/trunk/apps/app_zapras.c
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Execute an ISDN RAS
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>zaptel</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#ifdef __linux__
-#include <sys/signal.h>
-#else
-#include <signal.h>
-#endif /* __linux__ */
-
-#include <fcntl.h>
-
-#include "asterisk/zapata.h"
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-
-static char *app = "ZapRAS";
-
-static char *synopsis = "Executes Zaptel ISDN RAS application";
-
-static char *descrip =
-" ZapRAS(args): Executes a RAS server using pppd on the given channel.\n"
-"The channel must be a clear channel (i.e. PRI source) and a Zaptel\n"
-"channel to be able to use this function (No modem emulation is included).\n"
-"Your pppd must be patched to be zaptel aware. Arguments should be\n"
-"separated by , characters.\n";
-
-
-#define PPP_MAX_ARGS 32
-#define PPP_EXEC "/usr/sbin/pppd"
-
-static pid_t spawn_ras(struct ast_channel *chan, char *args)
-{
- pid_t pid;
- int x;
- char *c;
-
- char *argv[PPP_MAX_ARGS];
- int argc = 0;
- char *stringp=NULL;
- sigset_t fullset, oldset;
-
- sigfillset(&fullset);
- pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
- /* Start by forking */
- pid = fork();
- if (pid) {
- pthread_sigmask(SIG_SETMASK, &oldset, NULL);
- return pid;
- }
-
- /* Restore original signal handlers */
- for (x=0;x<NSIG;x++)
- signal(x, SIG_DFL);
-
- pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
-
- /* Execute RAS on File handles */
- dup2(chan->fds[0], STDIN_FILENO);
-
- /* Drop high priority */
- if (ast_opt_high_priority)
- ast_set_priority(0);
-
- /* Close other file descriptors */
- for (x=STDERR_FILENO + 1;x<1024;x++)
- close(x);
-
- /* Reset all arguments */
- memset(argv, 0, sizeof(argv));
-
- /* First argument is executable, followed by standard
- arguments for zaptel PPP */
- argv[argc++] = PPP_EXEC;
- argv[argc++] = "nodetach";
-
- /* And all the other arguments */
- stringp=args;
- c = strsep(&stringp, ",");
- while(c && strlen(c) && (argc < (PPP_MAX_ARGS - 4))) {
- argv[argc++] = c;
- c = strsep(&stringp, ",");
- }
-
- argv[argc++] = "plugin";
- argv[argc++] = "zaptel.so";
- argv[argc++] = "stdin";
-
- /* Finally launch PPP */
- execv(PPP_EXEC, argv);
- fprintf(stderr, "Failed to exec PPPD!\n");
- exit(1);
-}
-
-static void run_ras(struct ast_channel *chan, char *args)
-{
- pid_t pid;
- int status;
- int res;
- int signalled = 0;
- struct zt_bufferinfo savebi;
- int x;
-
- res = ioctl(chan->fds[0], ZT_GET_BUFINFO, &savebi);
- if(res) {
- ast_log(LOG_WARNING, "Unable to check buffer policy on channel %s\n", chan->name);
- return;
- }
-
- pid = spawn_ras(chan, args);
- if (pid < 0) {
- ast_log(LOG_WARNING, "Failed to spawn RAS\n");
- } else {
- for (;;) {
- res = wait4(pid, &status, WNOHANG, NULL);
- if (!res) {
- /* Check for hangup */
- if (ast_check_hangup(chan) && !signalled) {
- ast_debug(1, "Channel '%s' hungup. Signalling RAS at %d to die...\n", chan->name, pid);
- kill(pid, SIGTERM);
- signalled=1;
- }
- /* Try again */
- sleep(1);
- continue;
- }
- if (res < 0) {
- ast_log(LOG_WARNING, "wait4 returned %d: %s\n", res, strerror(errno));
- }
- if (option_verbose > 2) {
- if (WIFEXITED(status)) {
- ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with status %d\n", chan->name, WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with signal %d\n",
- chan->name, WTERMSIG(status));
- } else {
- ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated weirdly.\n", chan->name);
- }
- }
- /* Throw back into audio mode */
- x = 1;
- ioctl(chan->fds[0], ZT_AUDIOMODE, &x);
-
- /* Restore saved values */
- res = ioctl(chan->fds[0], ZT_SET_BUFINFO, &savebi);
- if (res < 0) {
- ast_log(LOG_WARNING, "Unable to set buffer policy on channel %s\n", chan->name);
- }
- break;
- }
- }
-}
-
-static int zapras_exec(struct ast_channel *chan, void *data)
-{
- int res=-1;
- char *args;
- ZT_PARAMS ztp;
-
- if (!data)
- data = "";
-
- args = ast_strdupa(data);
-
- /* Answer the channel if it's not up */
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
- if (strcasecmp(chan->tech->type, "Zap")) {
- /* If it's not a zap channel, we're done. Wait a couple of
- seconds and then hangup... */
- ast_verb(2, "Channel %s is not a Zap channel\n", chan->name);
- sleep(2);
- } else {
- memset(&ztp, 0, sizeof(ztp));
- if (ioctl(chan->fds[0], ZT_GET_PARAMS, &ztp)) {
- ast_log(LOG_WARNING, "Unable to get zaptel parameters\n");
- } else if (ztp.sigtype != ZT_SIG_CLEAR) {
- ast_verb(2, "Channel %s is not a clear channel\n", chan->name);
- } else {
- /* Everything should be okay. Run PPP. */
- ast_verb(3, "Starting RAS on %s\n", chan->name);
- /* Execute RAS */
- run_ras(chan, args);
- }
- }
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ((ast_register_application(app, zapras_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Zaptel ISDN Remote Access Server");
-
diff --git a/trunk/apps/app_zapscan.c b/trunk/apps/app_zapscan.c
deleted file mode 100644
index 5367d99d1..000000000
--- a/trunk/apps/app_zapscan.c
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * Modified from app_zapbarge by David Troy <dave@toad.net>
- *
- * Special thanks to comphealth.com for sponsoring this
- * GPL application.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief Zap Scanner
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>zaptel</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/zapata.h"
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/config.h"
-#include "asterisk/app.h"
-#include "asterisk/utils.h"
-#include "asterisk/cli.h"
-#include "asterisk/say.h"
-
-static char *app = "ZapScan";
-
-static char *synopsis = "Scan Zap channels to monitor calls";
-
-static char *descrip =
-" ZapScan([group]) allows a call center manager to monitor Zap channels in\n"
-"a convenient way. Use '#' to select the next channel and use '*' to exit\n"
-"Limit scanning to a channel GROUP by setting the option group argument.\n";
-
-
-#define CONF_SIZE 160
-
-static struct ast_channel *get_zap_channel_locked(int num) {
- char name[80];
-
- snprintf(name,sizeof(name),"Zap/%d-1",num);
- return ast_get_channel_by_name_locked(name);
-}
-
-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;
- char input[4];
- int ic=0;
-
- 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->tech->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_debug(1, "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_debug(1, "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_debug(1, "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) {
- if(f->subclass == '#') {
- ret = 0;
- break;
- }
- else if (f->subclass == '*') {
- ret = -1;
- break;
-
- }
- else {
- input[ic++] = f->subclass;
- }
- if(ic == 3) {
- input[ic++] = '\0';
- ic=0;
- ret = atoi(input);
- ast_verb(3, "Zapscan: change channel to %d\n",ret);
- break;
- }
- }
-
- 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 (f)
- ast_frfree(f);
- 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;
- int confflags = 0;
- int confno = 0;
- char confstr[80] = "", *tmp = NULL;
- struct ast_channel *tempchan = NULL, *lastchan = NULL,*ichan = NULL;
- struct ast_frame *f;
- char *desired_group;
- int input=0,search_group=0;
-
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- desired_group = ast_strdupa(data);
- if(!ast_strlen_zero(desired_group)) {
- ast_verb(3, "Scanning for group %s\n", desired_group);
- search_group = 1;
- }
-
- for (;;) {
- if (ast_waitfor(chan, 100) < 0)
- break;
-
- f = ast_read(chan);
- if (!f)
- break;
- if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) {
- ast_frfree(f);
- break;
- }
- ast_frfree(f);
- ichan = NULL;
- if(input) {
- ichan = get_zap_channel_locked(input);
- input = 0;
- }
-
- tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
-
- if ( !tempchan && !lastchan )
- break;
-
- if (tempchan && search_group) {
- const char *mygroup;
- if((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) {
- ast_verb(3, "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
- } else {
- ast_channel_unlock(tempchan);
- lastchan = tempchan;
- continue;
- }
- }
- if (tempchan && (!strcmp(tempchan->tech->type, "Zap")) && (tempchan != chan) ) {
- ast_verb(3, "Zap channel %s is in-use, monitoring...\n", tempchan->name);
- ast_copy_string(confstr, tempchan->name, sizeof(confstr));
- ast_channel_unlock(tempchan);
- if ((tmp = strchr(confstr,'-'))) {
- *tmp = '\0';
- }
- confno = atoi(strchr(confstr,'/') + 1);
- ast_stopstream(chan);
- ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
- res = conf_run(chan, confno, confflags);
- if (res<0) break;
- input = res;
- } else if (tempchan)
- ast_channel_unlock(tempchan);
- lastchan = tempchan;
- }
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ((ast_register_application(app, conf_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan Zap channels application");
-
diff --git a/trunk/apps/enter.h b/trunk/apps/enter.h
deleted file mode 100644
index ac765984a..000000000
--- a/trunk/apps/enter.h
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * U-law 8-bit audio data
- *
- * Source: enter.raw
- *
- * Copyright (C) 1999, Mark Spencer and Linux Support Services
- *
- * Distributed under the terms of the GNU General Public License
- *
- */
-
-static unsigned char enter[] = {
-0xba, 0xba, 0xb0, 0xa6, 0xa9, 0xb8, 0xfe, 0x46, 0x42, 0x46,
-0x4a, 0xfe, 0xac, 0xa2, 0x9f, 0x9f, 0xa8, 0xb8, 0x3b, 0x29,
-0x35, 0x4a, 0xfe, 0xc1, 0xad, 0xa2, 0xad, 0xc5, 0x4e, 0x68,
-0x68, 0xe7, 0xb8, 0xb0, 0xb2, 0xc1, 0xc1, 0xb0, 0xae, 0xcd,
-0xfe, 0xfe, 0xcd, 0xcd, 0xfe, 0x68, 0xd3, 0xb2, 0xae, 0xab,
-0xb2, 0xfe, 0x35, 0x31, 0xdb, 0xac, 0xab, 0xaf, 0xab, 0xaa,
-0xb4, 0x68, 0x3b, 0x39, 0x3f, 0x68, 0xb4, 0xa8, 0xa8, 0xb0,
-0xbc, 0xbc, 0xc5, 0x3f, 0x31, 0x37, 0xfe, 0xc1, 0xbc, 0xb0,
-0xa5, 0xa2, 0xa8, 0xaf, 0xbe, 0x3b, 0x28, 0x26, 0x3d, 0xbc,
-0xb0, 0xae, 0xa2, 0x9f, 0xa2, 0xfe, 0x29, 0x24, 0x29, 0x4a,
-0xc5, 0xaa, 0xa8, 0xa9, 0xa8, 0xa5, 0xa7, 0xdb, 0x2c, 0x27,
-0x2d, 0x4a, 0xfe, 0xdb, 0xb2, 0xa2, 0x9f, 0x9f, 0xae, 0xe7,
-0x2c, 0x22, 0x2b, 0xfe, 0xba, 0xb0, 0xaa, 0x9f, 0xa3, 0xb0,
-0x5c, 0x33, 0x33, 0x39, 0x5c, 0xdb, 0xc1, 0xb4, 0xb0, 0xaa,
-0xad, 0xba, 0x54, 0x46, 0xfe, 0xe7, 0xfe, 0x54, 0xe7, 0xaf,
-0xa6, 0xa7, 0xb0, 0xfe, 0x46, 0x39, 0x5c, 0xe7, 0xdb, 0xfe,
-0xba, 0xac, 0xa8, 0xc5, 0x46, 0x33, 0x54, 0xc5, 0xae, 0xad,
-0xb2, 0xc1, 0xcd, 0xc1, 0xbc, 0xfe, 0x3f, 0x37, 0xfe, 0xb4,
-0xb6, 0xcd, 0xdb, 0xc1, 0xb0, 0xb6, 0xcd, 0x4e, 0x39, 0x37,
-0xfe, 0xb0, 0xab, 0xa9, 0xa9, 0xa9, 0xb0, 0x5c, 0x29, 0x25,
-0x31, 0xfe, 0xc1, 0xb4, 0xae, 0xab, 0xab, 0xb2, 0xcd, 0x3b,
-0x2a, 0x2c, 0x54, 0xb4, 0xb4, 0xba, 0xb2, 0xa3, 0x9f, 0xa8,
-0xfe, 0x33, 0x27, 0x2a, 0x39, 0xfe, 0xc1, 0xbe, 0xb0, 0xa2,
-0x9f, 0xb0, 0x33, 0x22, 0x25, 0x46, 0xc1, 0xb8, 0xb0, 0xab,
-0xa8, 0xa8, 0xb0, 0xbe, 0x42, 0x2c, 0x2e, 0x4a, 0xfe, 0x5c,
-0xfe, 0xb4, 0xa8, 0xa8, 0xba, 0xfe, 0x4a, 0x39, 0x39, 0x46,
-0xfe, 0xbc, 0xaf, 0xa5, 0xa5, 0xae, 0x68, 0x37, 0x4a, 0xfe,
-0xfe, 0x4a, 0x4a, 0xd3, 0xb0, 0xb0, 0xc1, 0x5c, 0x46, 0x46,
-0xd3, 0xb6, 0xbe, 0x54, 0x54, 0xc9, 0xab, 0xae, 0xc5, 0x46,
-0x4a, 0xfe, 0xcd, 0xc9, 0xcd, 0xe7, 0xe7, 0xc9, 0xb4, 0xc5,
-0x4a, 0x2c, 0x37, 0xc1, 0xb0, 0xb2, 0xb4, 0xb2, 0xb6, 0xdb,
-0xfe, 0x4a, 0x46, 0x3f, 0x68, 0xba, 0xb2, 0xba, 0xc5, 0xb6,
-0xb2, 0xcd, 0x33, 0x2e, 0x39, 0x68, 0xfe, 0xe7, 0xba, 0xaf,
-0xa7, 0xa7, 0xad, 0xe7, 0x2d, 0x25, 0x2f, 0xd3, 0xbe, 0xcd,
-0xc5, 0xac, 0xa6, 0xac, 0xfe, 0x3b, 0x2c, 0x2d, 0x3d, 0xc1,
-0xb4, 0xbe, 0xcd, 0xaf, 0xa5, 0xa8, 0xe7, 0x31, 0x2f, 0x39,
-0x46, 0x5c, 0xdb, 0xbc, 0xba, 0xaf, 0xa9, 0xad, 0xfe, 0x2f,
-0x2d, 0xba, 0xad, 0xba, 0xfe, 0x3d, 0x42, 0x5c, 0xc9, 0xc1,
-0xcd, 0xfe, 0xc1, 0xae, 0xa6, 0xcd, 0x33, 0x25, 0x3b, 0xdb,
-0xb0, 0xb6, 0xb8, 0xb6, 0xb4, 0xb8, 0xba, 0xfe, 0x3d, 0x37,
-0xfe, 0xba, 0xc1, 0x54, 0x54, 0xd3, 0xb0, 0xb4, 0xe7, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xd3, 0xb6, 0xa9, 0xa7, 0xba,
-0x3d, 0x35, 0xfe, 0xc1, 0xcd, 0x4a, 0x54, 0xbe, 0xb2, 0xb8,
-0xfe, 0x46, 0x3b, 0xfe, 0xba, 0xab, 0xc5, 0x46, 0x3b, 0xbc,
-0xaa, 0xab, 0xd3, 0x68, 0xfe, 0xd3, 0xcd, 0xdb, 0x54, 0x3d,
-0x4a, 0xbc, 0xac, 0xb4, 0x3f, 0x2e, 0x3d, 0xba, 0xb0, 0xb8,
-0xba, 0xb6, 0xba, 0xcd, 0xfe, 0xfe, 0x5c, 0x54, 0xc9, 0xb4,
-0xbe, 0x54, 0x54, 0xcd, 0xb6, 0xc9, 0x46, 0x54, 0xcd, 0xc5,
-0xdb, 0xfe, 0xfe, 0xc1, 0xae, 0xa9, 0xac, 0xfe, 0x35, 0x2e,
-0xfe, 0xba, 0xc1, 0x5c, 0xfe, 0xb6, 0xaa, 0xb0, 0xe7, 0x35,
-0x2e, 0x39, 0xc1, 0xac, 0xb0, 0xfe, 0xfe, 0xbc, 0xa6, 0xac,
-0xc1, 0x42, 0x46, 0x54, 0xfe, 0xfe, 0xfe, 0xfe, 0xc9, 0xae,
-0xa9, 0xb0, 0x54, 0x35, 0x37, 0xfe, 0xd3, 0xd3, 0xb8, 0xae,
-0xab, 0xb6, 0xe7, 0xfe, 0xfe, 0x68, 0xfe, 0xfe, 0xfe, 0x4e,
-0xfe, 0xb0, 0xac, 0xb8, 0xfe, 0xfe, 0xc1, 0xb6, 0xc5, 0x46,
-0x3d, 0xe7, 0xb4, 0xa7, 0xab, 0xbc, 0x3f, 0x37, 0x54, 0xba,
-0xcd, 0x54, 0x42, 0xc5, 0xae, 0xac, 0xc9, 0x46, 0x3d, 0x54,
-0xba, 0xb0, 0xb0, 0xfe, 0x5c, 0xcd, 0xb0, 0xb0, 0xc9, 0x54,
-0x54, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xcd, 0xc1, 0xba, 0xc5,
-0xfe, 0x42, 0x46, 0xfe, 0xc5, 0xba, 0xb2, 0xa7, 0xa7, 0xb0,
-0xfe, 0x3d, 0x4a, 0x5c, 0xfe, 0xfe, 0xfe, 0xe7, 0xbc, 0xb0,
-0xae, 0xc5, 0x4e, 0x39, 0xfe, 0xc5, 0xbe, 0xfe, 0x54, 0xc9,
-0xa9, 0xa2, 0xa5, 0xbc, 0x3b, 0x2f, 0x35, 0xfe, 0xc9, 0xfe,
-0xfe, 0xc5, 0xa9, 0xa6, 0xb0, 0x54, 0x31, 0x31, 0x3f, 0xd3,
-0xbc, 0xc1, 0xcd, 0xb8, 0xae, 0xa8, 0xb4, 0xd3, 0x54, 0x4e,
-0x5c, 0x54, 0xfe, 0xdb, 0xba, 0xb4, 0xb4, 0xba, 0xcd, 0x5c,
-0x3d, 0x3f, 0x54, 0xfe, 0xcd, 0xaf, 0xa8, 0xac, 0xc5, 0xfe,
-0xfe, 0xe7, 0xdb, 0xfe, 0xfe, 0xfe, 0xe7, 0xb8, 0xaf, 0xb0,
-0xe7, 0x42, 0x4a, 0xcd, 0xbc, 0xdb, 0x46, 0x68, 0xcd, 0xb0,
-0xab, 0xbc, 0xfe, 0x3d, 0x46, 0xfe, 0xb8, 0xbc, 0xd3, 0xd3,
-0xb6, 0xb0, 0xb6, 0x5c, 0x3b, 0x35, 0x54, 0xdb, 0xba, 0xb4,
-0xc1, 0xc9, 0xc1, 0xba, 0xc9, 0x5c, 0x3d, 0x46, 0xfe, 0xcd,
-0xc5, 0xb8, 0xae, 0xaf, 0xb4, 0xd3, 0x54, 0x3d, 0x35, 0x46,
-0xfe, 0xdb, 0xbc, 0xb2, 0xa9, 0xab, 0xba, 0x3f, 0x31, 0x39,
-0xfe, 0xe7, 0xdb, 0xcd, 0xb8, 0xae, 0xab, 0xac, 0xe7, 0x3d,
-0x2d, 0x3f, 0xfe, 0xdb, 0xfe, 0xfe, 0xbc, 0xaa, 0xa8, 0xb0,
-0xfe, 0x31, 0x2d, 0x3d, 0xdb, 0xc5, 0xcd, 0xc9, 0xb4, 0xa8,
-0xad, 0xc5, 0x46, 0x39, 0x3f, 0x5c, 0xfe, 0xd3, 0xc5, 0xc1,
-0xb6, 0xb0, 0xbc, 0x68, 0x46, 0x4e, 0xe7, 0xfe, 0x5c, 0xfe,
-0xc1, 0xaf, 0xb0, 0xb8, 0xe7, 0x5c, 0x5c, 0xfe, 0xe7, 0xfe,
-0xfe, 0xe7, 0xb0, 0xab, 0xb2, 0x4a, 0x37, 0x3f, 0xcd, 0xbe,
-0xc1, 0xe7, 0xe7, 0xd3, 0xb6, 0xb4, 0xc9, 0x3b, 0x33, 0x4a,
-0xba, 0xb4, 0xc5, 0xfe, 0xc9, 0xb6, 0xb4, 0xcd, 0xfe, 0x3b,
-0x3b, 0xfe, 0xc1, 0xb6, 0xc5, 0xc5, 0xb8, 0xb0, 0xba, 0x4a,
-0x31, 0x35, 0x68, 0xcd, 0xc5, 0xba, 0xb4, 0xb0, 0xb0, 0xba,
-0x5c, 0x35, 0x2f, 0x4e, 0xd3, 0xc1, 0xdb, 0xd3, 0xb4, 0xa9,
-0xab, 0xcd, 0x3b, 0x2f, 0x35, 0xfe, 0xd3, 0xd3, 0xdb, 0xbc,
-0xad, 0xa4, 0xb0, 0xfe, 0x2d, 0x2f, 0x3f, 0xe7, 0xe7, 0xe7,
-0xcd, 0xb4, 0xaf, 0xad, 0xc5, 0x3d, 0x31, 0x3d, 0xe7, 0xd3,
-0xe7, 0xe7, 0xc1, 0xaf, 0xad, 0xb6, 0xfe, 0x4a, 0x42, 0x54,
-0xfe, 0x68, 0xfe, 0xd3, 0xb2, 0xae, 0xb4, 0xfe, 0x42, 0x4e,
-0xcd, 0xc5, 0xcd, 0xdb, 0xc9, 0xb4, 0xb0, 0xb6, 0xfe, 0x3b,
-0x42, 0xe7, 0xb0, 0xb8, 0xcd, 0xfe, 0xc9, 0xb6, 0xb8, 0xfe,
-0x42, 0x3d, 0xfe, 0xc1, 0xb0, 0xba, 0xd3, 0xfe, 0xc1, 0xb0,
-0xb6, 0xfe, 0x3b, 0x3f, 0xe7, 0xba, 0xb8, 0xbc, 0xc5, 0xc1,
-0xc1, 0xcd, 0xfe, 0x3b, 0x37, 0xfe, 0xc1, 0xb4, 0xb6, 0xb8,
-0xb6, 0xb8, 0xc5, 0x5c, 0x3f, 0x46, 0xfe, 0xcd, 0xc5, 0xcd,
-0xcd, 0xc1, 0xb2, 0xb2, 0xfe, 0x3f, 0x35, 0x54, 0xdb, 0xc1,
-0xcd, 0xcd, 0xbc, 0xaf, 0xac, 0xb6, 0x54, 0x35, 0x31, 0x68,
-0xba, 0xb8, 0xcd, 0xdb, 0xc9, 0xb2, 0xb4, 0xc9, 0x46, 0x39,
-0x42, 0xdb, 0xbc, 0xbc, 0xcd, 0xcd, 0xbe, 0xb2, 0xb8, 0xe7,
-0x54, 0x46, 0xfe, 0xfe, 0xdb, 0xc9, 0xc5, 0xbe, 0xbe, 0xc9,
-0xfe, 0x5c, 0x5c, 0xfe, 0xd3, 0xcd, 0xcd, 0xc5, 0xb6, 0xb2,
-0xc5, 0x68, 0x4e, 0xfe, 0xc5, 0xc1, 0xcd, 0x68, 0x5c, 0xe7,
-0xb8, 0xb6, 0xd3, 0x4a, 0x46, 0xfe, 0xbc, 0xb8, 0xc1, 0xe7,
-0xe7, 0xc1, 0xb4, 0xbe, 0xfe, 0x3f, 0x3f, 0xfe, 0xba, 0xb2,
-0xba, 0xe7, 0xfe, 0xcd, 0xcd, 0xfe, 0x4e, 0x46, 0xfe, 0xc5,
-0xb8, 0xb2, 0xba, 0xc1, 0xcd, 0xd3, 0xe7, 0xfe, 0x5c, 0x5c,
-0xfe, 0xe7, 0xc5, 0xbe, 0xb6, 0xba, 0xc5, 0xfe, 0x3f, 0x3f,
-0x54, 0xfe, 0xd3, 0xc1, 0xbc, 0xb6, 0xb0, 0xb0, 0xd3, 0x54,
-0x39, 0x46, 0xfe, 0xc1, 0xcd, 0xe7, 0xe7, 0xc5, 0xb8, 0xb4,
-0xd3, 0x54, 0x37, 0x42, 0xdb, 0xbe, 0xc1, 0xd3, 0xcd, 0xb8,
-0xb0, 0xb0, 0xcd, 0x4a, 0x3b, 0x42, 0xe7, 0xc5, 0xbe, 0xcd,
-0xe7, 0xd3, 0xc5, 0xcd, 0xfe, 0x54, 0x54, 0x68, 0xe7, 0xc5,
-0xc1, 0xc1, 0xcd, 0xcd, 0xc9, 0xc9, 0xcd, 0xe7, 0xfe, 0xfe,
-0xfe, 0xe7, 0xc5, 0xbe, 0xc1, 0xfe, 0x5c, 0x5c, 0xfe, 0xcd,
-0xcd, 0xcd, 0xdb, 0xd3, 0xc1, 0xbc, 0xbe, 0xfe, 0x4e, 0x54,
-0xcd, 0xb6, 0xb8, 0xd3, 0x5c, 0x5c, 0xfe, 0xc5, 0xc9, 0xfe,
-0x46, 0x4a, 0xe7, 0xb4, 0xb6, 0xc5, 0xfe, 0xe7, 0xcd, 0xc9,
-0xdb, 0xfe, 0x4e, 0x68, 0xd3, 0xb6, 0xb2, 0xbc, 0xfe, 0x68,
-0xfe, 0xfe, 0x68, 0x54, 0x68, 0xe7, 0xc5, 0xbc, 0xb8, 0xbe,
-0xcd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xd3, 0xd3, 0xcd,
-0xc1, 0xb8, 0xbc, 0xdb, 0x4e, 0x42, 0x4a, 0xfe, 0xc9, 0xc1,
-0xcd, 0xd3, 0xcd, 0xba, 0xb8, 0xcd, 0x46, 0x3b, 0xfe, 0xc9,
-0xba, 0xcd, 0xe7, 0xfe, 0xd3, 0xc1, 0xba, 0xdb, 0x54, 0x3d,
-0x68, 0xd3, 0xbc, 0xcd, 0xfe, 0xfe, 0xc5, 0xbe, 0xc1, 0xe7,
-0x54, 0x4a, 0xfe, 0xc9, 0xc1, 0xcd, 0xfe, 0xfe, 0xd3, 0xd3,
-0xd3, 0xfe, 0xe7, 0xe7, 0xe7, 0xdb, 0xd3, 0xe7, 0xe7, 0xe7,
-0xfe, 0xfe, 0xfe, 0xfe, 0xcd, 0xc9, 0xdb, 0xfe, 0xfe, 0xdb,
-0xbe, 0xc9, 0xfe, 0x5c, 0xfe, 0xc9, 0xbc, 0xbe, 0xdb, 0x68,
-0x5c, 0xdb, 0xc5, 0xd3, 0x54, 0x46, 0xfe, 0xbc, 0xb2, 0xb8,
-0xdb, 0x68, 0x68, 0xe7, 0xcd, 0xdb, 0x5c, 0x54, 0xfe, 0xc1,
-0xb8, 0xc1, 0xe7, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe,
-0xd3, 0xc5, 0xc1, 0xc5, 0xcd, 0xd3, 0xe7, 0xfe, 0x54, 0x4e,
-0xfe, 0xd3, 0xcd, 0xd3, 0xd3, 0xc5, 0xc1, 0xc1, 0xe7, 0x5c,
-0x4e, 0x5c, 0xd3, 0xc1, 0xcd, 0xfe, 0xfe, 0xcd, 0xba, 0xba,
-0xe7, 0x4a, 0x4a, 0x68, 0xcd, 0xc5, 0xcd, 0xfe, 0xfe, 0xcd,
-0xb8, 0xc1, 0xe7, 0x4e, 0x5c, 0xe7, 0xc1, 0xc9, 0xdb, 0xfe,
-0xe7, 0xc9, 0xc5, 0xd3, 0xfe, 0x68, 0xfe, 0xdb, 0xd3, 0xe7,
-0xfe, 0xfe, 0xcd, 0xc9, 0xcd, 0xd3, 0xd3, 0xd3, 0xcd, 0xe7,
-0xfe, 0xfe, 0xe7, 0xc5, 0xc5, 0xe7, 0x68, 0x68, 0xe7, 0xc1,
-0xc5, 0xfe, 0x5c, 0xfe, 0xd3, 0xc1, 0xd3, 0xfe, 0x68, 0xe7,
-0xc5, 0xb6, 0xc5, 0xe7, 0x68, 0xfe, 0xcd, 0xc5, 0xe7, 0xfe,
-0x54, 0xfe, 0xc9, 0xc5, 0xdb, 0xfe, 0xfe, 0xfe, 0xd3, 0xd3,
-0xfe, 0xfe, 0xfe, 0xcd, 0xc1, 0xc1, 0xc9, 0xd3, 0xd3, 0xe7,
-0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xd3, 0xdb, 0xe7, 0xe7, 0xd3,
-0xcd, 0xd3, 0xfe, 0xfe, 0xfe, 0xcd, 0xc5, 0xd3, 0xe7, 0xe7,
-0xc9, 0xbc, 0xbe, 0xe7, 0x68, 0x4a, 0xfe, 0xdb, 0xcd, 0xfe,
-0xfe, 0xfe, 0xcd, 0xc1, 0xc9, 0xfe, 0x54, 0x5c, 0xe7, 0xc9,
-0xc5, 0xe7, 0xfe, 0xfe, 0xcd, 0xc5, 0xc5, 0xe7, 0xfe, 0xfe,
-0xfe, 0xe7, 0xe7, 0xfe, 0xfe, 0xdb, 0xd3, 0xd3, 0xdb, 0xe7,
-0xfe, 0xfe, 0xe7, 0xe7, 0xdb, 0xd3, 0xc9, 0xd3, 0xe7, 0xfe,
-0xfe, 0xd3, 0xd3, 0xdb, 0xfe, 0xfe, 0xfe, 0xd3, 0xcd, 0xcd,
-0xfe, 0xfe, 0xe7, 0xc9, 0xc5, 0xd3, 0xfe, 0xfe, 0xfe, 0xcd,
-0xc9, 0xd3, 0xfe, 0xfe, 0xfe, 0xdb, 0xc9, 0xcd, 0xe7, 0xfe,
-0xe7, 0xcd, 0xcd, 0xe7, 0xfe, 0xfe, 0xe7, 0xd3, 0xc5, 0xcd,
-0xe7, 0xfe, 0xfe, 0xfe, 0xdb, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe,
-0xe7, 0xcd, 0xcd, 0xd3, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe,
-0xe7, 0xe7, 0xdb, 0xc9, 0xc1, 0xc5, 0xfe, 0x5c, 0x68, 0xfe,
-0xd3, 0xdb, 0xe7, 0xe7, 0xe7, 0xd3, 0xc5, 0xcd, 0xe7, 0x68,
-0xfe, 0xe7, 0xcd, 0xd3, 0xe7, 0xfe, 0xe7, 0xcd, 0xc1, 0xc1,
-0xdb, 0xfe, 0x54, 0xfe, 0xe7, 0xcd, 0xe7, 0xfe, 0xe7, 0xd3,
-0xcd, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xcd, 0xc5, 0xcd, 0xfe,
-0xfe, 0xe7, 0xcd, 0xd3, 0xdb, 0xe7, 0xfe, 0xfe, 0xfe, 0xe7,
-0xd3, 0xd3, 0xe7, 0xfe, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe,
-0xfe, 0xdb, 0xc5, 0xc1, 0xd3, 0xfe, 0xfe, 0xfe, 0xd3, 0xc9,
-0xcd, 0xe7, 0xfe, 0xfe, 0xd3, 0xcd, 0xdb, 0xfe, 0x5c, 0xfe,
-0xcd, 0xc9, 0xd3, 0xfe, 0xfe, 0xfe, 0xd3, 0xc9, 0xcd, 0xfe,
-0x68, 0xfe, 0xd3, 0xc1, 0xc1, 0xdb, 0xfe, 0xfe, 0xe7, 0xe7,
-0xfe, 0xfe, 0x68, 0xfe, 0xe7, 0xc5, 0xc9, 0xdb, 0xfe, 0xfe,
-0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xdb, 0xc5, 0xc5,
-0xd3, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xfe,
-0xc9, 0xc1, 0xc5, 0xfe, 0x54, 0x5c, 0xfe, 0xcd, 0xc5, 0xcd,
-0xfe, 0xfe, 0xdb, 0xc5, 0xc9, 0xfe, 0x5c, 0x68, 0xfe, 0xcd,
-0xcd, 0xfe, 0xfe, 0xfe, 0xe7, 0xc5, 0xc1, 0xd3, 0xfe, 0xfe,
-0xdb, 0xc9, 0xc5, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe,
-0xfe, 0xfe, 0xe7, 0xcd, 0xcd, 0xdb, 0xfe, 0xfe, 0xfe, 0xfe,
-0xe7, 0xd3, 0xcd, 0xd3, 0xfe, 0xfe, 0xdb, 0xcd, 0xd3, 0xe7,
-0xfe, 0xfe, 0xfe, 0xdb, 0xcd, 0xd3, 0xe7, 0xfe, 0xd3, 0xc5,
-0xc9, 0xfe, 0x5c, 0x54, 0xfe, 0xcd, 0xc1, 0xcd, 0xe7, 0xfe,
-0xfe, 0xd3, 0xcd, 0xfe, 0x54, 0x5c, 0xe7, 0xc1, 0xc1, 0xd3,
-0xfe, 0xfe, 0xe7, 0xd3, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xcd,
-0xc5, 0xcd, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xe7, 0xd3, 0xcd, 0xc9, 0xcd, 0xe7, 0xfe, 0xfe, 0xfe, 0xdb,
-0xc9, 0xcd, 0xe7, 0xfe, 0xe7, 0xc9, 0xc5, 0xdb, 0xfe, 0x5c,
-0xfe, 0xe7, 0xcd, 0xcd, 0xe7, 0xfe, 0xe7, 0xc5, 0xc1, 0xd3,
-0xfe, 0x5c, 0xfe, 0xcd, 0xc5, 0xcd, 0xe7, 0xfe, 0xfe, 0xe7,
-0xd3, 0xe7, 0xfe, 0xfe, 0xe7, 0xcd, 0xcd, 0xdb, 0xfe, 0xfe,
-0xfe, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xe7, 0xdb, 0xcd, 0xd3,
-0xd3, 0xdb, 0xfe, 0xfe, 0xfe, 0xfe, 0xdb, 0xd3, 0xdb, 0xe7,
-0xe7, 0xdb, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xe7, 0xc9, 0xc5,
-0xcd, 0xe7, 0xfe, 0xdb, 0xd3, 0xe7, 0xfe, 0x68, 0xfe, 0xe7,
-0xcd, 0xcd, 0xd3, 0xfe, 0xfe, 0xe7, 0xdb, 0xe7, 0xfe, 0x68,
-0xfe, 0xdb, 0xfe, 0x68, 0xbe, 0xb2, 0xae, 0xab, 0xb2, 0xfe,
-0x2f, 0x31, 0xdb, 0xac, 0xad, 0xaf, 0xab, 0xab, 0xb4, 0x68,
-0x37, 0x39, 0x3f, 0xe7, 0xb4, 0xa8, 0xaa, 0xb0, 0xbc, 0xbc,
-0xc5, 0x3f, 0x31, 0x3d, 0xfe, 0xc1, 0xb8, 0xb0, 0xa5, 0xa2,
-0xa8, 0xaf, 0xdb, 0x3b, 0x28, 0x2a, 0x3d, 0xbc, 0xb0, 0xaa,
-0xa2, 0x9f, 0xab, 0xfe, 0x29, 0x24, 0x29, 0x4a, 0xb4, 0xaa,
-0xa8, 0xa9, 0xa8, 0xa5, 0xac, 0xdb, 0x2c, 0x27, 0x35, 0x4a,
-0xfe, 0xcd, 0xb2, 0xa2, 0x9f, 0x9f, 0xae, 0x4e, 0x2c, 0x22,
-0x33, 0xfe, 0xba, 0xb0, 0xa6, 0x9f, 0xa3, 0xbc, 0x5c, 0x33,
-0x31, 0x39, 0x5c, 0xcd, 0xc1, 0xb4, 0xad, 0xaa, 0xad, 0xcd,
-0x54, 0x46, 0xfe, 0xe7, 0xfe, 0x54, 0xc5, 0xaf, 0xa6, 0xa9,
-0xb0, 0xfe, 0x3d, 0x39, 0x5c, 0xdb, 0xdb, 0xfe, 0xba, 0xac,
-0xa8, 0xc5, 0x39, 0x33, 0x54, 0xb8, 0xae, 0xad, 0xb8, 0xc1,
-0xcd, 0xbe, 0xbc, 0xfe, 0x39, 0x37, 0xfe, 0xb4, 0xba, 0xcd,
-0xdb, 0xb8, 0xb0, 0xb6, 0xfe, 0x4e, 0x39, 0x3d, 0xfe, 0xb0,
-0xaa, 0xa9, 0xa9, 0xaa, 0xb0, 0x5c, 0x29, 0x28, 0x31, 0xfe,
-0xba, 0xb4, 0xae, 0xab, 0xab, 0xb2, 0xfe, 0x3b, 0x2a, 0x2f,
-0x54, 0xb4, 0xb4, 0xba, 0xb2, 0xa3, 0x9f, 0xa8, 0xfe, 0x2c,
-0x27, 0x2a, 0x46, 0xfe, 0xc1, 0xbc, 0xb0, 0xa2, 0xa2, 0xb0,
-0x33, 0x22, 0x2b, 0x46, 0xc1, 0xb4, 0xb0, 0xab, 0xa8, 0xa8,
-0xb0, 0xdb, 0x42, 0x2c, 0x33, 0x4a, 0xfe, 0x5c, 0xdb, 0xb4,
-0xa8, 0xad, 0xba, 0xfe, 0x46, 0x39, 0x39, 0x4a, 0xfe, 0xbc,
-0xab, 0xa5, 0xa5, 0xb8, 0x68, 0x37, 0x4a, 0xe7, 0xfe, 0x4a,
-0x5c, 0xd3, 0xb0, 0xb2, 0xc1, 0x5c, 0x42, 0x46, 0xd3, 0xb4,
-0xbe, 0x54, 0x54, 0xb6, 0xab, 0xae, 0xe7, 0x46, 0x4a, 0xfe,
-0xcd, 0xc9, 0xd3, 0xe7, 0xe7, 0xbe, 0xb4, 0xc5, 0x37, 0x2c,
-0x37, 0xc1, 0xb0, 0xb2, 0xb4, 0xb2, 0xb6, 0xdb, 0x54, 0x4a,
-0x46, 0x42, 0x68, 0xba, 0xb2, 0xba, 0xc5, 0xb6, 0xb6, 0xcd,
-0x33, 0x2f, 0x39, 0x68, 0xfe, 0xe7, 0xba, 0xac, 0xa7, 0xa7,
-0xb2, 0xe7, 0x2d, 0x25, 0x2f, 0xd3, 0xbe, 0xd3, 0xc5, 0xac,
-0xa6, 0xac, 0xfe, 0x33, 0x2c, 0x2d, 0x54, 0xc1, 0xb4, 0xcd,
-0xcd, 0xaf, 0xa4, 0xa8, 0xe7, 0x31, 0x31, 0x39, 0x46, 0xfe,
-0xdb, 0xbc, 0xb6, 0xaf, 0xa9, 0xb2, 0xfe, 0x2f, 0xfe, 0xba,
-0xad, 0xba, 0x4e, 0x3d, 0x42, 0xfe, 0xc9, 0xc1, 0xe7, 0xfe,
-0xc1, 0xa9, 0xa6, 0xcd, 0x2a, 0x25, 0x3b, 0xbc, 0xb0, 0xb6,
-0xb8, 0xb4, 0xb4, 0xb8, 0xc1, 0xfe, 0x3d, 0x3d, 0xfe, 0xba,
-0xd3, 0x54, 0x54, 0xbe, 0xb0, 0xb4, 0xe7, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xc5, 0xb6, 0xa9, 0xaa, 0xba, 0x3d, 0x39,
-0xfe, 0xc1, 0xfe, 0x4a, 0x54, 0xbe, 0xb2, 0xb8, 0xfe, 0x3d,
-0x3b, 0xfe, 0xb0, 0xab, 0xc5, 0x39, 0x3b, 0xbc, 0xa7, 0xab,
-0xd3, 0x68, 0xfe, 0xd3, 0xcd, 0xfe, 0x54, 0x3d, 0xfe, 0xbc,
-0xac, 0xc9, 0x3f, 0x2e, 0xfe, 0xba, 0xb0, 0xba, 0xba, 0xb6,
-0xba, 0xd3, 0xfe, 0xfe, 0x5c, 0x54, 0xc9, 0xb4, 0xbe, 0x54,
-0x68, 0xcd, 0xb6, 0xfe, 0x46, 0x54, 0xcd, 0xc5, 0xdb, 0xfe,
-0xe7, 0xc1, 0xae, 0xa8, 0xac, 0xfe, 0x2e, 0x2e, 0xfe, 0xb6,
-0xc1, 0x5c, 0xe7, 0xb6, 0xaa, 0xb0, 0x54, 0x35, 0x2e, 0x4a,
-0xc1, 0xac, 0xbc, 0xfe, 0xfe, 0xaf, 0xa6, 0xac, 0xfe, 0x42,
-0x46, 0x5c, 0xfe, 0xfe, 0xfe, 0xe7, 0xc9, 0xae, 0xa9, 0xb0,
-0x54, 0x31, 0x37, 0xfe, 0xd3, 0xd3, 0xb8, 0xac, 0xab, 0xb6,
-0xe7, 0xfe, 0xfe, 0x68, 0xfe, 0xfe, 0xfe, 0x54, 0xfe, 0xb0,
-0xae, 0xb8, 0xfe, 0xe7, 0xc1, 0xb6, 0xe7, 0x46, 0x3d, 0xe7,
-0xae, 0xa7, 0xab, 0xdb, 0x3f, 0x37, 0xfe, 0xba, 0xcd, 0x3f,
-0x42, 0xc5, 0xab, 0xac, 0xc9, 0x46, 0x3d, 0x54, 0xba, 0xad,
-0xb0, 0xfe, 0x68, 0xcd, 0xb0, 0xb0, 0xc9, 0x54, 0x54, 0xfe,
-0xfe, 0xfe, 0xfe, 0xe7, 0xcd, 0xbe, 0xba, 0xc5, 0x68, 0x42,
-0x46, 0xe7, 0xc5, 0xba, 0xaf, 0xa7, 0xa7, 0xbc, 0xfe, 0x3d,
-0x4a, 0x68, 0xfe, 0xfe, 0xfe, 0xe7, 0xbc, 0xaf, 0xae, 0xc5,
-0x3d, 0x39, 0xfe, 0xbc, 0xbe, 0xfe, 0x68, 0xc9, 0xa9, 0xa2,
-0xaa, 0xbc, 0x3b, 0x2d, 0x35, 0xfe, 0xcd, 0xfe, 0xfe, 0xb4,
-0xa9, 0xa6, 0xbc, 0x54, 0x31, 0x31, 0x54, 0xd3, 0xbc, 0xc5,
-0xcd, 0xb8, 0xab, 0xa8, 0xb4, 0xfe, 0x54, 0x4e, 0x68, 0x54,
-0xfe, 0xc9, 0xba, 0xb4, 0xb4, 0xba, 0xcd, 0x5c, 0x3b, 0x3f,
-0x54, 0xfe, 0xcd, 0xaf, 0xa8, 0xac, 0xc5, 0x68, 0xfe, 0xe7,
-0xdb, 0xfe, 0xfe, 0xfe, 0xcd, 0xb8, 0xaf, 0xb6, 0xe7, 0x42,
-0x5c, 0xcd, 0xbc, 0xfe, 0x46, 0x68, 0xba, 0xb0, 0xab, 0xbc,
-0x54, 0x3d, 0x46, 0xc9, 0xb8, 0xbc, 0xdb, 0xd3, 0xb6, 0xb0,
-0xb6, 0x5c, 0x37, 0x35, 0x54, 0xc9, 0xba, 0xb4, 0xc1, 0xc9,
-0xc1, 0xba, 0xe7, 0x5c, 0x3d, 0x54, 0xfe, 0xcd, 0xc5, 0xb8,
-0xae, 0xaf, 0xb4, 0xd3, 0x54, 0x3b, 0x35, 0x46, 0xfe, 0xdb,
-0xbc, 0xaf, 0xa9, 0xab, 0xd3, 0x3f, 0x31, 0x3f, 0xfe, 0xe7,
-0xdb, 0xcd, 0xb8, 0xae, 0xaa, 0xac, 0xe7, 0x33, 0x2d, 0x3f,
-0xd3, 0xdb, 0xfe, 0xfe, 0xbc, 0xaa, 0xa9, 0xb0, 0xfe, 0x31,
-0x2f, 0x3d, 0xdb, 0xc5, 0xcd, 0xc9, 0xae, 0xa8, 0xad, 0xfe,
-0x46, 0x39, 0x46, 0x5c, 0xfe, 0xcd, 0xc5, 0xc1, 0xb6, 0xb0,
-0xbc, 0x68, 0x42, 0x4e, 0xe7, 0xfe, 0x5c, 0xfe, 0xb6, 0xaf,
-0xb0, 0xc5, 0xe7, 0x5c, 0x5c, 0xfe, 0xe7, 0xfe, 0x68, 0xe7,
-0xb0, 0xac, 0xb2, 0x4a, 0x35, 0x3f, 0xcd, 0xbc, 0xc1, 0xe7,
-0xe7, 0xd3, 0xb6, 0xb4, 0xfe, 0x3b, 0x33, 0xfe, 0xba, 0xb4,
-0xd3, 0xfe, 0xc9, 0xb4, 0xb4, 0xcd, 0x4a, 0x3b, 0x3b, 0xfe,
-0xb8, 0xb6, 0xc5, 0xc5, 0xb8, 0xb0, 0xcd, 0x4a, 0x31, 0x3b,
-0x68, 0xcd, 0xc1, 0xba, 0xb4, 0xb0, 0xb0, 0xba, 0x5c, 0x2f,
-0x2f, 0x4e, 0xc9, 0xc1, 0xdb, 0xc9, 0xb4 };
diff --git a/trunk/apps/leave.h b/trunk/apps/leave.h
deleted file mode 100644
index 238976f20..000000000
--- a/trunk/apps/leave.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * U-law 8-bit audio data
- *
- * Source: leave.raw
- *
- * Copyright (C) 1999, Mark Spencer and Linux Support Services
- *
- * Distributed under the terms of the GNU General Public License
- *
- */
-
-static unsigned char leave[] = {
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xc1, 0x3d,
-0x42, 0x46, 0x3f, 0x3f, 0x46, 0x3f, 0x4e, 0xba, 0xbe, 0xbe,
-0xbc, 0xba, 0xbe, 0xc5, 0xb6, 0x2e, 0x2c, 0x33, 0x2f, 0x2e,
-0x2f, 0x33, 0x2b, 0x54, 0xac, 0xb0, 0xb0, 0xad, 0xaf, 0xb0,
-0xae, 0xcd, 0x3b, 0x2f, 0x31, 0x2e, 0x2f, 0x31, 0x2e, 0x46,
-0xc5, 0xaf, 0xb0, 0xaf, 0xae, 0xaf, 0xaf, 0xb0, 0xfe, 0x2d,
-0x31, 0x31, 0x2e, 0x31, 0x2f, 0x31, 0xfe, 0xae, 0xaf, 0xaf,
-0xae, 0xb0, 0xae, 0xaf, 0xfe, 0xdb, 0x2e, 0x2e, 0x31, 0x31,
-0x2d, 0x2e, 0xdb, 0x68, 0xaf, 0xad, 0xb0, 0xb0, 0xae, 0xaf,
-0x5c, 0xe7, 0x39, 0x2d, 0x31, 0x31, 0x31, 0x2d, 0xfe, 0xfe,
-0x68, 0xad, 0xaf, 0xb0, 0xaf, 0xac, 0xbc, 0xfe, 0xd3, 0x2f,
-0x2e, 0x33, 0x31, 0x2d, 0x4e, 0xdb, 0xfe, 0xfe, 0xac, 0xaf,
-0xb0, 0xac, 0xb6, 0x68, 0xe7, 0xdb, 0x2e, 0x2f, 0x35, 0x2f,
-0x31, 0xe7, 0xe7, 0x68, 0xad, 0xac, 0xb0, 0xae, 0xac, 0xfe,
-0xfe, 0xdb, 0xfe, 0x2d, 0x33, 0x31, 0x2e, 0xfe, 0xfe, 0xfe,
-0xfe, 0xbc, 0xaf, 0xb0, 0xad, 0xfe, 0xfe, 0xfe, 0xe7, 0x5c,
-0x2e, 0x33, 0x2e, 0x35, 0xe7, 0xfe, 0xfe, 0xfe, 0xad, 0xb0,
-0xaf, 0xc1, 0xfe, 0xe7, 0xfe, 0xe7, 0x3d, 0x31, 0x2f, 0x37,
-0xe7, 0xfe, 0xfe, 0xe7, 0xfe, 0xaf, 0xad, 0xbe, 0xfe, 0xdb,
-0xfe, 0xfe, 0xdb, 0x35, 0x2d, 0x39, 0xdb, 0xfe, 0xfe, 0xdb,
-0xfe, 0xfe, 0xad, 0xaf, 0xfe, 0xfe, 0xe7, 0x68, 0xfe, 0xd3,
-0x2e, 0x2c, 0xdb, 0xdb, 0x2c, 0x35, 0xd3, 0x68, 0xaf, 0xad,
-0xb0, 0xb0, 0xad, 0xba, 0x68, 0xe7, 0xe7, 0x2e, 0x2f, 0x33,
-0x31, 0x2d, 0xdb, 0xd3, 0x5c, 0xae, 0xaa, 0xe7, 0x68, 0xaa,
-0xe7, 0xfe, 0xdb, 0xe7, 0xfe, 0xe7, 0xd3, 0x2d, 0xfe, 0xdb,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xc5, 0xfe, 0xe7, 0xe7,
-0xfe, 0xfe, 0xe7, 0xe7, 0x3b, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xc5, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xfe, 0x3b,
-0xdb, 0xfe, 0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xb0, 0xfe, 0xfe,
-0xe7, 0xfe, 0xfe, 0xfe, 0xdb, 0x2e, 0x5c, 0xdb, 0xfe, 0xfe,
-0xe7, 0xe7, 0x68, 0xb0, 0xbe, 0x68, 0xe7, 0xe7, 0xfe, 0xfe,
-0xdb, 0x39, 0x2f, 0xdb, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xbe,
-0xaf, 0xe7, 0x68, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0x33, 0x33,
-0xdb, 0xfe, 0xfe, 0xdb, 0xe7, 0xfe, 0xb0, 0xb0, 0xfe, 0xfe,
-0xe7, 0xfe, 0xfe, 0xfe, 0x35, 0x33, 0xe7, 0xe7, 0xfe, 0xe7,
-0xe7, 0xfe, 0xb0, 0xb2, 0xb0, 0xfe, 0xfe, 0xe7, 0xfe, 0xe7,
-0x46, 0x35, 0x35, 0x3f, 0xe7, 0xfe, 0xe7, 0xfe, 0xb2, 0xb0,
-0xb2, 0xb0, 0xfe, 0xfe, 0xfe, 0xfe, 0x42, 0x35, 0x37, 0x33,
-0xe7, 0xfe, 0xfe, 0xfe, 0xb8, 0xb0, 0xb6, 0xb0, 0xba, 0xfe,
-0xfe, 0xe7, 0xe7, 0x33, 0x39, 0x39, 0x33, 0xe7, 0xdb, 0xfe,
-0xe7, 0xb0, 0xb4, 0xb6, 0xb0, 0xcd, 0xfe, 0xe7, 0xe7, 0x33,
-0x39, 0x3b, 0x33, 0x46, 0xd3, 0xfe, 0xfe, 0xb0, 0xb2, 0xb6,
-0xb4, 0xb0, 0xfe, 0xfe, 0xdb, 0x35, 0x37, 0x39, 0x39, 0x35,
-0x37, 0xdb, 0x68, 0xcd, 0xb2, 0xb6, 0xb6, 0xb4, 0xb4, 0x68,
-0xe7, 0x42, 0x37, 0x3b, 0x3b, 0x39, 0x37, 0xdb, 0xfe, 0xcd,
-0xb2, 0xb6, 0xb6, 0xb6, 0xb2, 0xb4, 0xfe, 0x54, 0x37, 0x3b,
-0x39, 0x3b, 0x3b, 0x39, 0xe7, 0xfe, 0xb6, 0xb6, 0xb6, 0xb4,
-0xb6, 0xb6, 0xbc, 0xfe, 0x3f, 0x3b, 0x3b, 0x39, 0x3b, 0x3b,
-0x39, 0xe7, 0xb6, 0xb8, 0xb8, 0xb6, 0xb8, 0xb8, 0xb4, 0xfe,
-0x3b, 0x3d, 0x3d, 0x3b, 0x39, 0x3d, 0x3b, 0x39, 0xbe, 0xb8,
-0xba, 0xb8, 0xb6, 0xb8, 0xba, 0xb4, 0xfe, 0x39, 0x3f, 0x3d,
-0x3b, 0x3d, 0x3f, 0x39, 0xdb, 0xb4, 0xba, 0xb8, 0xb6, 0xb8,
-0xbc, 0xb4, 0xba, 0x39, 0x42, 0x3f, 0x3d, 0x3d, 0x3f, 0x3f,
-0x3b, 0xb8, 0xb6, 0xbc, 0xb8, 0xb8, 0xba, 0xbc, 0xb8, 0xe7,
-0x3d, 0x42, 0x3f, 0x3d, 0x3f, 0x42, 0x3d, 0xfe, 0xb8, 0xbc,
-0xbc, 0xba, 0xba, 0xbc, 0xba, 0xe7, 0x3d, 0x3f, 0x42, 0x3f,
-0x3f, 0x42, 0x42, 0xfe, 0xfe, 0xbc, 0xbc, 0xbe, 0xbc, 0xbe,
-0xbc, 0xc5, 0xe7, 0x68, 0x42, 0x46, 0x42, 0x46, 0x42, 0x46,
-0xfe, 0xfe, 0xbc, 0xbe, 0xbe, 0xbe, 0xbc, 0xc5, 0xfe, 0xdb,
-0x46, 0x46, 0x4a, 0x4a, 0x46, 0x46, 0xe7, 0xfe, 0xd3, 0xbe,
-0xc9, 0xc9, 0xc5, 0xc5, 0xe7, 0xdb, 0xd3, 0x4a, 0x4e, 0x54,
-0x4e, 0x4e, 0xfe, 0x5c, 0x54, 0xd3, 0xcd, 0xd3, 0xd3, 0xcd,
-0xd3, 0xd3, 0xcd, 0xfe, 0x5c, 0x68, 0x5c, 0x5c, 0x5c, 0x68,
-0x5c, 0x5c, 0xcd, 0xcd, 0xd3, 0xcd, 0xdb, 0xe7, 0xe7, 0xdb,
-0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7,
-0xfe, 0x5c, 0x5c, 0xfe, 0xfe, 0xfe, 0xfe, 0x46, 0x35, 0x35,
-0x37, 0x39, 0x3b, 0x39, 0x35, 0x33, 0x35, 0x5c, 0xd3, 0xcd,
-0xdb, 0xfe, 0xfe, 0xd3, 0xb0, 0xb0, 0xb0, 0xb4, 0xb4, 0xb6,
-0xb2, 0xb0, 0xb0, 0xb6, 0xcd, 0x5c, 0x68, 0xfe, 0xfe, 0xfe,
-0x3b, 0x33, 0x35, 0x37, 0x39, 0x3b, 0x39, 0x37, 0x35, 0x35,
-0x3f, 0xdb, 0xcd, 0xcd, 0xdb, 0xfe, 0xe7, 0xc5, 0xb0, 0xb0,
-0xb2, 0xb6, 0xb6, 0xb6, 0xb2, 0xb0, 0xb0, 0xcd, 0x5c, 0x5c,
-0xfe, 0xe7, 0xe7, 0xfe, 0x35, 0x35, 0x35, 0x39, 0x3b, 0x3b,
-0x39, 0x35, 0x33, 0x39, 0xdb, 0xcd, 0xd3, 0xe7, 0xfe, 0xfe,
-0xba, 0xb0, 0xb0, 0xb2, 0xb4, 0xb6, 0xb6, 0xb4, 0xb2, 0xb0,
-0xb4, 0xc9, 0x5c, 0x68, 0xfe, 0xfe, 0x5c, 0x3b, 0x35, 0x37,
-0x39, 0x3b, 0x3b, 0x3b, 0x39, 0x37, 0x37, 0x3d, 0xe7, 0xcd,
-0xdb, 0xfe, 0xe7, 0xbe, 0xb2, 0xb2, 0xb4, 0xb4, 0xb6, 0xb6,
-0xb6, 0xb4, 0xb0, 0xb0, 0xc5, 0x5c, 0x5c, 0xfe, 0xe7, 0xe7,
-0x4e, 0x35, 0x35, 0x37, 0x3b, 0x3b, 0x3b, 0x39, 0x37, 0x37,
-0x3b, 0xe7, 0xc9, 0xcd, 0xe7, 0xfe, 0xd3, 0xb4, 0xb2, 0xb2,
-0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb4, 0xb2, 0xb4, 0xc1, 0x68,
-0x68, 0xfe, 0xfe, 0x42, 0x39, 0x37, 0x39, 0x3b, 0x3b, 0x3b,
-0x3b, 0x3b, 0x39, 0x37, 0x3b, 0xfe, 0xd3, 0xdb, 0xfe, 0xcd,
-0xb4, 0xb2, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb4, 0xb2,
-0xb2, 0xc1, 0x5c, 0x5c, 0xfe, 0xfe, 0xfe, 0x3d, 0x37, 0x37,
-0x39, 0x3b, 0x3b, 0x3b, 0x3b, 0x37, 0x37, 0x39, 0xfe, 0xcd,
-0xd3, 0xfe, 0xfe, 0xc1, 0xb2, 0xb2, 0xb4, 0xb6, 0xb6, 0xb6,
-0xb6, 0xb6, 0xb6, 0xb4, 0xb4, 0xbc, 0x68, 0xfe, 0xfe, 0xfe,
-0x3b, 0x39, 0x39, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x39,
-0x39, 0x3b, 0xfe, 0xdb, 0xe7, 0xfe, 0xbc, 0xb6, 0xb6, 0xb6,
-0xb8, 0xb6, 0xb6, 0xb6, 0xb8, 0xb6, 0xb4, 0xb4, 0xbc, 0xfe,
-0x68, 0xfe, 0xe7, 0x5c, 0x3b, 0x39, 0x39, 0x3b, 0x3b, 0x3b,
-0x3d, 0x3d, 0x3b, 0x39, 0x3b, 0x68, 0xdb, 0xdb, 0xfe, 0xe7,
-0xb8, 0xb6, 0xb6, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb6,
-0xb4, 0xb6, 0xdb, 0x68, 0xfe, 0xfe, 0x46, 0x3b, 0x3b, 0x3b,
-0x3d, 0x3d, 0x3b, 0x3d, 0x3d, 0x3d, 0x3d, 0x3b, 0x3b, 0x5c,
-0xdb, 0xdb, 0xc9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
-0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xbc, 0xcd, 0xfe, 0xfe, 0x3d,
-0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3b, 0x3d, 0x3d, 0x3d, 0x3d,
-0x3b, 0x3d, 0x46, 0xfe, 0xe7, 0xe7, 0xc5, 0xb8, 0xb8, 0xb8,
-0xba, 0xba, 0xb8, 0xb8, 0xba, 0xba, 0xb8, 0xb8, 0xb8, 0xcd,
-0xfe, 0xfe, 0x68, 0x3f, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
-0x3d, 0x3d, 0x3f, 0x3f, 0x3d, 0x3b, 0x4a, 0xfe, 0xdb, 0xbc,
-0xb8, 0xba, 0xba, 0xba, 0xba, 0xb8, 0xb8, 0xb8, 0xba, 0xba,
-0xba, 0xba, 0xba, 0xc5, 0xfe, 0x54, 0x3f, 0x3f, 0x3f, 0x3f,
-0x3f, 0x3f, 0x3d, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x42,
-0xfe, 0xe7, 0xdb, 0xbc, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
-0xba, 0xba, 0xbc, 0xba, 0xba, 0xba, 0xc5, 0xfe, 0xfe, 0x4e,
-0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x42,
-0x42, 0x42, 0x3f, 0x46, 0xfe, 0xcd, 0xb8, 0xba, 0xbc, 0xbc,
-0xbc, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xba, 0xb8,
-0xbe, 0xfe, 0x42, 0x3d, 0x3f, 0x42, 0x42, 0x42, 0x3f, 0x3f,
-0x3f, 0x42, 0x42, 0x42, 0x42, 0x3f, 0x3f, 0x68, 0xdb, 0xc5,
-0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xba, 0xba, 0xba, 0xbc, 0xbc,
-0xbc, 0xbc, 0xba, 0xc1, 0xfe, 0xfe, 0x3f, 0x42, 0x46, 0x46,
-0x46, 0x42, 0x42, 0x42, 0x42, 0x42, 0x46, 0x46, 0x42, 0x3f,
-0x42, 0x68, 0xbe, 0xba, 0xbc, 0xbe, 0xbe, 0xbe, 0xbc, 0xbc,
-0xbc, 0xbc, 0xbe, 0xc1, 0xbe, 0xbc, 0xba, 0xbe, 0x68, 0x3f,
-0x42, 0x46, 0x4a, 0x4a, 0x46, 0x42, 0x42, 0x42, 0x46, 0x46,
-0x46, 0x46, 0x42, 0x42, 0x68, 0xd3, 0xbc, 0xbc, 0xbe, 0xc1,
-0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xc1, 0xc1, 0xbe, 0xbe,
-0xc1, 0xfe, 0x4e, 0x42, 0x46, 0x4a, 0x4a, 0x4a, 0x46, 0x46,
-0x46, 0x46, 0x4a, 0x4a, 0x4a, 0x46, 0x46, 0x68, 0xdb, 0xbe,
-0xbe, 0xc1, 0xc5, 0xc1, 0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xc1,
-0xc5, 0xc5, 0xbe, 0xbc, 0xc1, 0x4e, 0x46, 0x46, 0x4a, 0x4e,
-0x4e, 0x4a, 0x46, 0x46, 0x46, 0x4a, 0x4a, 0x4e, 0x4a, 0x46,
-0x46, 0xfe, 0xbe, 0xbe, 0xc1, 0xc9, 0xc5, 0xc5, 0xc1, 0xc1,
-0xc1, 0xc1, 0xc5, 0xc5, 0xc5, 0xc5, 0xbe, 0xc1, 0xfe, 0x4a,
-0x4a, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4a, 0x4a, 0x4a, 0x4e,
-0x54, 0x4e, 0x4a, 0x4a, 0x4e, 0xcd, 0xc1, 0xc5, 0xc5, 0xc9,
-0xc5, 0xc5, 0xc5, 0xc5, 0xc9, 0xcd, 0xcd, 0xcd, 0xcd, 0xc9,
-0xc9, 0xd3, 0x68, 0x54, 0x5c, 0x68, 0x68, 0x68, 0x5c, 0x5c,
-0x5c, 0x5c, 0x5c, 0x68, 0x68, 0x5c, 0x54, 0x5c, 0xdb, 0xcd,
-0xcd, 0xdb, 0xdb, 0xdb, 0xdb, 0xd3, 0xd3, 0xe7, 0xe7, 0xe7,
-0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
-0xfe, 0xfe, 0xfe };
diff --git a/trunk/apps/rpt_flow.pdf b/trunk/apps/rpt_flow.pdf
deleted file mode 100644
index 2085af0f2..000000000
--- a/trunk/apps/rpt_flow.pdf
+++ /dev/null
Binary files differ