Adds new formats to app_alarmreceiver, ALAW calls support and enhanced protection.

Commiting this on behalf of Kaloyan Kovachev (license 5506).
AlarmReceiver now supports the following DTMF signaling types:
 - ContactId
 - 4x1
 - 4x2
 - High Speed
 - Super Fast
We are also auto-detecting which signaling is being received. So support for
those protocols should work out-the-box. Correctly identify ALAW / ULAW calls.
Some enhanced protection for broken panels and malicious callers where added.

(closes issue ASTERISK-20289)
Reported by: Kaloyan Kovachev

Review: https://reviewboard.asterisk.org/r/2088/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@375150 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Pedro Kiefer 13 years ago
parent 372e29620c
commit 8b34dc8192

@ -34,7 +34,7 @@
* \addtogroup configuration_file Configuration Files
*/
/*!
/*!
* \page alarmreceiver.conf alarmreceiver.conf
* \verbinclude alarmreceiver.conf.sample
*/
@ -67,9 +67,65 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/indications.h"
#define ALMRCV_CONFIG "alarmreceiver.conf"
#define UNKNOWN_FORMAT "UNKNOWN_FORMAT"
#define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
/*
AAAA _ID_ P CCC XX ZZZ S
where AAAA is the account number, _ID_ is 18 or 98, P is the pin status (alarm or restore), CCC
is the alarm code which is pre-defined by Ademco (but you may be able to reprogram it in the panel), XX
is the dialer group, partition or area number, ZZZ is the zone or user number and S is the checksum
*/
#define ADEMCO_EXPRESS_4_1 "ADEMCO_EXPRESS_4_1"
/*
AAAA _ID_ C S
where AAAA is the account number, _ID_ is 17, C is the alarm code and S is the checksum.
*/
#define ADEMCO_EXPRESS_4_2 "ADEMCO_EXPRESS_4_2"
/*
AAAA _ID_ C Z S
where AAAA is the account number, _ID_ is 27, C is the alarm code, Z is the zone or user number and S is the checksum.
*/
#define ADEMCO_HIGH_SPEED "ADEMCO_HIGH_SPEED"
/*
AAAA _ID_ PPPP PPPP X S
where AAAA is the account number, _ID_ is 55, PPPP PPPP is the status of each zone, X
is a special digit which describes the type of information in the PPPP PPPP fields and S is checksum.
Each P field contains one of the following values:
1 new alarm 3 new restore 5 normal
2 new opening 4 new closing 6 outstanding
The X field contains one of the following values:
0 AlarmNet messages
1 ambush or duress
2 opening by user (the first P field contains the user number)
3 bypass (the P fields indicate which zones are bypassed)
4 closing by user (the first P field contain the user number)
5 trouble (the P fields contain which zones are in trouble)
6 system trouble
7 normal message (the P fields indicate zone status)
8 low battery (the P fields indicate zone status)
9 test (the P fields indicate zone status)
*/
#define ADEMCO_SUPER_FAST "ADEMCO_SUPER_FAST"
/*
AAAA _ID_ PPPP PPPP X
where AAA is the account number, _ID_ is 56
*/
#define ADEMCO_MSG_TYPE_1 "18"
#define ADEMCO_MSG_TYPE_2 "98"
#define ADEMCO_MSG_TYPE_3 "17"
#define ADEMCO_MSG_TYPE_4 "27"
#define ADEMCO_MSG_TYPE_5 "55"
#define ADEMCO_MSG_TYPE_6 "56"
#define ADEMCO_AUDIO_CALL_NEXT "606"
struct {
@ -86,6 +142,8 @@ struct event_node{
typedef struct event_node event_node_t;
struct timeval call_start_time;
static const char app[] = "AlarmReceiver";
/*** DOCUMENTATION
<application name="AlarmReceiver" language="en_US">
@ -101,8 +159,20 @@ static const char app[] = "AlarmReceiver";
events to the standard input of the application.
The configuration file also contains settings for DTMF timing, and for the loudness of the
acknowledgement tones.</para>
<note><para>Only 1 signalling format is supported at this time: Ademco Contact ID.</para></note>
<note><para>Few Ademco DTMF signalling formats are detected automaticaly: Contact ID, Express 4+1,
Express 4+2, High Speed and Super Fast.</para></note>
</description>
<para>The application is affected by the following variables:</para>
<variablelist>
<variable name="ALARMRECEIVER_CALL_LIMIT">
<para>Maximum call time, in milliseconds.</para>
<para>If set, this variable causes application to exit after the specified time.</para>
</variable>
<variable name="ALARMRECEIVER_RETRIES_LIMIT">
<para>Maximum number of retries per call.</para>
<para>If set, this variable causes application to exit after the specified number of messages.</para>
</variable>
</variablelist>
<see-also>
<ref type="filename">alarmreceiver.conf</ref>
</see-also>
@ -112,8 +182,10 @@ static const char app[] = "AlarmReceiver";
/* Config Variables */
static int fdtimeout = 2000;
static int sdtimeout = 200;
static int answait = 1250;
static int toneloudness = 4096;
static int log_individual_events = 0;
static int no_group_meta = 0;
static char event_spool_dir[128] = {'\0'};
static char event_app[128] = {'\0'};
static char db_family[128] = {'\0'};
@ -172,26 +244,24 @@ static void database_increment(char *key)
* \param chan Asterisk Channel
* \param digit_string Digits String
* \param buf_size The size of the Digits String buffer
* \param length Length of the message we expect
* \param fdto First Digit Timeout
* \param sdto Other Digits Timeout
* \param expected Digits expected for this message type
* \param received Pointer to number of digits received so far
*
* \retval 0 if all digits were successfully received
* \retval 1 if a timeout occurred
* \retval -1 if the caller hung up or on channel errors
*/
static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int buf_size, int length, int fdto, int sdto)
static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int buf_size, int expected, int *received)
{
int rtn = 0;
int i = 0;
int r;
struct ast_frame *f;
struct timeval lastdigittime;
lastdigittime = ast_tvnow();
while (i < length && i < buf_size - 1) {
while (*received < expected && *received < buf_size - 1) {
/* If timed out, leave */
if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((i > 0) ? sdto : fdto)) {
if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((*received > 0) ? sdtimeout : fdtimeout)) {
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));
rtn = 1;
@ -226,14 +296,15 @@ static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int
}
/* Save digit */
digit_string[i++] = f->subclass.integer;
digit_string[(*received)++] = f->subclass.integer;
ast_frfree(f);
lastdigittime = ast_tvnow();
}
/* Null terminate the end of the digit string */
digit_string[i] = '\0';
/* Null terminate the end of the digit_string */
digit_string[*received] = '\0';
return rtn;
}
@ -243,11 +314,12 @@ static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int
* \param logfile Log File Pointer
* \param signalling_type Signaling Type
* \param chan Asterisk Channel
* \param no_checksum Expecting messages without checksum
*
* \retval 0 success
* \retval -1 failure
*/
static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan)
static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum)
{
struct timeval t;
struct ast_tm now;
@ -276,18 +348,27 @@ static int write_metadata(FILE *logfile, char *signalling_type, struct ast_chann
/* Format the time */
ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
if (fprintf(logfile, "\n\n[metadata]\n\n"
if (no_group_meta && fprintf(logfile, "PROTOCOL=%s\n"
"CHECKSUM=%s\n"
"CALLINGFROM=%s\n"
"CALLERNAME=%s\n"
"TIMESTAMP=%s\n\n",
signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
return 0;
} else if (fprintf(logfile, "\n\n[metadata]\n\n"
"PROTOCOL=%s\n"
"CHECKSUM=%s\n"
"CALLINGFROM=%s\n"
"CALLERNAME=%s\n"
"TIMESTAMP=%s\n\n"
"[events]\n\n", signalling_type, cl, cn, timestamp) < 0) {
ast_verb(3, "AlarmReceiver: can't write metadata\n");
ast_debug(1, "AlarmReceiver: can't write metadata\n");
return -1;
"[events]\n\n",
signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
return 0;
}
return 0;
ast_verb(3, "AlarmReceiver: can't write metadata\n");
ast_debug(1, "AlarmReceiver: can't write metadata\n");
return -1;
}
/*!
@ -301,23 +382,25 @@ static int write_metadata(FILE *logfile, char *signalling_type, struct ast_chann
*/
static int write_event(FILE *logfile, event_node_t *event)
{
if (fprintf(logfile, "%s\n", event->data) < 0) {
if (fprintf(logfile, "%s%s\n", no_group_meta ? "event=" : "", event->data) < 0) {
return -1;
}
return 0;
}
/*!
* \brief Log events if configuration key logindividualevents is enabled
* \brief Log events if configuration key logindividualevents is enabled or on exit
*
* \param chan Asterisk Channel
* \param signalling_type Signaling Type
* \param event Event Structure
* \param no_checksum Expecting messages without checksum
*
* \retval 0 success
* \retval -1 failure
*/
static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event)
static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event, int no_checksum)
{
char workstring[sizeof(event_spool_dir) + sizeof(event_file)] = "";
int fd;
@ -344,13 +427,13 @@ static int log_events(struct ast_channel *chan, char *signalling_type, event_nod
}
/* Write the file */
if (write_metadata(logfile, signalling_type, chan) != 0) {
if (write_metadata(logfile, signalling_type, chan, no_checksum)) {
fflush(logfile);
fclose(logfile);
return -1;
}
while ((write_event(logfile, elp) > 0) && (elp != NULL)) {
while ((elp != NULL) && (write_event(logfile, elp) == 0)) {
elp = elp->next;
}
@ -406,19 +489,18 @@ static int ademco_verify_checksum(char *event, int expected)
* \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)
static int send_tone_burst(struct ast_channel *chan, const char *tone_freq, int tone_duration, int delay)
{
if (delay && ast_safe_sleep(chan, delay)) {
return -1;
}
if (ast_playtones_start(chan, tldn, tone_freq, 0)) {
if (ast_playtones_start(chan, toneloudness, tone_freq, 0)) {
return -1;
}
@ -431,26 +513,113 @@ static int send_tone_burst(struct ast_channel *chan, const char *tone_freq, int
}
/*!
* \brief Receive Ademco ContactID Data String
* \brief Check if the message is in known and valid Ademco format
*
* \param signalling_type Expected signalling type for the message
* \param event event received
*
* \retval 0 The event is valid
* \retval -1 The event is not valid
*/
static int ademco_check_valid(char *signalling_type, char *event)
{
if (!strcmp(signalling_type, UNKNOWN_FORMAT)) {
return 1;
}
if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
&& strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
&& strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
return -1;
}
if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_1) && strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
return -1;
}
if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2) && strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
return -1;
}
if (!strcmp(signalling_type, ADEMCO_HIGH_SPEED) && strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
return -1;
}
if (!strcmp(signalling_type, ADEMCO_SUPER_FAST) && strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
return -1;
}
return 0;
}
/*!
* \brief Detect the message format of an event
*
* \param signalling_type Expected signalling type for the message
* \param event event received
* \param no_checksum Should we calculate checksum for the message
*
* \returns The expected digits for the detected event type
*/
static int ademco_detect_format(char *signalling_type, char *event, int *no_checksum)
{
int res = 16;
if (!strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
|| !strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
sprintf(signalling_type, "%s", ADEMCO_CONTACT_ID);
}
if (!strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_1);
res = 8;
}
if (!strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_2);
res = 9;
}
if (!strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
sprintf(signalling_type, "%s", ADEMCO_HIGH_SPEED);
}
if (!strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
sprintf(signalling_type, "%s", ADEMCO_SUPER_FAST);
*no_checksum = 1;
res = 15;
}
if (strcmp(signalling_type, UNKNOWN_FORMAT)) {
ast_verb(4, "AlarmMonitoring: Detected format %s.\n", signalling_type);
ast_debug(1, "AlarmMonitoring: Autodetected format %s.\n", signalling_type);
}
return res;
}
/*!
* \brief Receive Ademco ContactID or other format 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
* \param signalling_type Expected signalling type for the message
* \param no_checksum Should we calculate checksum for the message
*
* \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)
static int receive_ademco_event(struct ast_channel *chan, event_node_t **ehead, char *signalling_type, int *no_checksum)
{
int res = 0;
int exit_on_next = 0;
const char *limit;
char event[17];
event_node_t *enew, *elp;
int got_some_digits = 0;
int events_received = 0;
int ack_retries = 0;
int limit_retries = 0;
int expected_length = sizeof(event) - 1;
database_increment("calls-received");
@ -458,43 +627,90 @@ static int receive_ademco_contact_id(struct ast_channel *chan, int fdto, int sdt
ast_verb(4, "AlarmReceiver: Waiting for first event from panel...\n");
while (res >= 0) {
int digits_received = 0;
res = 0;
if (log_individual_events) {
sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
expected_length = 16;
*no_checksum = 0;
}
if (got_some_digits == 0) {
/* Send ACK tone sequence */
ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
res = send_tone_burst(chan, "1400", 100, tldn, 0);
res = send_tone_burst(chan, "1400", 100, 0);
if (!res) {
ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
res = send_tone_burst(chan, "2300", 100, tldn, 100);
res = send_tone_burst(chan, "2300", 100, 100);
}
}
if (res) {
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), sizeof(event) - 1, fdto, sdto);
res = receive_dtmf_digits(chan, event, sizeof(event), expected_length, &digits_received);
if (res < 0) {
if (events_received == 0) {
/* Hangup with no events received should be logged in the DB */
database_increment("no-events-received");
ast_verb(4, "AlarmReceiver: No events received!\n");
} else {
if (ack_retries) {
ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
database_increment("ack-retries");
ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
}
}
ast_verb(4, "AlarmReceiver: App exiting...\n");
break;
}
if (!strcmp(signalling_type, UNKNOWN_FORMAT) && digits_received > 5) {
expected_length = ademco_detect_format(signalling_type, event, no_checksum);
if (res > 0) {
if (digits_received == expected_length) {
res = limit_retries = 0;
} else if (digits_received == expected_length - 1
&& (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2)
|| !strcmp(signalling_type, ADEMCO_EXPRESS_4_1))) {
/* ADEMCO EXPRESS without checksum */
res = limit_retries = 0;
expected_length--;
*no_checksum = 1;
ast_verb(4, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
ast_debug(1, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
}
}
}
ast_channel_lock(chan);
limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_CALL_LIMIT");
if (!ast_strlen_zero(limit)) {
if (ast_tvdiff_ms(ast_tvnow(), call_start_time) > atoi(limit)) {
ast_channel_unlock(chan);
return -1;
}
}
limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_RETRIES_LIMIT");
ast_channel_unlock(chan);
if (!ast_strlen_zero(limit)) {
if (limit_retries + 1 >= atoi(limit)) {
return -1;
}
}
if (res) {
/* Didn't get all of the digits */
ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
limit_retries++;
if (!events_received && strcmp(signalling_type, UNKNOWN_FORMAT))
{
sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
expected_length = sizeof(event) - 1;
}
if (!got_some_digits) {
got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
@ -509,7 +725,7 @@ static int receive_ademco_contact_id(struct ast_channel *chan, int fdto, int sdt
ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
/* Calculate checksum */
if (ademco_verify_checksum(event, 16)) {
if (!(*no_checksum) && ademco_verify_checksum(event, expected_length)) {
database_increment("checksum-errors");
ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
@ -517,13 +733,11 @@ static int receive_ademco_contact_id(struct ast_channel *chan, int fdto, int sdt
}
/* Check the message type for correctness */
if (strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)) {
if (strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
database_increment("format-errors");
ast_verb(2, "AlarmReceiver: Wrong message type\n");
ast_debug(1, "AlarmReceiver: Wrong message type\n");
if (ademco_check_valid(signalling_type, event)) {
database_increment("format-errors");
ast_verb(2, "AlarmReceiver: Wrong message type\n");
ast_debug(1, "AlarmReceiver: Wrong message type\n");
continue;
}
}
events_received++;
@ -546,25 +760,22 @@ static int receive_ademco_contact_id(struct ast_channel *chan, int fdto, int sdt
elp->next = enew;
}
/* Audio call follows, exit alarm receiver app */
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 */
if (log_individual_events) {
res = log_events(chan, ADEMCO_CONTACT_ID, enew);
if (res) {
return -1;
}
if (log_individual_events && log_events(chan, signalling_type, enew, *no_checksum)) {
return -1;
}
/* Send the kissoff tone (1400 Hz, 900 ms, after 200ms delay) */
res = send_tone_burst(chan, "1400", 900, tldn, 200);
if (res) {
if (send_tone_burst(chan, "1400", 900, 200)) {
return -1;
}
/* If audio call follows, exit alarm receiver app */
if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
&& !strncmp(event + 7, ADEMCO_AUDIO_CALL_NEXT, 3)) {
ast_verb(4, "AlarmReceiver: App exiting... Audio call next!\n");
return 0;
}
}
return res;
@ -582,53 +793,53 @@ static int receive_ademco_contact_id(struct ast_channel *chan, int fdto, int sdt
static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
{
int res = 0;
int no_checksum = 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_by_id(chan,AST_FORMAT_ULAW)) {
ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",ast_channel_name(chan));
return -1;
if (ast_channel_writeformat(chan)->id != AST_FORMAT_ALAW
&& ast_channel_writeformat(chan)->id != AST_FORMAT_ULAW) {
ast_verb(4, "AlarmReceiver: Setting write format to Mu-law\n");
if (ast_set_write_format_by_id(chan,AST_FORMAT_ULAW)) {
ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",ast_channel_name(chan));
return -1;
}
}
if (ast_set_read_format_by_id(chan,AST_FORMAT_ULAW)) {
ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",ast_channel_name(chan));
return -1;
if (ast_channel_readformat(chan)->id != AST_FORMAT_ALAW
&& ast_channel_readformat(chan)->id != AST_FORMAT_ULAW) {
ast_verb(4, "AlarmReceiver: Setting read format to Mu-law\n");
if (ast_set_read_format_by_id(chan,AST_FORMAT_ULAW)) {
ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",ast_channel_name(chan));
return -1;
}
}
/* Set default values for this invocation of the application */
ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
ast_copy_string(signalling_type, UNKNOWN_FORMAT, sizeof(signalling_type));
call_start_time = ast_tvnow();
/* Answer the channel if it is not already */
ast_verb(4, "AlarmReceiver: Answering channel\n");
if (ast_channel_state(chan) != AST_STATE_UP) {
if ((res = ast_answer(chan))) {
ast_verb(4, "AlarmReceiver: Answering channel\n");
if (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);
if (ast_safe_sleep(chan, answait)) {
return -1;
}
/* 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, fdtimeout, sdtimeout, toneloudness, &event_head);
} else {
res = -1;
}
}
receive_ademco_event(chan, &event_head, signalling_type, &no_checksum);
/* 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);
if (!log_individual_events) {
res = log_events(chan, signalling_type, event_head, no_checksum);
}
/* Do we exec a command line at the end? */
@ -650,14 +861,16 @@ static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
/*!
* \brief Load the configuration from the configuration file
*
* \param reload True on reload
*
* \retval 1 success
* \retval 0 failure
*/
static int load_config(void)
static int load_config(int reload)
{
struct ast_config *cfg;
const char *value;
struct ast_flags config_flags = { 0 };
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
/* Read in the config file */
cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
@ -665,6 +878,8 @@ static int load_config(void)
if (!cfg) {
ast_verb(4, "AlarmReceiver: No config file\n");
return 0;
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
return 1;
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n",
ALMRCV_CONFIG);
@ -702,6 +917,19 @@ static int load_config(void)
}
}
if ((value = ast_variable_retrieve(cfg, "general", "answait")) != NULL) {
answait = atoi(value);
if (answait < 500) {
answait = 500;
} else if (answait > 10000) {
answait = 10000;
}
}
if ((value = ast_variable_retrieve(cfg, "general", "no_group_meta")) != NULL) {
no_group_meta = ast_true(value);
}
if ((value = ast_variable_retrieve(cfg, "general", "logindividualevents")) != NULL) {
log_individual_events = ast_true(value);
}
@ -740,20 +968,33 @@ static int unload_module(void)
* Module loading including tests for configuration or dependencies.
* This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
* or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
* tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
* configuration file or other non-critical problem return
* tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
* configuration file or other non-critical problem return
* AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
*/
static int load_module(void)
{
if (load_config()) {
if (load_config(0)) {
if (ast_register_application_xml(app, alarmreceiver_exec)) {
return AST_MODULE_LOAD_FAILURE;
}
return AST_MODULE_LOAD_SUCCESS;
} else {
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_DECLINE;
}
static int reload(void)
{
if (load_config(1)) {
return AST_MODULE_LOAD_SUCCESS;
}
return AST_MODULE_LOAD_DECLINE;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Alarm Receiver for Asterisk",
.load = load_module,
.unload = unload_module,
.reload = reload,
);

@ -59,6 +59,17 @@ fdtimeout = 2000
sdtimeout = 200
;
; Wait for the connection to settle post-answer. Adjustable from 500 msec. to 10000 msec.
; The default is 1250 msec.
;
answait = 1250
; When logging individual events it may be desirable to skip grouping of metadata
;no_group_meta = yes
;
; The loudness of the ACK and Kissoff tones is adjustable from 100 to 8192.
; The default is 8192. This shouldn't need to be messed with, but is included

Loading…
Cancel
Save