app_alarmreceiver.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C)  2004 - 2005 Steve Rodgers
00005  *
00006  * Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Central Station Alarm receiver for Ademco Contact ID
00022  * \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
00023  *
00024  * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
00025  *
00026  * Use at your own risk. Please consult the GNU GPL license document included with Asterisk.         *
00027  *
00028  * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
00029  *
00030  * \ingroup applications
00031  */
00032 
00033 /*! \li \ref app_alarmreceiver.c uses the configuration file \ref alarmreceiver.conf
00034  * \addtogroup configuration_file Configuration Files
00035  */
00036 
00037 /*!
00038  * \page alarmreceiver.conf alarmreceiver.conf
00039  * \verbinclude alarmreceiver.conf.sample
00040  */
00041 
00042 /*** MODULEINFO
00043    <support_level>extended</support_level>
00044  ***/
00045 
00046 #include "asterisk.h"
00047 
00048 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 431000 $")
00049 
00050 #include <math.h>
00051 #include <sys/wait.h>
00052 #include <sys/time.h>
00053 
00054 #include "asterisk/lock.h"
00055 #include "asterisk/file.h"
00056 #include "asterisk/channel.h"
00057 #include "asterisk/pbx.h"
00058 #include "asterisk/module.h"
00059 #include "asterisk/translate.h"
00060 #include "asterisk/app.h"
00061 #include "asterisk/dsp.h"
00062 #include "asterisk/config.h"
00063 #include "asterisk/localtime.h"
00064 #include "asterisk/callerid.h"
00065 #include "asterisk/astdb.h"
00066 #include "asterisk/utils.h"
00067 #include "asterisk/indications.h"
00068 #include "asterisk/format_cache.h"
00069 
00070 #define ALMRCV_CONFIG "alarmreceiver.conf"
00071 #define UNKNOWN_FORMAT "UNKNOWN_FORMAT"
00072 
00073 #define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
00074 /*
00075    AAAA _ID_ P CCC XX ZZZ S
00076 
00077 where AAAA is the account number, _ID_ is 18 or 98, P is the pin status (alarm or restore), CCC
00078 is the alarm code which is pre-defined by Ademco (but you may be able to reprogram it in the panel), XX
00079 is the dialer group, partition or area number, ZZZ is the zone or user number and S is the checksum
00080 */
00081 
00082 #define ADEMCO_EXPRESS_4_1 "ADEMCO_EXPRESS_4_1"
00083 /*
00084    AAAA _ID_ C S
00085 
00086 where AAAA is the account number, _ID_ is 17, C is the alarm code and S is the checksum.
00087 */
00088 
00089 #define ADEMCO_EXPRESS_4_2 "ADEMCO_EXPRESS_4_2"
00090 /*
00091    AAAA _ID_ C Z S
00092 
00093 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.
00094 */
00095 
00096 #define ADEMCO_HIGH_SPEED "ADEMCO_HIGH_SPEED"
00097 /*
00098    AAAA _ID_ PPPP PPPP X S
00099 
00100 where AAAA is the account number, _ID_ is 55, PPPP PPPP is the status of each zone, X
00101 is a special digit which describes the type of information in the PPPP PPPP fields and S is checksum.
00102 Each P field contains one of the following values:
00103         1  new alarm           3  new restore           5  normal
00104         2  new opening         4  new closing           6  outstanding
00105 The X field contains one of the following values:
00106         0  AlarmNet messages
00107         1  ambush or duress
00108         2  opening by user (the first P field contains the user number)
00109         3  bypass (the P fields indicate which zones are bypassed)
00110         4  closing by user (the first P field contain the user number)
00111         5  trouble (the P fields contain which zones are in trouble)
00112         6  system trouble
00113         7  normal message (the P fields indicate zone status)
00114         8  low battery (the P fields indicate zone status)
00115         9  test (the P fields indicate zone status)
00116 */
00117 #define ADEMCO_SUPER_FAST "ADEMCO_SUPER_FAST"
00118 /*
00119    AAAA _ID_ PPPP PPPP X
00120 where AAA is the account number, _ID_ is 56
00121 */
00122 
00123 #define ADEMCO_MSG_TYPE_1 "18"
00124 #define ADEMCO_MSG_TYPE_2 "98"
00125 #define ADEMCO_MSG_TYPE_3 "17"
00126 #define ADEMCO_MSG_TYPE_4 "27"
00127 #define ADEMCO_MSG_TYPE_5 "55"
00128 #define ADEMCO_MSG_TYPE_6 "56"
00129 
00130 #define ADEMCO_AUDIO_CALL_NEXT "606"
00131 
00132 struct {
00133   char digit;
00134   char weight;
00135 } digits_mapping[] = { {'0', 10}, {'1', 1} , {'2', 2}, {'3', 3}, {'4', 4}, {'5', 5},
00136    {'6', 6}, {'7', 7}, {'8', 8}, {'9', 9}, {'*', 11}, {'#', 12},
00137    {'A', 13}, {'B', 14}, {'C', 15} };
00138 
00139 struct event_node{
00140    char data[17];
00141    struct event_node *next;
00142 };
00143 
00144 typedef struct event_node event_node_t;
00145 
00146 struct timeval call_start_time;
00147 
00148 static const char app[] = "AlarmReceiver";
00149 /*** DOCUMENTATION
00150    <application name="AlarmReceiver" language="en_US">
00151       <synopsis>
00152          Provide support for receiving alarm reports from a burglar or fire alarm panel.
00153       </synopsis>
00154       <syntax />
00155       <description>
00156          <para>This application should be called whenever there is an alarm panel calling in to dump its events.
00157          The application will handshake with the alarm panel, and receive events, validate them, handshake them,
00158          and store them until the panel hangs up. Once the panel hangs up, the application will run the system
00159          command specified by the eventcmd setting in <filename>alarmreceiver.conf</filename> and pipe the
00160          events to the standard input of the application.
00161          The configuration file also contains settings for DTMF timing, and for the loudness of the
00162          acknowledgement tones.</para>
00163          <note><para>Few Ademco DTMF signalling formats are detected automaticaly: Contact ID, Express 4+1,
00164          Express 4+2, High Speed and Super Fast.</para></note>
00165          <para>The application is affected by the following variables:</para>
00166          <variablelist>
00167             <variable name="ALARMRECEIVER_CALL_LIMIT">
00168                <para>Maximum call time, in milliseconds.</para>
00169                <para>If set, this variable causes application to exit after the specified time.</para>
00170             </variable>
00171             <variable name="ALARMRECEIVER_RETRIES_LIMIT">
00172                <para>Maximum number of retries per call.</para>
00173                <para>If set, this variable causes application to exit after the specified number of messages.</para>
00174             </variable>
00175          </variablelist>
00176       </description>
00177       <see-also>
00178          <ref type="filename">alarmreceiver.conf</ref>
00179       </see-also>
00180    </application>
00181  ***/
00182 
00183 /* Config Variables */
00184 static int fdtimeout = 2000;
00185 static int sdtimeout = 200;
00186 static int answait = 1250;
00187 static int toneloudness = 4096;
00188 static int log_individual_events = 0;
00189 static int no_group_meta = 0;
00190 static char event_spool_dir[128] = {'\0'};
00191 static char event_app[128] = {'\0'};
00192 static char db_family[128] = {'\0'};
00193 static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
00194 
00195 /* Misc variables */
00196 static char event_file[14] = "/event-XXXXXX";
00197 
00198 /*!
00199  * \brief Attempt to access a database variable and increment it
00200  *
00201  * \note Only if the user defined db-family in alarmreceiver.conf
00202  *
00203  * The alarmreceiver app will write statistics to a few variables
00204  * in this family if it is defined. If the new key doesn't exist in the
00205  * family, then create it and set its value to 1.
00206  *
00207  * \param key A database key to increment
00208  * \return Nothing
00209  */
00210 static void database_increment(char *key)
00211 {
00212    unsigned v;
00213    char value[16];
00214 
00215    if (ast_strlen_zero(db_family)) {
00216       return;  /* If not defined, don't do anything */
00217    }
00218 
00219    if (ast_db_get(db_family, key, value, sizeof(value) - 1)) {
00220       ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
00221       /* Guess we have to create it */
00222       ast_db_put(db_family, key, "1");
00223       return;
00224    }
00225 
00226    sscanf(value, "%30u", &v);
00227    v++;
00228 
00229    ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v);
00230    snprintf(value, sizeof(value), "%u", v);
00231 
00232    if (ast_db_put(db_family, key, value)) {
00233       ast_verb(4, "AlarmReceiver: database_increment write error\n");
00234    }
00235 
00236    return;
00237 }
00238 
00239 /*!
00240  * \brief Receive a fixed length DTMF string.
00241  *
00242  * \note Doesn't give preferential treatment to any digit,
00243  * \note allow different timeout values for the first and all subsequent digits
00244  *
00245  * \param chan Asterisk Channel
00246  * \param digit_string Digits String
00247  * \param buf_size The size of the Digits String buffer
00248  * \param expected Digits expected for this message type
00249  * \param received Pointer to number of digits received so far
00250  *
00251  * \retval 0 if all digits were successfully received
00252  * \retval 1 if a timeout occurred
00253  * \retval -1 if the caller hung up or on channel errors
00254  */
00255 static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int buf_size, int expected, int *received)
00256 {
00257    int rtn = 0;
00258    int r;
00259    struct ast_frame *f;
00260    struct timeval lastdigittime;
00261 
00262    lastdigittime = ast_tvnow();
00263    while (*received < expected && *received < buf_size - 1) {
00264       /* If timed out, leave */
00265       if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((*received > 0) ? sdtimeout : fdtimeout)) {
00266          ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", ast_channel_name(chan));
00267          ast_debug(1, "AlarmReceiver: DTMF timeout on chan %s\n", ast_channel_name(chan));
00268          rtn = 1;
00269          break;
00270       }
00271 
00272       if ((r = ast_waitfor(chan, -1)) < 0) {
00273          ast_debug(1, "Waitfor returned %d\n", r);
00274          continue;
00275       }
00276 
00277       if ((f = ast_read(chan)) == NULL) {
00278          rtn = -1;
00279          break;
00280       }
00281 
00282       /* If they hung up, leave */
00283       if ((f->frametype == AST_FRAME_CONTROL)
00284          && (f->subclass.integer == AST_CONTROL_HANGUP)) {
00285          if (f->data.uint32) {
00286             ast_channel_hangupcause_set(chan, f->data.uint32);
00287          }
00288          ast_frfree(f);
00289          rtn = -1;
00290          break;
00291       }
00292 
00293       /* If not DTMF, just do it again */
00294       if (f->frametype != AST_FRAME_DTMF) {
00295          ast_frfree(f);
00296          continue;
00297       }
00298 
00299       /* Save digit */
00300       digit_string[(*received)++] = f->subclass.integer;
00301       ast_frfree(f);
00302 
00303       lastdigittime = ast_tvnow();
00304    }
00305 
00306    /* Null terminate the end of the digit_string */
00307    digit_string[*received] = '\0';
00308 
00309    return rtn;
00310 }
00311 
00312 /*!
00313  * \brief Write metadata to log file
00314  *
00315  * \param logfile Log File Pointer
00316  * \param signalling_type Signaling Type
00317  * \param chan Asterisk Channel
00318  * \param no_checksum Expecting messages without checksum
00319  *
00320  * \retval 0 success
00321  * \retval -1 failure
00322  */
00323 static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum)
00324 {
00325    struct timeval t;
00326    struct ast_tm now;
00327    char *cl;
00328    char *cn;
00329    char workstring[80];
00330    char timestamp[80];
00331 
00332    /* Extract the caller ID location */
00333    ast_copy_string(workstring,
00334       S_COR(ast_channel_caller(chan)->id.number.valid,
00335       ast_channel_caller(chan)->id.number.str, ""), sizeof(workstring));
00336    ast_shrink_phone_number(workstring);
00337    if (ast_strlen_zero(workstring)) {
00338       cl = "<unknown>";
00339    } else {
00340       cl = workstring;
00341    }
00342    cn = S_COR(ast_channel_caller(chan)->id.name.valid,
00343       ast_channel_caller(chan)->id.name.str, "<unknown>");
00344 
00345    /* Get the current time */
00346    t = ast_tvnow();
00347    ast_localtime(&t, &now, NULL);
00348 
00349    /* Format the time */
00350    ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
00351 
00352    if (no_group_meta && fprintf(logfile, "PROTOCOL=%s\n"
00353          "CHECKSUM=%s\n"
00354          "CALLINGFROM=%s\n"
00355          "CALLERNAME=%s\n"
00356          "TIMESTAMP=%s\n\n",
00357          signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
00358       return 0;
00359    } else if (fprintf(logfile, "\n\n[metadata]\n\n"
00360          "PROTOCOL=%s\n"
00361          "CHECKSUM=%s\n"
00362          "CALLINGFROM=%s\n"
00363          "CALLERNAME=%s\n"
00364          "TIMESTAMP=%s\n\n"
00365          "[events]\n\n",
00366          signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
00367       return 0;
00368    }
00369 
00370    ast_verb(3, "AlarmReceiver: can't write metadata\n");
00371    ast_debug(1, "AlarmReceiver: can't write metadata\n");
00372    return -1;
00373 }
00374 
00375 /*!
00376  * \brief Log a single event
00377  *
00378  * \param logfile Log File Pointer
00379  * \param event Event Structure
00380  *
00381  * \retval 0 success
00382  * \retval -1 failure
00383  */
00384 static int write_event(FILE *logfile, event_node_t *event)
00385 {
00386    if (fprintf(logfile, "%s%s\n", no_group_meta ? "event=" : "", event->data) < 0) {
00387       return -1;
00388    }
00389 
00390    return 0;
00391 }
00392 
00393 /*!
00394  * \brief Log events if configuration key logindividualevents is enabled or on exit
00395  *
00396  * \param chan Asterisk Channel
00397  * \param signalling_type Signaling Type
00398  * \param event Event Structure
00399  * \param no_checksum Expecting messages without checksum
00400  *
00401  * \retval 0 success
00402  * \retval -1 failure
00403  */
00404 static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event, int no_checksum)
00405 {
00406    char workstring[sizeof(event_spool_dir) + sizeof(event_file)] = "";
00407    int fd;
00408    FILE *logfile;
00409    event_node_t *elp = event;
00410 
00411    if (!ast_strlen_zero(event_spool_dir)) {
00412 
00413       /* Make a template */
00414       ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
00415       strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
00416 
00417       /* Make the temporary file */
00418       fd = mkstemp(workstring);
00419 
00420       if (fd == -1) {
00421          ast_verb(3, "AlarmReceiver: can't make temporary file\n");
00422          ast_debug(1, "AlarmReceiver: can't make temporary file\n");
00423          return -1;
00424       }
00425 
00426       if ((logfile = fdopen(fd, "w")) == NULL) {
00427          return -1;
00428       }
00429 
00430       /* Write the file */
00431       if (write_metadata(logfile, signalling_type, chan, no_checksum)) {
00432          fflush(logfile);
00433          fclose(logfile);
00434          return -1;
00435       }
00436 
00437       while ((elp != NULL) && (write_event(logfile, elp) == 0)) {
00438          elp = elp->next;
00439       }
00440 
00441       fflush(logfile);
00442       fclose(logfile);
00443    }
00444 
00445    return 0;
00446 }
00447 
00448 /*!
00449  * \brief Verify Ademco checksum
00450  * \since 11.0
00451  *
00452  * \param event Received DTMF String
00453  * \param expected Number of Digits expected
00454  *
00455  * \retval 0 success
00456  * \retval -1 failure
00457  */
00458 static int ademco_verify_checksum(char *event, int expected)
00459 {
00460    int checksum = 0;
00461    int i, j;
00462 
00463    for (j = 0; j < expected; j++) {
00464       for (i = 0; i < ARRAY_LEN(digits_mapping); i++) {
00465          if (digits_mapping[i].digit == event[j]) {
00466             break;
00467          }
00468       }
00469 
00470       if (i >= ARRAY_LEN(digits_mapping)) {
00471          ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
00472          return -1;
00473       }
00474 
00475       checksum += digits_mapping[i].weight;
00476    }
00477 
00478    /* Checksum is mod(15) of the total */
00479    if (!(checksum % 15)) {
00480       return 0;
00481    }
00482 
00483    return -1;
00484 }
00485 
00486 /*!
00487  * \brief Send a single tone burst for a specified duration and frequency.
00488  * \since 11.0
00489  *
00490  * \param chan Asterisk Channel
00491  * \param tone_freq Frequency of the tone to send
00492  * \param tone_duration Tone duration in ms
00493  * \param delay Delay before sending the tone
00494  *
00495  * \retval 0 success
00496  * \retval -1 failure
00497  */
00498 static int send_tone_burst(struct ast_channel *chan, const char *tone_freq, int tone_duration, int delay)
00499 {
00500    if (delay && ast_safe_sleep(chan, delay)) {
00501       return -1;
00502    }
00503 
00504    if (ast_playtones_start(chan, toneloudness, tone_freq, 0)) {
00505       return -1;
00506    }
00507 
00508    if (ast_safe_sleep(chan, tone_duration)) {
00509       return -1;
00510    }
00511 
00512    ast_playtones_stop(chan);
00513    return 0;
00514 }
00515 
00516 /*!
00517  * \brief Check if the message is in known and valid Ademco format
00518  *
00519  * \param signalling_type Expected signalling type for the message
00520  * \param event event received
00521  *
00522  * \retval 0 The event is valid
00523  * \retval -1 The event is not valid
00524  */
00525 static int ademco_check_valid(char *signalling_type, char *event)
00526 {
00527    if (!strcmp(signalling_type, UNKNOWN_FORMAT)) {
00528       return 1;
00529    }
00530 
00531    if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
00532       && strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
00533       && strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
00534       return -1;
00535    }
00536 
00537    if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_1) && strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
00538       return -1;
00539    }
00540 
00541    if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2) && strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
00542       return -1;
00543    }
00544 
00545    if (!strcmp(signalling_type, ADEMCO_HIGH_SPEED) && strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
00546       return -1;
00547    }
00548 
00549    if (!strcmp(signalling_type, ADEMCO_SUPER_FAST) && strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
00550       return -1;
00551    }
00552 
00553    return 0;
00554 }
00555 
00556 /*!
00557  * \brief Detect the message format of an event
00558  *
00559  * \param signalling_type Expected signalling type for the message
00560  * \param event event received
00561  * \param no_checksum Should we calculate checksum for the message
00562  *
00563  * \returns The expected digits for the detected event type
00564  */
00565 static int ademco_detect_format(char *signalling_type, char *event, int *no_checksum)
00566 {
00567    int res = 16;
00568 
00569    if (!strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
00570       || !strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
00571       sprintf(signalling_type, "%s", ADEMCO_CONTACT_ID);
00572    }
00573 
00574    if (!strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
00575       sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_1);
00576       res = 8;
00577    }
00578 
00579    if (!strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
00580       sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_2);
00581       res = 9;
00582    }
00583 
00584    if (!strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
00585       sprintf(signalling_type, "%s", ADEMCO_HIGH_SPEED);
00586    }
00587 
00588    if (!strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
00589       sprintf(signalling_type, "%s", ADEMCO_SUPER_FAST);
00590       *no_checksum = 1;
00591       res = 15;
00592    }
00593 
00594    if (strcmp(signalling_type, UNKNOWN_FORMAT)) {
00595       ast_verb(4, "AlarmMonitoring: Detected format %s.\n", signalling_type);
00596       ast_debug(1, "AlarmMonitoring: Autodetected format %s.\n", signalling_type);
00597    }
00598 
00599    return res;
00600 }
00601 
00602 /*!
00603  * \brief Receive Ademco ContactID or other format Data String
00604  *
00605  * \param chan Asterisk Channel
00606  * \param ehead Pointer to events list
00607  * \param signalling_type Expected signalling type for the message
00608  * \param no_checksum Should we calculate checksum for the message
00609  *
00610  * \retval 0 success
00611  * \retval -1 failure
00612  */
00613 static int receive_ademco_event(struct ast_channel *chan, event_node_t **ehead, char *signalling_type, int *no_checksum)
00614 {
00615    int res = 0;
00616    const char *limit;
00617    char event[17];
00618    event_node_t *enew, *elp;
00619    int got_some_digits = 0;
00620    int events_received = 0;
00621    int ack_retries = 0;
00622    int limit_retries = 0;
00623    int expected_length = sizeof(event) - 1;
00624 
00625    database_increment("calls-received");
00626 
00627    /* Wait for first event */
00628    ast_verb(4, "AlarmReceiver: Waiting for first event from panel...\n");
00629 
00630    while (res >= 0) {
00631       int digits_received = 0;
00632 
00633       res = 0;
00634 
00635       if (log_individual_events) {
00636          sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
00637          expected_length = 16;
00638          *no_checksum = 0;
00639       }
00640 
00641       if (got_some_digits == 0) {
00642          /* Send ACK tone sequence */
00643          ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
00644          res = send_tone_burst(chan, "1400", 100, 0);
00645          if (!res) {
00646             ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
00647             res = send_tone_burst(chan, "2300", 100, 100);
00648          }
00649       }
00650       if (res) {
00651          return -1;
00652       }
00653 
00654       res = receive_dtmf_digits(chan, event, sizeof(event), expected_length, &digits_received);
00655       if (res < 0) {
00656          if (events_received == 0) {
00657             /* Hangup with no events received should be logged in the DB */
00658             database_increment("no-events-received");
00659             ast_verb(4, "AlarmReceiver: No events received!\n");
00660          } else {
00661             if (ack_retries) {
00662                database_increment("ack-retries");
00663                ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
00664             }
00665          }
00666          ast_verb(4, "AlarmReceiver: App exiting...\n");
00667          break;
00668       }
00669 
00670       if (!strcmp(signalling_type, UNKNOWN_FORMAT) && digits_received > 5) {
00671          expected_length = ademco_detect_format(signalling_type, event, no_checksum);
00672 
00673          if (res > 0) {
00674             if (digits_received == expected_length) {
00675                res = limit_retries = 0;
00676             } else if (digits_received == expected_length - 1
00677                && (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2)
00678                || !strcmp(signalling_type, ADEMCO_EXPRESS_4_1))) {
00679                /* ADEMCO EXPRESS without checksum */
00680                res = limit_retries = 0;
00681                expected_length--;
00682                *no_checksum = 1;
00683                ast_verb(4, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
00684                ast_debug(1, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
00685             }
00686          }
00687       }
00688 
00689       ast_channel_lock(chan);
00690       limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_CALL_LIMIT");
00691       if (!ast_strlen_zero(limit)) {
00692          if (ast_tvdiff_ms(ast_tvnow(), call_start_time) > atoi(limit)) {
00693             ast_channel_unlock(chan);
00694             return -1;
00695          }
00696       }
00697       limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_RETRIES_LIMIT");
00698       ast_channel_unlock(chan);
00699       if (!ast_strlen_zero(limit)) {
00700          if (limit_retries + 1 >= atoi(limit)) {
00701             return -1;
00702          }
00703       }
00704 
00705       if (res) {
00706          /* Didn't get all of the digits */
00707          ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
00708          limit_retries++;
00709 
00710          if (!events_received && strcmp(signalling_type, UNKNOWN_FORMAT))
00711          {
00712             sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
00713             expected_length = sizeof(event) - 1;
00714          }
00715 
00716          if (!got_some_digits) {
00717             got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
00718             ack_retries++;
00719          }
00720          continue;
00721       }
00722 
00723       got_some_digits = 1;
00724 
00725       ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
00726       ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
00727 
00728       /* Calculate checksum */
00729       if (!(*no_checksum) && ademco_verify_checksum(event, expected_length)) {
00730          database_increment("checksum-errors");
00731          ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
00732          ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
00733          continue;
00734       }
00735 
00736       /* Check the message type for correctness */
00737       if (ademco_check_valid(signalling_type, event)) {
00738          database_increment("format-errors");
00739          ast_verb(2, "AlarmReceiver: Wrong message type\n");
00740          ast_debug(1, "AlarmReceiver: Wrong message type\n");
00741          continue;
00742       }
00743 
00744       events_received++;
00745 
00746       /* Queue the Event */
00747       if (!(enew = ast_calloc(1, sizeof(*enew)))) {
00748          return -1;
00749       }
00750 
00751       enew->next = NULL;
00752       ast_copy_string(enew->data, event, sizeof(enew->data));
00753 
00754       /* Insert event onto end of list */
00755       if (*ehead == NULL) {
00756          *ehead = enew;
00757       } else {
00758          for (elp = *ehead; elp->next != NULL; elp = elp->next) {
00759             ;
00760          }
00761          elp->next = enew;
00762       }
00763 
00764       /* Let the user have the option of logging the single event before sending the kissoff tone */
00765       if (log_individual_events && log_events(chan, signalling_type, enew, *no_checksum)) {
00766          return -1;
00767       }
00768 
00769       /* Send the kissoff tone (1400 Hz, 900 ms, after 200ms delay) */
00770       if (send_tone_burst(chan, "1400", 900, 200)) {
00771          return -1;
00772       }
00773 
00774       /* If audio call follows, exit alarm receiver app */
00775       if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
00776          && !strncmp(event + 7, ADEMCO_AUDIO_CALL_NEXT, 3)) {
00777          ast_verb(4, "AlarmReceiver: App exiting... Audio call next!\n");
00778          return 0;
00779       }
00780    }
00781 
00782    return res;
00783 }
00784 
00785 /*!
00786  * \brief This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
00787  *
00788  * \param chan Asterisk Channel
00789  * \param data Application data
00790  *
00791  * \retval 0 success
00792  * \retval -1 failure
00793  */
00794 static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
00795 {
00796    int res = 0;
00797    int no_checksum = 0;
00798    event_node_t *elp, *efree;
00799    char signalling_type[64] = "";
00800    event_node_t *event_head = NULL;
00801 
00802    if ((ast_format_cmp(ast_channel_writeformat(chan), ast_format_ulaw) == AST_FORMAT_CMP_NOT_EQUAL) &&
00803       (ast_format_cmp(ast_channel_writeformat(chan), ast_format_alaw) == AST_FORMAT_CMP_NOT_EQUAL)) {
00804       ast_verb(4, "AlarmReceiver: Setting write format to Mu-law\n");
00805       if (ast_set_write_format(chan, ast_format_ulaw)) {
00806          ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",ast_channel_name(chan));
00807          return -1;
00808       }
00809    }
00810 
00811    if ((ast_format_cmp(ast_channel_readformat(chan), ast_format_ulaw) == AST_FORMAT_CMP_NOT_EQUAL) &&
00812       (ast_format_cmp(ast_channel_readformat(chan), ast_format_alaw) == AST_FORMAT_CMP_NOT_EQUAL)) {
00813       ast_verb(4, "AlarmReceiver: Setting read format to Mu-law\n");
00814       if (ast_set_read_format(chan, ast_format_ulaw)) {
00815          ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",ast_channel_name(chan));
00816          return -1;
00817       }
00818    }
00819 
00820    /* Set default values for this invocation of the application */
00821    ast_copy_string(signalling_type, UNKNOWN_FORMAT, sizeof(signalling_type));
00822    call_start_time = ast_tvnow();
00823 
00824    /* Answer the channel if it is not already */
00825    if (ast_channel_state(chan) != AST_STATE_UP) {
00826       ast_verb(4, "AlarmReceiver: Answering channel\n");
00827       if (ast_answer(chan)) {
00828          return -1;
00829       }
00830    }
00831 
00832    /* Wait for the connection to settle post-answer */
00833    ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
00834    if (ast_safe_sleep(chan, answait)) {
00835       return -1;
00836    }
00837 
00838    /* Attempt to receive the events */
00839    receive_ademco_event(chan, &event_head, signalling_type, &no_checksum);
00840 
00841    /* Events queued by receiver, write them all out here if so configured */
00842    if (!log_individual_events) {
00843       res = log_events(chan, signalling_type, event_head, no_checksum);
00844    }
00845 
00846    /* Do we exec a command line at the end? */
00847    if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
00848       ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
00849       ast_safe_system(event_app);
00850    }
00851 
00852    /* Free up the data allocated in our linked list */
00853    for (elp = event_head; (elp != NULL);) {
00854       efree = elp;
00855       elp = elp->next;
00856       ast_free(efree);
00857    }
00858 
00859    return 0;
00860 }
00861 
00862 /*!
00863  * \brief Load the configuration from the configuration file
00864  *
00865  * \param reload True on reload
00866  *
00867  * \retval 1 success
00868  * \retval 0 failure
00869  */
00870 static int load_config(int reload)
00871 {
00872    struct ast_config *cfg;
00873    const char *value;
00874    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00875 
00876    /* Read in the config file */
00877    cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
00878 
00879    if (!cfg) {
00880       ast_verb(4, "AlarmReceiver: No config file\n");
00881       return 0;
00882    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00883       return 1;
00884    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00885       ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n",
00886          ALMRCV_CONFIG);
00887       return 0;
00888    }
00889 
00890    if ((value = ast_variable_retrieve(cfg, "general", "eventcmd")) != NULL) {
00891       ast_copy_string(event_app, value, sizeof(event_app));
00892    }
00893 
00894    if ((value = ast_variable_retrieve(cfg, "general", "loudness")) != NULL) {
00895       toneloudness = atoi(value);
00896       if (toneloudness < 100) {
00897          toneloudness = 100;
00898       } else if (toneloudness > 8192) {
00899          toneloudness = 8192;
00900       }
00901    }
00902 
00903    if ((value = ast_variable_retrieve(cfg, "general", "fdtimeout")) != NULL) {
00904       fdtimeout = atoi(value);
00905       if (fdtimeout < 1000) {
00906          fdtimeout = 1000;
00907       } else if (fdtimeout > 10000) {
00908          fdtimeout = 10000;
00909       }
00910    }
00911 
00912    if ((value = ast_variable_retrieve(cfg, "general", "sdtimeout")) != NULL) {
00913       sdtimeout = atoi(value);
00914       if (sdtimeout < 110) {
00915          sdtimeout = 110;
00916       } else if (sdtimeout > 4000) {
00917          sdtimeout = 4000;
00918       }
00919    }
00920 
00921    if ((value = ast_variable_retrieve(cfg, "general", "answait")) != NULL) {
00922       answait = atoi(value);
00923       if (answait < 500) {
00924          answait = 500;
00925       } else if (answait > 10000) {
00926          answait = 10000;
00927       }
00928    }
00929 
00930    if ((value = ast_variable_retrieve(cfg, "general", "no_group_meta")) != NULL) {
00931       no_group_meta = ast_true(value);
00932    }
00933 
00934    if ((value = ast_variable_retrieve(cfg, "general", "logindividualevents")) != NULL) {
00935       log_individual_events = ast_true(value);
00936    }
00937 
00938    if ((value = ast_variable_retrieve(cfg, "general", "eventspooldir")) != NULL) {
00939       ast_copy_string(event_spool_dir, value, sizeof(event_spool_dir));
00940    }
00941 
00942    if ((value = ast_variable_retrieve(cfg, "general", "timestampformat")) != NULL) {
00943       ast_copy_string(time_stamp_format, value, sizeof(time_stamp_format));
00944    }
00945 
00946    if ((value = ast_variable_retrieve(cfg, "general", "db-family")) != NULL) {
00947       ast_copy_string(db_family, value, sizeof(db_family));
00948    }
00949 
00950    ast_config_destroy(cfg);
00951 
00952    return 1;
00953 }
00954 
00955 /*!
00956  * \brief Unregister Alarm Receiver App
00957  *
00958  * \retval 0 success
00959  * \retval -1 failure
00960  */
00961 static int unload_module(void)
00962 {
00963    return ast_unregister_application(app);
00964 }
00965 
00966 /*!
00967  * \brief Load the module
00968  *
00969  * Module loading including tests for configuration or dependencies.
00970  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
00971  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
00972  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
00973  * configuration file or other non-critical problem return
00974  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
00975  */
00976 static int load_module(void)
00977 {
00978    if (load_config(0)) {
00979       if (ast_register_application_xml(app, alarmreceiver_exec)) {
00980          return AST_MODULE_LOAD_FAILURE;
00981       }
00982       return AST_MODULE_LOAD_SUCCESS;
00983    }
00984 
00985    return AST_MODULE_LOAD_DECLINE;
00986 }
00987 
00988 static int reload(void)
00989 {
00990    if (load_config(1)) {
00991       return AST_MODULE_LOAD_SUCCESS;
00992    }
00993 
00994    return AST_MODULE_LOAD_DECLINE;
00995 }
00996 
00997 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Alarm Receiver for Asterisk",
00998       .support_level = AST_MODULE_SUPPORT_EXTENDED,
00999       .load = load_module,
01000       .unload = unload_module,
01001       .reload = reload,
01002 );

Generated on Thu Apr 16 06:27:08 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6