Asterisk - The Open Source Telephony Project  21.4.1
app_alarmreceiver.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2004 - 2005 Steve Rodgers
5  *
6  * Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Central Station Alarm receiver for Ademco Contact ID
22  * \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
23  *
24  * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
25  *
26  * Use at your own risk. Please consult the GNU GPL license document included with Asterisk. *
27  *
28  * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
29  *
30  * \ingroup applications
31  */
32 
33 /*! \li \ref app_alarmreceiver.c uses the configuration file \ref alarmreceiver.conf
34  * \addtogroup configuration_file Configuration Files
35  */
36 
37 /*!
38  * \page alarmreceiver.conf alarmreceiver.conf
39  * \verbinclude alarmreceiver.conf.sample
40  */
41 
42 /*** MODULEINFO
43  <support_level>extended</support_level>
44  ***/
45 
46 #include "asterisk.h"
47 
48 #include <math.h>
49 #include <sys/wait.h>
50 #include <sys/time.h>
51 
52 #include "asterisk/lock.h"
53 #include "asterisk/file.h"
54 #include "asterisk/channel.h"
55 #include "asterisk/pbx.h"
56 #include "asterisk/module.h"
57 #include "asterisk/translate.h"
58 #include "asterisk/app.h"
59 #include "asterisk/dsp.h"
60 #include "asterisk/config.h"
61 #include "asterisk/localtime.h"
62 #include "asterisk/callerid.h"
63 #include "asterisk/astdb.h"
64 #include "asterisk/utils.h"
65 #include "asterisk/indications.h"
66 #include "asterisk/format_cache.h"
67 
68 #define ALMRCV_CONFIG "alarmreceiver.conf"
69 #define UNKNOWN_FORMAT "UNKNOWN_FORMAT"
70 
71 #define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
72 /*
73  AAAA _ID_ P CCC XX ZZZ S
74 
75 where AAAA is the account number, _ID_ is 18 or 98, P is the pin status (alarm or restore), CCC
76 is the alarm code which is pre-defined by Ademco (but you may be able to reprogram it in the panel), XX
77 is the dialer group, partition or area number, ZZZ is the zone or user number and S is the checksum
78 */
79 
80 #define ADEMCO_EXPRESS_4_1 "ADEMCO_EXPRESS_4_1"
81 /*
82  AAAA _ID_ C S
83 
84 where AAAA is the account number, _ID_ is 17, C is the alarm code and S is the checksum.
85 */
86 
87 #define ADEMCO_EXPRESS_4_2 "ADEMCO_EXPRESS_4_2"
88 /*
89  AAAA _ID_ C Z S
90 
91 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.
92 */
93 
94 #define ADEMCO_HIGH_SPEED "ADEMCO_HIGH_SPEED"
95 /*
96  AAAA _ID_ PPPP PPPP X S
97 
98 where AAAA is the account number, _ID_ is 55, PPPP PPPP is the status of each zone, X
99 is a special digit which describes the type of information in the PPPP PPPP fields and S is checksum.
100 Each P field contains one of the following values:
101  1 new alarm 3 new restore 5 normal
102  2 new opening 4 new closing 6 outstanding
103 The X field contains one of the following values:
104  0 AlarmNet messages
105  1 ambush or duress
106  2 opening by user (the first P field contains the user number)
107  3 bypass (the P fields indicate which zones are bypassed)
108  4 closing by user (the first P field contain the user number)
109  5 trouble (the P fields contain which zones are in trouble)
110  6 system trouble
111  7 normal message (the P fields indicate zone status)
112  8 low battery (the P fields indicate zone status)
113  9 test (the P fields indicate zone status)
114 */
115 #define ADEMCO_SUPER_FAST "ADEMCO_SUPER_FAST"
116 /*
117  AAAA _ID_ PPPP PPPP X
118 where AAA is the account number, _ID_ is 56
119 */
120 
121 #define ADEMCO_MSG_TYPE_1 "18"
122 #define ADEMCO_MSG_TYPE_2 "98"
123 #define ADEMCO_MSG_TYPE_3 "17"
124 #define ADEMCO_MSG_TYPE_4 "27"
125 #define ADEMCO_MSG_TYPE_5 "55"
126 #define ADEMCO_MSG_TYPE_6 "56"
127 
128 #define ADEMCO_AUDIO_CALL_NEXT "606"
129 
130 struct {
131  char digit;
132  char weight;
133 } digits_mapping[] = { {'0', 10}, {'1', 1} , {'2', 2}, {'3', 3}, {'4', 4}, {'5', 5},
134  {'6', 6}, {'7', 7}, {'8', 8}, {'9', 9}, {'*', 11}, {'#', 12},
135  {'A', 13}, {'B', 14}, {'C', 15} };
136 
137 struct event_node{
138  char data[17];
139  struct event_node *next;
140 };
141 
142 typedef struct event_node event_node_t;
143 
144 struct timeval call_start_time;
145 
146 static const char app[] = "AlarmReceiver";
147 /*** DOCUMENTATION
148  <application name="AlarmReceiver" language="en_US">
149  <synopsis>
150  Provide support for receiving alarm reports from a burglar or fire alarm panel.
151  </synopsis>
152  <syntax />
153  <description>
154  <para>This application should be called whenever there is an alarm panel calling in to dump its events.
155  The application will handshake with the alarm panel, and receive events, validate them, handshake them,
156  and store them until the panel hangs up. Once the panel hangs up, the application will run the system
157  command specified by the eventcmd setting in <filename>alarmreceiver.conf</filename> and pipe the
158  events to the standard input of the application.
159  The configuration file also contains settings for DTMF timing, and for the loudness of the
160  acknowledgement tones.</para>
161  <note><para>Few Ademco DTMF signalling formats are detected automatically: Contact ID, Express 4+1,
162  Express 4+2, High Speed and Super Fast.</para></note>
163  <para>The application is affected by the following variables:</para>
164  <variablelist>
165  <variable name="ALARMRECEIVER_CALL_LIMIT">
166  <para>Maximum call time, in milliseconds.</para>
167  <para>If set, this variable causes application to exit after the specified time.</para>
168  </variable>
169  <variable name="ALARMRECEIVER_RETRIES_LIMIT">
170  <para>Maximum number of retries per call.</para>
171  <para>If set, this variable causes application to exit after the specified number of messages.</para>
172  </variable>
173  </variablelist>
174  </description>
175  <see-also>
176  <ref type="filename">alarmreceiver.conf</ref>
177  </see-also>
178  </application>
179  ***/
180 
181 /* Config Variables */
182 static int fdtimeout = 2000;
183 static int sdtimeout = 200;
184 static int answait = 1250;
185 static int toneloudness = 4096;
186 static int log_individual_events = 0;
187 static int no_group_meta = 0;
188 static char event_spool_dir[128] = {'\0'};
189 static char event_app[128] = {'\0'};
190 static char db_family[128] = {'\0'};
191 static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
192 
193 /* Misc variables */
194 static char event_file[14] = "/event-XXXXXX";
195 
196 /*!
197  * \brief Attempt to access a database variable and increment it
198  *
199  * \note Only if the user defined db-family in alarmreceiver.conf
200  *
201  * The alarmreceiver app will write statistics to a few variables
202  * in this family if it is defined. If the new key doesn't exist in the
203  * family, then create it and set its value to 1.
204  *
205  * \param key A database key to increment
206  */
207 static void database_increment(char *key)
208 {
209  unsigned v;
210  char value[16];
211 
212  if (ast_strlen_zero(db_family)) {
213  return; /* If not defined, don't do anything */
214  }
215 
216  if (ast_db_get(db_family, key, value, sizeof(value) - 1)) {
217  ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
218  /* Guess we have to create it */
219  ast_db_put(db_family, key, "1");
220  return;
221  }
222 
223  sscanf(value, "%30u", &v);
224  v++;
225 
226  ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v);
227  snprintf(value, sizeof(value), "%u", v);
228 
229  if (ast_db_put(db_family, key, value)) {
230  ast_verb(4, "AlarmReceiver: database_increment write error\n");
231  }
232 
233  return;
234 }
235 
236 /*!
237  * \brief Receive a fixed length DTMF string.
238  *
239  * \note Doesn't give preferential treatment to any digit,
240  * \note allow different timeout values for the first and all subsequent digits
241  *
242  * \param chan Asterisk Channel
243  * \param digit_string Digits String
244  * \param buf_size The size of the Digits String buffer
245  * \param expected Digits expected for this message type
246  * \param received Pointer to number of digits received so far
247  *
248  * \retval 0 if all digits were successfully received
249  * \retval 1 if a timeout occurred
250  * \retval -1 if the caller hung up or on channel errors
251  */
252 static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int buf_size, int expected, int *received)
253 {
254  int rtn = 0;
255  int r;
256  struct ast_frame *f;
257  struct timeval lastdigittime;
258 
259  lastdigittime = ast_tvnow();
260  while (*received < expected && *received < buf_size - 1) {
261  /* If timed out, leave */
262  if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((*received > 0) ? sdtimeout : fdtimeout)) {
263  ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", ast_channel_name(chan));
264  ast_debug(1, "AlarmReceiver: DTMF timeout on chan %s\n", ast_channel_name(chan));
265  rtn = 1;
266  break;
267  }
268 
269  if ((r = ast_waitfor(chan, -1)) < 0) {
270  ast_debug(1, "Waitfor returned %d\n", r);
271  continue;
272  }
273 
274  if ((f = ast_read(chan)) == NULL) {
275  rtn = -1;
276  break;
277  }
278 
279  /* If they hung up, leave */
280  if ((f->frametype == AST_FRAME_CONTROL)
281  && (f->subclass.integer == AST_CONTROL_HANGUP)) {
282  if (f->data.uint32) {
283  ast_channel_hangupcause_set(chan, f->data.uint32);
284  }
285  ast_frfree(f);
286  rtn = -1;
287  break;
288  }
289 
290  /* If not DTMF, just do it again */
291  if (f->frametype != AST_FRAME_DTMF) {
292  ast_frfree(f);
293  continue;
294  }
295 
296  /* Save digit */
297  digit_string[(*received)++] = f->subclass.integer;
298  ast_frfree(f);
299 
300  lastdigittime = ast_tvnow();
301  }
302 
303  /* Null terminate the end of the digit_string */
304  digit_string[*received] = '\0';
305 
306  return rtn;
307 }
308 
309 /*!
310  * \brief Write metadata to log file
311  *
312  * \param logfile Log File Pointer
313  * \param signalling_type Signaling Type
314  * \param chan Asterisk Channel
315  * \param no_checksum Expecting messages without checksum
316  *
317  * \retval 0 success
318  * \retval -1 failure
319  */
320 static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum)
321 {
322  struct timeval t;
323  struct ast_tm now;
324  char *cl;
325  char *cn;
326  char workstring[80];
327  char timestamp[80];
328 
329  /* Extract the caller ID location */
330  ast_copy_string(workstring,
331  S_COR(ast_channel_caller(chan)->id.number.valid,
332  ast_channel_caller(chan)->id.number.str, ""), sizeof(workstring));
333  ast_shrink_phone_number(workstring);
334  if (ast_strlen_zero(workstring)) {
335  cl = "<unknown>";
336  } else {
337  cl = workstring;
338  }
339  cn = S_COR(ast_channel_caller(chan)->id.name.valid,
340  ast_channel_caller(chan)->id.name.str, "<unknown>");
341 
342  /* Get the current time */
343  t = ast_tvnow();
344  ast_localtime(&t, &now, NULL);
345 
346  /* Format the time */
347  ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
348 
349  if (no_group_meta && fprintf(logfile, "PROTOCOL=%s\n"
350  "CHECKSUM=%s\n"
351  "CALLINGFROM=%s\n"
352  "CALLERNAME=%s\n"
353  "TIMESTAMP=%s\n\n",
354  signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
355  return 0;
356  } else if (fprintf(logfile, "\n\n[metadata]\n\n"
357  "PROTOCOL=%s\n"
358  "CHECKSUM=%s\n"
359  "CALLINGFROM=%s\n"
360  "CALLERNAME=%s\n"
361  "TIMESTAMP=%s\n\n"
362  "[events]\n\n",
363  signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
364  return 0;
365  }
366 
367  ast_verb(3, "AlarmReceiver: can't write metadata\n");
368  ast_debug(1, "AlarmReceiver: can't write metadata\n");
369  return -1;
370 }
371 
372 /*!
373  * \brief Log a single event
374  *
375  * \param logfile Log File Pointer
376  * \param event Event Structure
377  *
378  * \retval 0 success
379  * \retval -1 failure
380  */
381 static int write_event(FILE *logfile, event_node_t *event)
382 {
383  if (fprintf(logfile, "%s%s\n", no_group_meta ? "event=" : "", event->data) < 0) {
384  return -1;
385  }
386 
387  return 0;
388 }
389 
390 /*!
391  * \brief Log events if configuration key logindividualevents is enabled or on exit
392  *
393  * \param chan Asterisk Channel
394  * \param signalling_type Signaling Type
395  * \param event Event Structure
396  * \param no_checksum Expecting messages without checksum
397  *
398  * \retval 0 success
399  * \retval -1 failure
400  */
401 static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event, int no_checksum)
402 {
403  char workstring[sizeof(event_spool_dir) + sizeof(event_file)] = "";
404  int fd;
405  FILE *logfile;
406  event_node_t *elp = event;
407 
408  if (!ast_strlen_zero(event_spool_dir)) {
409 
410  /* Make a template */
411  ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
412  strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
413 
414  /* Make the temporary file */
415  fd = mkstemp(workstring);
416 
417  if (fd == -1) {
418  ast_verb(3, "AlarmReceiver: can't make temporary file\n");
419  ast_debug(1, "AlarmReceiver: can't make temporary file\n");
420  return -1;
421  }
422 
423  if ((logfile = fdopen(fd, "w")) == NULL) {
424  return -1;
425  }
426 
427  /* Write the file */
428  if (write_metadata(logfile, signalling_type, chan, no_checksum)) {
429  fflush(logfile);
430  fclose(logfile);
431  return -1;
432  }
433 
434  while ((elp != NULL) && (write_event(logfile, elp) == 0)) {
435  elp = elp->next;
436  }
437 
438  fflush(logfile);
439  fclose(logfile);
440  }
441 
442  return 0;
443 }
444 
445 /*!
446  * \brief Verify Ademco checksum
447  * \since 11.0
448  *
449  * \param event Received DTMF String
450  * \param expected Number of Digits expected
451  *
452  * \retval 0 success
453  * \retval -1 failure
454  */
455 static int ademco_verify_checksum(char *event, int expected)
456 {
457  int checksum = 0;
458  int i, j;
459 
460  for (j = 0; j < expected; j++) {
461  for (i = 0; i < ARRAY_LEN(digits_mapping); i++) {
462  if (digits_mapping[i].digit == event[j]) {
463  break;
464  }
465  }
466 
467  if (i >= ARRAY_LEN(digits_mapping)) {
468  ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
469  return -1;
470  }
471 
472  checksum += digits_mapping[i].weight;
473  }
474 
475  /* Checksum is mod(15) of the total */
476  if (!(checksum % 15)) {
477  return 0;
478  }
479 
480  return -1;
481 }
482 
483 /*!
484  * \brief Send a single tone burst for a specified duration and frequency.
485  * \since 11.0
486  *
487  * \param chan Asterisk Channel
488  * \param tone_freq Frequency of the tone to send
489  * \param tone_duration Tone duration in ms
490  * \param delay Delay before sending the tone
491  *
492  * \retval 0 success
493  * \retval -1 failure
494  */
495 static int send_tone_burst(struct ast_channel *chan, const char *tone_freq, int tone_duration, int delay)
496 {
497  if (delay && ast_safe_sleep(chan, delay)) {
498  return -1;
499  }
500 
501  if (ast_playtones_start(chan, toneloudness, tone_freq, 0)) {
502  return -1;
503  }
504 
505  if (ast_safe_sleep(chan, tone_duration)) {
506  return -1;
507  }
508 
509  ast_playtones_stop(chan);
510  return 0;
511 }
512 
513 /*!
514  * \brief Check if the message is in known and valid Ademco format
515  *
516  * \param signalling_type Expected signalling type for the message
517  * \param event event received
518  *
519  * \retval 0 The event is valid
520  * \retval -1 The event is not valid
521  */
522 static int ademco_check_valid(char *signalling_type, char *event)
523 {
524  if (!strcmp(signalling_type, UNKNOWN_FORMAT)) {
525  return 1;
526  }
527 
528  if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
529  && strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
530  && strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
531  return -1;
532  }
533 
534  if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_1) && strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
535  return -1;
536  }
537 
538  if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2) && strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
539  return -1;
540  }
541 
542  if (!strcmp(signalling_type, ADEMCO_HIGH_SPEED) && strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
543  return -1;
544  }
545 
546  if (!strcmp(signalling_type, ADEMCO_SUPER_FAST) && strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
547  return -1;
548  }
549 
550  return 0;
551 }
552 
553 /*!
554  * \brief Detect the message format of an event
555  *
556  * \param signalling_type Expected signalling type for the message
557  * \param event event received
558  * \param no_checksum Should we calculate checksum for the message
559  *
560  * \returns The expected digits for the detected event type
561  */
562 static int ademco_detect_format(char *signalling_type, char *event, int *no_checksum)
563 {
564  int res = 16;
565 
566  if (!strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
567  || !strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
568  sprintf(signalling_type, "%s", ADEMCO_CONTACT_ID);
569  }
570 
571  if (!strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
572  sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_1);
573  res = 8;
574  }
575 
576  if (!strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
577  sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_2);
578  res = 9;
579  }
580 
581  if (!strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
582  sprintf(signalling_type, "%s", ADEMCO_HIGH_SPEED);
583  }
584 
585  if (!strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
586  sprintf(signalling_type, "%s", ADEMCO_SUPER_FAST);
587  *no_checksum = 1;
588  res = 15;
589  }
590 
591  if (strcmp(signalling_type, UNKNOWN_FORMAT)) {
592  ast_verb(4, "AlarmMonitoring: Detected format %s.\n", signalling_type);
593  ast_debug(1, "AlarmMonitoring: Autodetected format %s.\n", signalling_type);
594  }
595 
596  return res;
597 }
598 
599 /*!
600  * \brief Receive Ademco ContactID or other format Data String
601  *
602  * \param chan Asterisk Channel
603  * \param ehead Pointer to events list
604  * \param signalling_type Expected signalling type for the message
605  * \param no_checksum Should we calculate checksum for the message
606  *
607  * \retval 0 success
608  * \retval -1 failure
609  */
610 static int receive_ademco_event(struct ast_channel *chan, event_node_t **ehead, char *signalling_type, int *no_checksum)
611 {
612  int res = 0;
613  const char *limit;
614  char event[17];
615  event_node_t *enew, *elp;
616  int got_some_digits = 0;
617  int events_received = 0;
618  int ack_retries = 0;
619  int limit_retries = 0;
620  int expected_length = sizeof(event) - 1;
621 
622  database_increment("calls-received");
623 
624  /* Wait for first event */
625  ast_verb(4, "AlarmReceiver: Waiting for first event from panel...\n");
626 
627  while (res >= 0) {
628  int digits_received = 0;
629 
630  res = 0;
631 
632  if (log_individual_events) {
633  sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
634  expected_length = 16;
635  *no_checksum = 0;
636  }
637 
638  if (got_some_digits == 0) {
639  /* Send ACK tone sequence */
640  ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
641  res = send_tone_burst(chan, "1400", 100, 0);
642  if (!res) {
643  ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
644  res = send_tone_burst(chan, "2300", 100, 100);
645  }
646  }
647  if (res) {
648  return -1;
649  }
650 
651  res = receive_dtmf_digits(chan, event, sizeof(event), expected_length, &digits_received);
652  if (res < 0) {
653  if (events_received == 0) {
654  /* Hangup with no events received should be logged in the DB */
655  database_increment("no-events-received");
656  ast_verb(4, "AlarmReceiver: No events received!\n");
657  } else {
658  if (ack_retries) {
659  database_increment("ack-retries");
660  ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
661  }
662  }
663  ast_verb(4, "AlarmReceiver: App exiting...\n");
664  break;
665  }
666 
667  if (!strcmp(signalling_type, UNKNOWN_FORMAT) && digits_received > 5) {
668  expected_length = ademco_detect_format(signalling_type, event, no_checksum);
669 
670  if (res > 0) {
671  if (digits_received == expected_length) {
672  res = limit_retries = 0;
673  } else if (digits_received == expected_length - 1
674  && (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2)
675  || !strcmp(signalling_type, ADEMCO_EXPRESS_4_1))) {
676  /* ADEMCO EXPRESS without checksum */
677  res = limit_retries = 0;
678  expected_length--;
679  *no_checksum = 1;
680  ast_verb(4, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
681  ast_debug(1, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
682  }
683  }
684  }
685 
686  ast_channel_lock(chan);
687  limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_CALL_LIMIT");
688  if (!ast_strlen_zero(limit)) {
689  if (ast_tvdiff_ms(ast_tvnow(), call_start_time) > atoi(limit)) {
690  ast_channel_unlock(chan);
691  return -1;
692  }
693  }
694  limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_RETRIES_LIMIT");
695  ast_channel_unlock(chan);
696  if (!ast_strlen_zero(limit)) {
697  if (limit_retries + 1 >= atoi(limit)) {
698  return -1;
699  }
700  }
701 
702  if (res) {
703  /* Didn't get all of the digits */
704  ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
705  limit_retries++;
706 
707  if (!events_received && strcmp(signalling_type, UNKNOWN_FORMAT))
708  {
709  sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
710  expected_length = sizeof(event) - 1;
711  }
712 
713  if (!got_some_digits) {
714  got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
715  ack_retries++;
716  }
717  continue;
718  }
719 
720  got_some_digits = 1;
721 
722  ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
723  ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
724 
725  /* Calculate checksum */
726  if (!(*no_checksum) && ademco_verify_checksum(event, expected_length)) {
727  database_increment("checksum-errors");
728  ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
729  ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
730  continue;
731  }
732 
733  /* Check the message type for correctness */
734  if (ademco_check_valid(signalling_type, event)) {
735  database_increment("format-errors");
736  ast_verb(2, "AlarmReceiver: Wrong message type\n");
737  ast_debug(1, "AlarmReceiver: Wrong message type\n");
738  continue;
739  }
740 
741  events_received++;
742 
743  /* Queue the Event */
744  if (!(enew = ast_calloc(1, sizeof(*enew)))) {
745  return -1;
746  }
747 
748  enew->next = NULL;
749  ast_copy_string(enew->data, event, sizeof(enew->data));
750 
751  /* Insert event onto end of list */
752  if (*ehead == NULL) {
753  *ehead = enew;
754  } else {
755  for (elp = *ehead; elp->next != NULL; elp = elp->next) {
756  ;
757  }
758  elp->next = enew;
759  }
760 
761  /* Let the user have the option of logging the single event before sending the kissoff tone */
762  if (log_individual_events && log_events(chan, signalling_type, enew, *no_checksum)) {
763  return -1;
764  }
765 
766  /* Send the kissoff tone (1400 Hz, 900 ms, after 200ms delay) */
767  if (send_tone_burst(chan, "1400", 900, 200)) {
768  return -1;
769  }
770 
771  /* If audio call follows, exit alarm receiver app */
772  if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
773  && !strncmp(event + 7, ADEMCO_AUDIO_CALL_NEXT, 3)) {
774  ast_verb(4, "AlarmReceiver: App exiting... Audio call next!\n");
775  return 0;
776  }
777  }
778 
779  return res;
780 }
781 
782 /*!
783  * \brief This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
784  *
785  * \param chan Asterisk Channel
786  * \param data Application data
787  *
788  * \retval 0 success
789  * \retval -1 failure
790  */
791 static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
792 {
793  int res = 0;
794  int no_checksum = 0;
795  event_node_t *elp, *efree;
796  char signalling_type[64] = "";
797  event_node_t *event_head = NULL;
798 
799  if ((ast_format_cmp(ast_channel_writeformat(chan), ast_format_ulaw) == AST_FORMAT_CMP_NOT_EQUAL) &&
800  (ast_format_cmp(ast_channel_writeformat(chan), ast_format_alaw) == AST_FORMAT_CMP_NOT_EQUAL)) {
801  ast_verb(4, "AlarmReceiver: Setting write format to Mu-law\n");
803  ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",ast_channel_name(chan));
804  return -1;
805  }
806  }
807 
808  if ((ast_format_cmp(ast_channel_readformat(chan), ast_format_ulaw) == AST_FORMAT_CMP_NOT_EQUAL) &&
809  (ast_format_cmp(ast_channel_readformat(chan), ast_format_alaw) == AST_FORMAT_CMP_NOT_EQUAL)) {
810  ast_verb(4, "AlarmReceiver: Setting read format to Mu-law\n");
812  ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",ast_channel_name(chan));
813  return -1;
814  }
815  }
816 
817  /* Set default values for this invocation of the application */
818  ast_copy_string(signalling_type, UNKNOWN_FORMAT, sizeof(signalling_type));
819  call_start_time = ast_tvnow();
820 
821  /* Answer the channel if it is not already */
822  if (ast_channel_state(chan) != AST_STATE_UP) {
823  ast_verb(4, "AlarmReceiver: Answering channel\n");
824  if (ast_answer(chan)) {
825  return -1;
826  }
827  }
828 
829  /* Wait for the connection to settle post-answer */
830  ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
831  if (ast_safe_sleep(chan, answait)) {
832  return -1;
833  }
834 
835  /* Attempt to receive the events */
836  receive_ademco_event(chan, &event_head, signalling_type, &no_checksum);
837 
838  /* Events queued by receiver, write them all out here if so configured */
839  if (!log_individual_events) {
840  res = log_events(chan, signalling_type, event_head, no_checksum);
841  }
842 
843  /* Do we exec a command line at the end? */
844  if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
845  ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
846  ast_safe_system(event_app);
847  }
848 
849  /* Free up the data allocated in our linked list */
850  for (elp = event_head; (elp != NULL);) {
851  efree = elp;
852  elp = elp->next;
853  ast_free(efree);
854  }
855 
856  return 0;
857 }
858 
859 /*!
860  * \brief Load the configuration from the configuration file
861  *
862  * \param reload True on reload
863  *
864  * \retval 1 success
865  * \retval 0 failure
866  */
867 static int load_config(int reload)
868 {
869  struct ast_config *cfg;
870  const char *value;
871  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
872 
873  /* Read in the config file */
874  cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
875 
876  if (!cfg) {
877  ast_verb(4, "AlarmReceiver: No config file\n");
878  return 0;
879  } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
880  return 1;
881  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
882  ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n",
883  ALMRCV_CONFIG);
884  return 0;
885  }
886 
887  if ((value = ast_variable_retrieve(cfg, "general", "eventcmd")) != NULL) {
888  ast_copy_string(event_app, value, sizeof(event_app));
889  }
890 
891  if ((value = ast_variable_retrieve(cfg, "general", "loudness")) != NULL) {
892  toneloudness = atoi(value);
893  if (toneloudness < 100) {
894  toneloudness = 100;
895  } else if (toneloudness > 8192) {
896  toneloudness = 8192;
897  }
898  }
899 
900  if ((value = ast_variable_retrieve(cfg, "general", "fdtimeout")) != NULL) {
901  fdtimeout = atoi(value);
902  if (fdtimeout < 1000) {
903  fdtimeout = 1000;
904  } else if (fdtimeout > 10000) {
905  fdtimeout = 10000;
906  }
907  }
908 
909  if ((value = ast_variable_retrieve(cfg, "general", "sdtimeout")) != NULL) {
910  sdtimeout = atoi(value);
911  if (sdtimeout < 110) {
912  sdtimeout = 110;
913  } else if (sdtimeout > 4000) {
914  sdtimeout = 4000;
915  }
916  }
917 
918  if ((value = ast_variable_retrieve(cfg, "general", "answait")) != NULL) {
919  answait = atoi(value);
920  if (answait < 500) {
921  answait = 500;
922  } else if (answait > 10000) {
923  answait = 10000;
924  }
925  }
926 
927  if ((value = ast_variable_retrieve(cfg, "general", "no_group_meta")) != NULL) {
928  no_group_meta = ast_true(value);
929  }
930 
931  if ((value = ast_variable_retrieve(cfg, "general", "logindividualevents")) != NULL) {
932  log_individual_events = ast_true(value);
933  }
934 
935  if ((value = ast_variable_retrieve(cfg, "general", "eventspooldir")) != NULL) {
936  ast_copy_string(event_spool_dir, value, sizeof(event_spool_dir));
937  }
938 
939  if ((value = ast_variable_retrieve(cfg, "general", "timestampformat")) != NULL) {
940  ast_copy_string(time_stamp_format, value, sizeof(time_stamp_format));
941  }
942 
943  if ((value = ast_variable_retrieve(cfg, "general", "db-family")) != NULL) {
944  ast_copy_string(db_family, value, sizeof(db_family));
945  }
946 
947  ast_config_destroy(cfg);
948 
949  return 1;
950 }
951 
952 /*!
953  * \brief Unregister Alarm Receiver App
954  *
955  * \retval 0 success
956  * \retval -1 failure
957  */
958 static int unload_module(void)
959 {
960  return ast_unregister_application(app);
961 }
962 
963 /*!
964  * \brief Load the module
965  *
966  * Module loading including tests for configuration or dependencies.
967  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
968  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
969  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
970  * configuration file or other non-critical problem return
971  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
972  */
973 static int load_module(void)
974 {
975  if (load_config(0)) {
978  }
980  }
981 
983 }
984 
985 static int reload(void)
986 {
987  if (load_config(1)) {
989  }
990 
992 }
993 
994 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Alarm Receiver for Asterisk",
995  .support_level = AST_MODULE_SUPPORT_EXTENDED,
996  .load = load_module,
997  .unload = unload_module,
998  .reload = reload,
999 );
static int load_config(int reload)
Load the configuration from the configuration file.
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1574
Tone Indication Support.
Main Channel structure associated with a channel.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
Support for translation of data formats. translate.c.
Convenient Signal Processing routines.
struct ast_format * ast_format_ulaw
Built-in cached ulaw format.
Definition: format_cache.c:86
static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int buf_size, int expected, int *received)
Receive a fixed length DTMF string.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
ast_channel_state
ast_channel states
Definition: channelstate.h:35
Definition: astman.c:222
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
static void database_increment(char *key)
Attempt to access a database variable and increment it.
void ast_playtones_stop(struct ast_channel *chan)
Stop playing tones on a channel.
Definition: indications.c:393
static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum)
Write metadata to log file.
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct ast_frame_subclass subclass
Utility functions.
Number structure.
Definition: app_followme.c:154
Custom localtime functions for multiple timezones.
Configuration File Parser.
static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
This is the main function called by Asterisk Core whenever the App is invoked in the extension logic...
#define ast_config_load(filename, flags)
Load a config file.
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition: format.c:201
General Asterisk PBX channel definitions.
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5762
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
#define ast_debug(level,...)
Log a DEBUG message.
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5803
static int ademco_verify_checksum(char *event, int expected)
Verify Ademco checksum.
Core PBX routines and definitions.
static int send_tone_burst(struct ast_channel *chan, const char *tone_freq, int tone_duration, int delay)
Send a single tone burst for a specified duration and frequency.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
Definition: extconf.c:829
static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event, int no_checksum)
Log events if configuration key logindividualevents is enabled or on exit.
union ast_frame::@224 data
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: main/db.c:427
Structure used to handle boolean flags.
Definition: utils.h:199
static int load_module(void)
Load the module.
static int write_event(FILE *logfile, event_node_t *event)
Log a single event.
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3162
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
struct ast_format * ast_format_alaw
Built-in cached alaw format.
Definition: format_cache.c:91
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
Data structure associated with a single frame of data.
int ast_playtones_start(struct ast_channel *chan, int vol, const char *tonelist, int interruptible)
Start playing a list of tones on a channel.
Definition: indications.c:302
static int ademco_check_valid(char *signalling_type, char *event)
Check if the message is in known and valid Ademco format.
static int receive_ademco_event(struct ast_channel *chan, event_node_t **ehead, char *signalling_type, int *no_checksum)
Receive Ademco ContactID or other format Data String.
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:342
enum ast_frame_type frametype
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
void ast_shrink_phone_number(char *n)
Shrink a phone number in place to just digits (more accurately it just removes ()'s, .'s, and -'s...
Definition: callerid.c:1101
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
Persistent data storage (akin to *doze registry)
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Media Format Cache API.
static int ademco_detect_format(char *signalling_type, char *event, int *no_checksum)
Detect the message format of an event.
static int unload_module(void)
Unregister Alarm Receiver App.