mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-03 11:25:35 +00:00
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
This commit is contained in:
@@ -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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
if (ast_safe_sleep(chan, answait)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Attempt to receive the events */
|
||||
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;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");
|
||||
static int reload(void)
|
||||
{
|
||||
if (load_config(1)) {
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Alarm Receiver for Asterisk",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.reload = reload,
|
||||
);
|
||||
|
Reference in New Issue
Block a user