mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-12 15:45:18 +00:00
Add fixes and cleanup to app_alarmreceiver.
This work comes courtesy of Pedro Kiefer (License #6407) The work was posted to review board by Kaloyan Kovachev (License #5506) (closes issue ASTERISK-16668) Reported by Grant Crawshay (closes issue ASTERISK-16694) Reported by Fred van Lieshout (closes issue ASTERISK-18417) Reported by Kostas Liakakis (closes issue ASTERISK-19435) Reported by Deon George (closes issue ASTERISK-20157) Reported by Pedro Kiefer (closes issue ASTERISK-20158) Reported by Pedro Kiefer (closes issue ASTERISK-20224) Reported by Pedro Kiefer Review: https://reviewboard.asterisk.org/r/2075 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@372310 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
|
*
|
||||||
* \brief Central Station Alarm receiver for Ademco Contact ID
|
* \brief Central Station Alarm receiver for Ademco Contact ID
|
||||||
* \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
|
* \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
|
||||||
*
|
*
|
||||||
@@ -47,7 +48,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
#include "asterisk/pbx.h"
|
#include "asterisk/pbx.h"
|
||||||
#include "asterisk/module.h"
|
#include "asterisk/module.h"
|
||||||
#include "asterisk/translate.h"
|
#include "asterisk/translate.h"
|
||||||
#include "asterisk/ulaw.h"
|
|
||||||
#include "asterisk/app.h"
|
#include "asterisk/app.h"
|
||||||
#include "asterisk/dsp.h"
|
#include "asterisk/dsp.h"
|
||||||
#include "asterisk/config.h"
|
#include "asterisk/config.h"
|
||||||
@@ -55,9 +55,20 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
#include "asterisk/callerid.h"
|
#include "asterisk/callerid.h"
|
||||||
#include "asterisk/astdb.h"
|
#include "asterisk/astdb.h"
|
||||||
#include "asterisk/utils.h"
|
#include "asterisk/utils.h"
|
||||||
|
#include "asterisk/indications.h"
|
||||||
|
|
||||||
#define ALMRCV_CONFIG "alarmreceiver.conf"
|
#define ALMRCV_CONFIG "alarmreceiver.conf"
|
||||||
#define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
|
#define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
|
||||||
|
#define ADEMCO_MSG_TYPE_1 "18"
|
||||||
|
#define ADEMCO_MSG_TYPE_2 "98"
|
||||||
|
#define ADEMCO_AUDIO_CALL_NEXT "606"
|
||||||
|
|
||||||
|
struct {
|
||||||
|
char digit;
|
||||||
|
char weight;
|
||||||
|
} digits_mapping[] = { {'0', 10}, {'1', 1} , {'2', 2}, {'3', 3}, {'4', 4}, {'5', 5},
|
||||||
|
{'6', 6}, {'7', 7}, {'8', 8}, {'9', 9}, {'*', 11}, {'#', 12},
|
||||||
|
{'A', 13}, {'B', 14}, {'C', 15} };
|
||||||
|
|
||||||
struct event_node{
|
struct event_node{
|
||||||
char data[17];
|
char data[17];
|
||||||
@@ -102,152 +113,78 @@ static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
|
|||||||
/* Misc variables */
|
/* Misc variables */
|
||||||
static char event_file[14] = "/event-XXXXXX";
|
static char event_file[14] = "/event-XXXXXX";
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
* Attempt to access a database variable and increment it,
|
* \brief 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
|
* \note Only if the user defined db-family in alarmreceiver.conf
|
||||||
* 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.
|
* 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
|
||||||
static void database_increment( char *key )
|
* family, then create it and set its value to 1.
|
||||||
|
*
|
||||||
|
* \param key A database key to increment
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void database_increment(char *key)
|
||||||
{
|
{
|
||||||
int res = 0;
|
|
||||||
unsigned v;
|
unsigned v;
|
||||||
char value[16];
|
char value[16];
|
||||||
|
|
||||||
|
if (ast_strlen_zero(db_family)) {
|
||||||
if (ast_strlen_zero(db_family))
|
return; /* If not defined, don't do anything */
|
||||||
return; /* If not defined, don't do anything */
|
}
|
||||||
|
|
||||||
res = ast_db_get(db_family, key, value, sizeof(value) - 1);
|
if (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);
|
ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
|
||||||
/* Guess we have to create it */
|
/* Guess we have to create it */
|
||||||
res = ast_db_put(db_family, key, "1");
|
ast_db_put(db_family, key, "1");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sscanf(value, "%30u", &v);
|
sscanf(value, "%30u", &v);
|
||||||
v++;
|
v++;
|
||||||
|
|
||||||
ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v);
|
ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v);
|
||||||
|
|
||||||
snprintf(value, sizeof(value), "%u", v);
|
snprintf(value, sizeof(value), "%u", v);
|
||||||
|
|
||||||
res = ast_db_put(db_family, key, value);
|
if (ast_db_put(db_family, key, value)) {
|
||||||
|
|
||||||
if (res)
|
|
||||||
ast_verb(4, "AlarmReceiver: database_increment write error\n");
|
ast_verb(4, "AlarmReceiver: database_increment write error\n");
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
/*
|
* \brief Receive a fixed length DTMF string.
|
||||||
* Build a MuLaw data block for a single frequency tone
|
*
|
||||||
*/
|
* \note Doesn't give preferential treatment to any digit,
|
||||||
static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x)
|
* \note allow different timeout values for the first and all subsequent digits
|
||||||
{
|
*
|
||||||
int i;
|
* \param chan Asterisk Channel
|
||||||
float val;
|
* \param digit_string Digits String
|
||||||
|
* \param length Length of the message we expect
|
||||||
for (i = 0; i < len; i++) {
|
* \param fdto First Digit Timeout
|
||||||
val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0);
|
* \param sdto Other Digits Timeout
|
||||||
data[i] = AST_LIN2MU((int)val);
|
*
|
||||||
}
|
* \retval 0 if all digits were successfully received
|
||||||
|
* \retval 1 if a timeout occurred
|
||||||
/* wrap back around from 8000 */
|
* \retval -1 if the caller hung up or on channel errors
|
||||||
|
*/
|
||||||
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;
|
|
||||||
ast_format_set(&wf.subclass.format, AST_FORMAT_ULAW, 0);
|
|
||||||
wf.offset = AST_FRIENDLY_OFFSET;
|
|
||||||
wf.mallocd = 0;
|
|
||||||
wf.data.ptr = 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", ast_channel_name(chan));
|
|
||||||
ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",ast_channel_name(chan));
|
|
||||||
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)
|
static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto)
|
||||||
{
|
{
|
||||||
int res = 0;
|
int rtn = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int r;
|
int r;
|
||||||
struct ast_frame *f;
|
struct ast_frame *f;
|
||||||
struct timeval lastdigittime;
|
struct timeval lastdigittime;
|
||||||
|
|
||||||
lastdigittime = ast_tvnow();
|
lastdigittime = ast_tvnow();
|
||||||
for (;;) {
|
while (i < length && i < sizeof(digit_string) - 1) {
|
||||||
/* if outa time, leave */
|
/* If timed out, leave */
|
||||||
if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((i > 0) ? sdto : fdto)) {
|
if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((i > 0) ? sdto : fdto)) {
|
||||||
ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", ast_channel_name(chan));
|
ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", ast_channel_name(chan));
|
||||||
ast_debug(1,"AlarmReceiver: DTMF timeout on chan %s\n",ast_channel_name(chan));
|
ast_debug(1, "AlarmReceiver: DTMF timeout on chan %s\n", ast_channel_name(chan));
|
||||||
res = 1;
|
rtn = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,68 +193,71 @@ static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
f = ast_read(chan);
|
if ((f = ast_read(chan)) == NULL) {
|
||||||
|
rtn = -1;
|
||||||
if (f == NULL) {
|
|
||||||
res = -1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If they hung up, leave */
|
/* If they hung up, leave */
|
||||||
if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
|
if ((f->frametype == AST_FRAME_CONTROL)
|
||||||
|
&& (f->subclass.integer == AST_CONTROL_HANGUP)) {
|
||||||
if (f->data.uint32) {
|
if (f->data.uint32) {
|
||||||
ast_channel_hangupcause_set(chan, f->data.uint32);
|
ast_channel_hangupcause_set(chan, f->data.uint32);
|
||||||
}
|
}
|
||||||
ast_frfree(f);
|
ast_frfree(f);
|
||||||
res = -1;
|
rtn = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if not DTMF, just do it again */
|
/* If not DTMF, just do it again */
|
||||||
if (f->frametype != AST_FRAME_DTMF) {
|
if (f->frametype != AST_FRAME_DTMF) {
|
||||||
ast_frfree(f);
|
ast_frfree(f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
digit_string[i++] = f->subclass.integer; /* save digit */
|
/* Save digit */
|
||||||
|
digit_string[i++] = f->subclass.integer;
|
||||||
ast_frfree(f);
|
ast_frfree(f);
|
||||||
|
|
||||||
/* If we have all the digits we expect, leave */
|
|
||||||
if(i >= length)
|
|
||||||
break;
|
|
||||||
|
|
||||||
lastdigittime = ast_tvnow();
|
lastdigittime = ast_tvnow();
|
||||||
}
|
}
|
||||||
|
|
||||||
digit_string[i] = '\0'; /* Nul terminate the end of the digit string */
|
/* Null terminate the end of the digit string */
|
||||||
return res;
|
digit_string[i] = '\0';
|
||||||
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
* Write the metadata to the log file
|
* \brief Write metadata to log file
|
||||||
*/
|
*
|
||||||
static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
|
* \param logfile Log File Pointer
|
||||||
|
* \param signalling_type Signaling Type
|
||||||
|
* \param chan Asterisk Channel
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
|
static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan)
|
||||||
{
|
{
|
||||||
int res = 0;
|
|
||||||
struct timeval t;
|
struct timeval t;
|
||||||
struct ast_tm now;
|
struct ast_tm now;
|
||||||
char *cl;
|
char *cl;
|
||||||
char *cn;
|
char *cn;
|
||||||
char workstring[80];
|
char workstring[80];
|
||||||
char timestamp[80];
|
char timestamp[80];
|
||||||
|
|
||||||
/* Extract the caller ID location */
|
/* Extract the caller ID location */
|
||||||
ast_copy_string(workstring,
|
ast_copy_string(workstring,
|
||||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
|
S_COR(ast_channel_caller(chan)->id.number.valid,
|
||||||
sizeof(workstring));
|
ast_channel_caller(chan)->id.number.str, ""), sizeof(workstring));
|
||||||
ast_shrink_phone_number(workstring);
|
ast_shrink_phone_number(workstring);
|
||||||
if (ast_strlen_zero(workstring)) {
|
if (ast_strlen_zero(workstring)) {
|
||||||
cl = "<unknown>";
|
cl = "<unknown>";
|
||||||
} else {
|
} else {
|
||||||
cl = workstring;
|
cl = workstring;
|
||||||
}
|
}
|
||||||
cn = S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>");
|
cn = S_COR(ast_channel_caller(chan)->id.name.valid,
|
||||||
|
ast_channel_caller(chan)->id.name.str, "<unknown>");
|
||||||
|
|
||||||
/* Get the current time */
|
/* Get the current time */
|
||||||
t = ast_tvnow();
|
t = ast_tvnow();
|
||||||
@@ -326,137 +266,208 @@ static int write_metadata( FILE *logfile, char *signalling_type, struct ast_chan
|
|||||||
/* Format the time */
|
/* Format the time */
|
||||||
ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
|
ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
|
||||||
|
|
||||||
res = fprintf(logfile, "\n\n[metadata]\n\n");
|
if (fprintf(logfile, "\n\n[metadata]\n\n"
|
||||||
if (res >= 0) {
|
"PROTOCOL=%s\n"
|
||||||
res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
|
"CALLINGFROM=%s\n"
|
||||||
}
|
"CALLERNAME=%s\n"
|
||||||
if (res >= 0) {
|
"TIMESTAMP=%s\n\n"
|
||||||
res = fprintf(logfile, "CALLINGFROM=%s\n", cl);
|
"[events]\n\n", signalling_type, cl, cn, timestamp) < 0) {
|
||||||
}
|
|
||||||
if (res >= 0) {
|
|
||||||
res = fprintf(logfile, "CALLERNAME=%s\n", cn);
|
|
||||||
}
|
|
||||||
if (res >= 0) {
|
|
||||||
res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
|
|
||||||
}
|
|
||||||
if (res >= 0) {
|
|
||||||
res = fprintf(logfile, "[events]\n\n");
|
|
||||||
}
|
|
||||||
if (res < 0) {
|
|
||||||
ast_verb(3, "AlarmReceiver: can't write metadata\n");
|
ast_verb(3, "AlarmReceiver: can't write metadata\n");
|
||||||
ast_debug(1,"AlarmReceiver: can't write metadata\n");
|
ast_debug(1, "AlarmReceiver: can't write metadata\n");
|
||||||
} else {
|
return -1;
|
||||||
res = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
* Write a single event to the log file
|
* \brief Log a single event
|
||||||
*/
|
*
|
||||||
static int write_event( FILE *logfile, event_node_t *event)
|
* \param logfile Log File Pointer
|
||||||
|
* \param event Event Structure
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
|
static int write_event(FILE *logfile, event_node_t *event)
|
||||||
{
|
{
|
||||||
int res = 0;
|
if (fprintf(logfile, "%s\n", event->data) < 0) {
|
||||||
|
return -1;
|
||||||
if (fprintf(logfile, "%s\n", event->data) < 0)
|
}
|
||||||
res = -1;
|
return 0;
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
* If we are configured to log events, do so here.
|
* \brief Log events if configuration key logindividualevents is enabled
|
||||||
*
|
*
|
||||||
*/
|
* \param chan Asterisk Channel
|
||||||
static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event)
|
* \param signalling_type Signaling Type
|
||||||
|
* \param event Event Structure
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
|
static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event)
|
||||||
{
|
{
|
||||||
|
char workstring[sizeof(event_spool_dir) + sizeof(event_file)] = "";
|
||||||
int res = 0;
|
|
||||||
char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
|
|
||||||
int fd;
|
int fd;
|
||||||
FILE *logfile;
|
FILE *logfile;
|
||||||
event_node_t *elp = event;
|
event_node_t *elp = event;
|
||||||
|
|
||||||
if (!ast_strlen_zero(event_spool_dir)) {
|
if (!ast_strlen_zero(event_spool_dir)) {
|
||||||
|
|
||||||
/* Make a template */
|
/* Make a template */
|
||||||
ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
|
ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
|
||||||
strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
|
strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
|
||||||
|
|
||||||
/* Make the temporary file */
|
/* Make the temporary file */
|
||||||
fd = mkstemp(workstring);
|
fd = mkstemp(workstring);
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
ast_verb(3, "AlarmReceiver: can't make temporary file\n");
|
ast_verb(3, "AlarmReceiver: can't make temporary file\n");
|
||||||
ast_debug(1,"AlarmReceiver: can't make temporary file\n");
|
ast_debug(1, "AlarmReceiver: can't make temporary file\n");
|
||||||
res = -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res) {
|
if ((logfile = fdopen(fd, "w")) == NULL) {
|
||||||
logfile = fdopen(fd, "w");
|
return -1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Write the file */
|
||||||
|
if (write_metadata(logfile, signalling_type, chan) != 0) {
|
||||||
|
fflush(logfile);
|
||||||
|
fclose(logfile);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((write_event(logfile, elp) > 0) && (elp != NULL)) {
|
||||||
|
elp = elp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(logfile);
|
||||||
|
fclose(logfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
* This function implements the logic to receive the Ademco contact ID format.
|
* \brief Verify Ademco checksum
|
||||||
*
|
* \since 11.0
|
||||||
* The function will return 0 when the caller hangs up, else a -1 if there was a problem.
|
*
|
||||||
*/
|
* \param event Received DTMF String
|
||||||
static int receive_ademco_contact_id(struct ast_channel *chan, const void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
|
* \param expected Number of Digits expected
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
|
static int ademco_verify_checksum(char *event, int expected)
|
||||||
{
|
{
|
||||||
|
int checksum = 0;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
|
for (j = 0; j < expected; j++) {
|
||||||
|
for (i = 0; i < ARRAY_LEN(digits_mapping); i++) {
|
||||||
|
if (digits_mapping[i].digit == event[j]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= ARRAY_LEN(digits_mapping)) {
|
||||||
|
ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum += digits_mapping[i].weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checksum is mod(15) of the total */
|
||||||
|
if (!(checksum % 15)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Send a single tone burst for a specifed duration and frequency.
|
||||||
|
* \since 11.0
|
||||||
|
*
|
||||||
|
* \param chan Asterisk Channel
|
||||||
|
* \param tone_freq Frequency of the tone to send
|
||||||
|
* \param tone_duration Tone duration in ms
|
||||||
|
* \param tldn Tone loudness
|
||||||
|
* \param delay Delay before sending the tone
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
|
static int send_tone_burst(struct ast_channel *chan, const char *tone_freq, int tone_duration, int tldn, int delay)
|
||||||
|
{
|
||||||
|
if (delay && ast_safe_sleep(chan, delay)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_playtones_start(chan, tldn, tone_freq, 0)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_safe_sleep(chan, tone_duration)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_playtones_stop(chan);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Receive Ademco ContactID Data String
|
||||||
|
*
|
||||||
|
* \param chan Asterisk Channel
|
||||||
|
* \param fdto First Digit Timeout
|
||||||
|
* \param sdto Other Digits Timeout
|
||||||
|
* \param tldn Tone loudness
|
||||||
|
* \param ehead Pointer to events list
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
|
static int receive_ademco_contact_id(struct ast_channel *chan, int fdto, int sdto, int tldn, event_node_t **ehead)
|
||||||
|
{
|
||||||
int res = 0;
|
int res = 0;
|
||||||
int checksum;
|
int exit_on_next = 0;
|
||||||
char event[17];
|
char event[17];
|
||||||
event_node_t *enew, *elp;
|
event_node_t *enew, *elp;
|
||||||
int got_some_digits = 0;
|
int got_some_digits = 0;
|
||||||
int events_received = 0;
|
int events_received = 0;
|
||||||
int ack_retries = 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");
|
database_increment("calls-received");
|
||||||
|
|
||||||
/* Wait for first event */
|
/* Wait for first event */
|
||||||
ast_verb(4, "AlarmReceiver: Waiting for first event from panel\n");
|
ast_verb(4, "AlarmReceiver: Waiting for first event from panel...\n");
|
||||||
|
|
||||||
while (res >= 0) {
|
while (res >= 0) {
|
||||||
|
res = 0;
|
||||||
if (got_some_digits == 0) {
|
if (got_some_digits == 0) {
|
||||||
/* Send ACK tone sequence */
|
/* Send ACK tone sequence */
|
||||||
ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
|
ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
|
||||||
res = send_tone_burst(chan, 1400.0, 100, tldn);
|
res = send_tone_burst(chan, "1400", 100, tldn, 0);
|
||||||
if (!res)
|
|
||||||
res = ast_safe_sleep(chan, 100);
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
|
ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
|
||||||
res = send_tone_burst(chan, 2300.0, 100, tldn);
|
res = send_tone_burst(chan, "2300", 100, tldn, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( res >= 0)
|
if (res) {
|
||||||
res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exit_on_next) {
|
||||||
|
res = send_tone_burst(chan, "1400", 900, tldn, 200);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
if (events_received == 0) {
|
if (events_received == 0) {
|
||||||
/* Hangup with no events received should be logged in the DB */
|
/* Hangup with no events received should be logged in the DB */
|
||||||
@@ -468,11 +479,10 @@ static int receive_ademco_contact_id(struct ast_channel *chan, const void *data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast_verb(4, "AlarmReceiver: App exiting...\n");
|
ast_verb(4, "AlarmReceiver: App exiting...\n");
|
||||||
res = -1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res != 0) {
|
if (res) {
|
||||||
/* Didn't get all of the digits */
|
/* Didn't get all of the digits */
|
||||||
ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
|
ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
|
||||||
|
|
||||||
@@ -489,28 +499,7 @@ static int receive_ademco_contact_id(struct ast_channel *chan, const void *data,
|
|||||||
ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
|
ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
|
||||||
|
|
||||||
/* Calculate checksum */
|
/* Calculate checksum */
|
||||||
|
if (ademco_verify_checksum(event, 16)) {
|
||||||
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");
|
database_increment("checksum-errors");
|
||||||
ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
|
ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
|
||||||
ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
|
ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
|
||||||
@@ -518,9 +507,8 @@ static int receive_ademco_contact_id(struct ast_channel *chan, const void *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check the message type for correctness */
|
/* Check the message type for correctness */
|
||||||
|
if (strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)) {
|
||||||
if (strncmp(event + 4, "18", 2)) {
|
if (strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
|
||||||
if (strncmp(event + 4, "98", 2)) {
|
|
||||||
database_increment("format-errors");
|
database_increment("format-errors");
|
||||||
ast_verb(2, "AlarmReceiver: Wrong message type\n");
|
ast_verb(2, "AlarmReceiver: Wrong message type\n");
|
||||||
ast_debug(1, "AlarmReceiver: Wrong message type\n");
|
ast_debug(1, "AlarmReceiver: Wrong message type\n");
|
||||||
@@ -532,46 +520,55 @@ static int receive_ademco_contact_id(struct ast_channel *chan, const void *data,
|
|||||||
|
|
||||||
/* Queue the Event */
|
/* Queue the Event */
|
||||||
if (!(enew = ast_calloc(1, sizeof(*enew)))) {
|
if (!(enew = ast_calloc(1, sizeof(*enew)))) {
|
||||||
res = -1;
|
return -1;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enew->next = NULL;
|
enew->next = NULL;
|
||||||
ast_copy_string(enew->data, event, sizeof(enew->data));
|
ast_copy_string(enew->data, event, sizeof(enew->data));
|
||||||
|
|
||||||
/*
|
/* Insert event onto end of list */
|
||||||
* Insert event onto end of list
|
if (*ehead == NULL) {
|
||||||
*/
|
|
||||||
if (*ehead == NULL)
|
|
||||||
*ehead = enew;
|
*ehead = enew;
|
||||||
else {
|
} else {
|
||||||
for(elp = *ehead; elp->next != NULL; elp = elp->next)
|
for (elp = *ehead; elp->next != NULL; elp = elp->next) {
|
||||||
;
|
;
|
||||||
|
}
|
||||||
elp->next = enew;
|
elp->next = enew;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res > 0)
|
/* Audio call follows, exit alarm receiver app */
|
||||||
res = 0;
|
if (!strncmp(event + 7, ADEMCO_AUDIO_CALL_NEXT, 3)) {
|
||||||
|
ast_verb(4, "AlarmReceiver: App exiting... Audio call next!\n");
|
||||||
|
exit_on_next = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Let the user have the option of logging the single event before sending the kissoff tone */
|
/* Let the user have the option of logging the single event before sending the kissoff tone */
|
||||||
if ((res == 0) && (log_individual_events))
|
if (log_individual_events) {
|
||||||
res = log_events(chan, ADEMCO_CONTACT_ID, enew);
|
res = log_events(chan, ADEMCO_CONTACT_ID, enew);
|
||||||
/* Wait 200 msec before sending kissoff */
|
if (res) {
|
||||||
if (res == 0)
|
return -1;
|
||||||
res = ast_safe_sleep(chan, 200);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Send the kissoff tone */
|
/* Send the kissoff tone (1400 Hz, 900 ms, after 200ms delay) */
|
||||||
if (res == 0)
|
res = send_tone_burst(chan, "1400", 900, tldn, 200);
|
||||||
res = send_tone_burst(chan, 1400.0, 900, tldn);
|
if (res) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
* This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
|
* \brief This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
|
||||||
* This function will always return 0.
|
*
|
||||||
*/
|
* \param chan Asterisk Channel
|
||||||
|
* \param data Application data
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
|
static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
|
||||||
{
|
{
|
||||||
int res = 0;
|
int res = 0;
|
||||||
@@ -598,8 +595,9 @@ static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
|
|||||||
/* Answer the channel if it is not already */
|
/* Answer the channel if it is not already */
|
||||||
ast_verb(4, "AlarmReceiver: Answering channel\n");
|
ast_verb(4, "AlarmReceiver: Answering channel\n");
|
||||||
if (ast_channel_state(chan) != AST_STATE_UP) {
|
if (ast_channel_state(chan) != AST_STATE_UP) {
|
||||||
if ((res = ast_answer(chan)))
|
if ((res = ast_answer(chan))) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for the connection to settle post-answer */
|
/* Wait for the connection to settle post-answer */
|
||||||
@@ -611,27 +609,25 @@ static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
|
|||||||
/* Determine the protocol to receive in advance */
|
/* Determine the protocol to receive in advance */
|
||||||
/* Note: Ademco contact is the only one supported at this time */
|
/* Note: Ademco contact is the only one supported at this time */
|
||||||
/* Others may be added later */
|
/* Others may be added later */
|
||||||
if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
|
if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)) {
|
||||||
receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
|
receive_ademco_contact_id(chan, fdtimeout, sdtimeout, toneloudness, &event_head);
|
||||||
else
|
} else {
|
||||||
res = -1;
|
res = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Events queued by receiver, write them all out here if so configured */
|
/* Events queued by receiver, write them all out here if so configured */
|
||||||
if ((!res) && (log_individual_events == 0))
|
if ((!res) && (log_individual_events == 0)) {
|
||||||
res = log_events(chan, signalling_type, event_head);
|
res = log_events(chan, signalling_type, event_head);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/* Do we exec a command line at the end? */
|
||||||
* Do we exec a command line at the end?
|
|
||||||
*/
|
|
||||||
if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
|
if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
|
||||||
ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
|
ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
|
||||||
ast_safe_system(event_app);
|
ast_safe_system(event_app);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Free up the data allocated in our linked list */
|
||||||
* Free up the data allocated in our linked list
|
|
||||||
*/
|
|
||||||
for (elp = event_head; (elp != NULL);) {
|
for (elp = event_head; (elp != NULL);) {
|
||||||
efree = elp;
|
efree = elp;
|
||||||
elp = elp->next;
|
elp = elp->next;
|
||||||
@@ -641,13 +637,16 @@ static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
* Load the configuration from the configuration file
|
* \brief Load the configuration from the configuration file
|
||||||
*/
|
*
|
||||||
|
* \retval 1 success
|
||||||
|
* \retval 0 failure
|
||||||
|
*/
|
||||||
static int load_config(void)
|
static int load_config(void)
|
||||||
{
|
{
|
||||||
struct ast_config *cfg;
|
struct ast_config *cfg;
|
||||||
const char *p;
|
const char *value;
|
||||||
struct ast_flags config_flags = { 0 };
|
struct ast_flags config_flags = { 0 };
|
||||||
|
|
||||||
/* Read in the config file */
|
/* Read in the config file */
|
||||||
@@ -657,78 +656,90 @@ static int load_config(void)
|
|||||||
ast_verb(4, "AlarmReceiver: No config file\n");
|
ast_verb(4, "AlarmReceiver: No config file\n");
|
||||||
return 0;
|
return 0;
|
||||||
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
|
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
|
||||||
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", ALMRCV_CONFIG);
|
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n",
|
||||||
|
ALMRCV_CONFIG);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
|
||||||
p = ast_variable_retrieve(cfg, "general", "eventcmd");
|
|
||||||
if (p) {
|
|
||||||
ast_copy_string(event_app, p, sizeof(event_app));
|
|
||||||
}
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
p = ast_variable_retrieve(cfg, "general", "timestampformat");
|
|
||||||
if (p) {
|
|
||||||
ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
|
|
||||||
}
|
|
||||||
|
|
||||||
p = ast_variable_retrieve(cfg, "general", "db-family");
|
|
||||||
if (p) {
|
|
||||||
ast_copy_string(db_family, p, sizeof(db_family));
|
|
||||||
}
|
|
||||||
ast_config_destroy(cfg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((value = ast_variable_retrieve(cfg, "general", "eventcmd")) != NULL) {
|
||||||
|
ast_copy_string(event_app, value, sizeof(event_app));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((value = ast_variable_retrieve(cfg, "general", "loudness")) != NULL) {
|
||||||
|
toneloudness = atoi(value);
|
||||||
|
if (toneloudness < 100) {
|
||||||
|
toneloudness = 100;
|
||||||
|
} else if (toneloudness > 8192) {
|
||||||
|
toneloudness = 8192;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((value = ast_variable_retrieve(cfg, "general", "fdtimeout")) != NULL) {
|
||||||
|
fdtimeout = atoi(value);
|
||||||
|
if (fdtimeout < 1000) {
|
||||||
|
fdtimeout = 1000;
|
||||||
|
} else if (fdtimeout > 10000) {
|
||||||
|
fdtimeout = 10000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((value = ast_variable_retrieve(cfg, "general", "sdtimeout")) != NULL) {
|
||||||
|
sdtimeout = atoi(value);
|
||||||
|
if (sdtimeout < 110) {
|
||||||
|
sdtimeout = 110;
|
||||||
|
} else if (sdtimeout > 4000) {
|
||||||
|
sdtimeout = 4000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((value = ast_variable_retrieve(cfg, "general", "logindividualevents")) != NULL) {
|
||||||
|
log_individual_events = ast_true(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((value = ast_variable_retrieve(cfg, "general", "eventspooldir")) != NULL) {
|
||||||
|
ast_copy_string(event_spool_dir, value, sizeof(event_spool_dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((value = ast_variable_retrieve(cfg, "general", "timestampformat")) != NULL) {
|
||||||
|
ast_copy_string(time_stamp_format, value, sizeof(time_stamp_format));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((value = ast_variable_retrieve(cfg, "general", "db-family")) != NULL) {
|
||||||
|
ast_copy_string(db_family, value, sizeof(db_family));
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_config_destroy(cfg);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
* These functions are required to implement an Asterisk App.
|
* \brief Unregister Alarm Receiver App
|
||||||
*/
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
static int unload_module(void)
|
static int unload_module(void)
|
||||||
{
|
{
|
||||||
return ast_unregister_application(app);
|
return ast_unregister_application(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Register Alarm Receiver App
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
static int load_module(void)
|
static int load_module(void)
|
||||||
{
|
{
|
||||||
if (load_config()) {
|
if (load_config()) {
|
||||||
if (ast_register_application_xml(app, alarmreceiver_exec))
|
if (ast_register_application_xml(app, alarmreceiver_exec)) {
|
||||||
return AST_MODULE_LOAD_FAILURE;
|
return AST_MODULE_LOAD_FAILURE;
|
||||||
|
}
|
||||||
return AST_MODULE_LOAD_SUCCESS;
|
return AST_MODULE_LOAD_SUCCESS;
|
||||||
} else
|
} else {
|
||||||
return AST_MODULE_LOAD_DECLINE;
|
return AST_MODULE_LOAD_DECLINE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");
|
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");
|
||||||
|
Reference in New Issue
Block a user