We now can have a reference to a filename inside a <see-also> tag.
[asterisk/asterisk.git] / apps / app_alarmreceiver.c
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  * \brief Central Station Alarm receiver for Ademco Contact ID
21  * \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
22  *
23  * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
24  *
25  * Use at your own risk. Please consult the GNU GPL license document included with Asterisk.         *
26  *
27  * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
28  *
29  * \ingroup applications
30  */
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include <math.h>
37 #include <sys/wait.h>
38 #include <sys/time.h>
39
40 #include "asterisk/lock.h"
41 #include "asterisk/file.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/module.h"
45 #include "asterisk/translate.h"
46 #include "asterisk/ulaw.h"
47 #include "asterisk/app.h"
48 #include "asterisk/dsp.h"
49 #include "asterisk/config.h"
50 #include "asterisk/localtime.h"
51 #include "asterisk/callerid.h"
52 #include "asterisk/astdb.h"
53 #include "asterisk/utils.h"
54
55 #define ALMRCV_CONFIG "alarmreceiver.conf"
56 #define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
57
58 struct event_node{
59         char data[17];
60         struct event_node *next;
61 };
62
63 typedef struct event_node event_node_t;
64
65 static char *app = "AlarmReceiver";
66 /*** DOCUMENTATION
67         <application name="AlarmReceiver" language="en_US">
68                 <synopsis>
69                         Provide support for receiving alarm reports from a burglar or fire alarm panel
70                 </synopsis>
71                 <syntax />
72                 <description>
73                         <para>This application should be called whenever there is an alarm panel calling in to dump its events.
74                         The application will handshake with the alarm panel, and receive events, validate them, handshake them, and store them until the panel hangs up.
75                         Once the panel hangs up, the application will run the system command specified by the eventcmd setting in <filename>alarmreceiver.conf</filename> and pipe the events to the standard input of the application. 
76                         The configuration file also contains settings for DTMF timing, and for the loudness of the acknowledgement tones.</para>
77                         <note><para>Only 1 signalling format is supported at this time: Ademco Contact ID.</para></note>
78                 </description>
79         </application>
80  ***/
81
82 /* Config Variables */
83 static int fdtimeout = 2000;
84 static int sdtimeout = 200;
85 static int toneloudness = 4096;
86 static int log_individual_events = 0;
87 static char event_spool_dir[128] = {'\0'};
88 static char event_app[128] = {'\0'};
89 static char db_family[128] = {'\0'};
90 static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
91
92 /* Misc variables */
93 static char event_file[14] = "/event-XXXXXX";
94
95 /*
96 * Attempt to access a database variable and increment it,
97 * provided that the user defined db-family in alarmreceiver.conf
98 * The alarmreceiver app will write statistics to a few variables
99 * in this family if it is defined. If the new key doesn't exist in the
100 * family, then create it and set its value to 1.
101 */
102 static void database_increment( char *key )
103 {
104         int res = 0;
105         unsigned v;
106         char value[16];
107         
108         
109         if (ast_strlen_zero(db_family))
110                 return; /* If not defined, don't do anything */
111         
112         res = ast_db_get(db_family, key, value, sizeof(value) - 1);
113         
114         if (res) {
115                 ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
116                 /* Guess we have to create it */
117                 res = ast_db_put(db_family, key, "1");
118                 return;
119         }
120         
121         sscanf(value, "%u", &v);
122         v++;
123
124         ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v);
125
126         snprintf(value, sizeof(value), "%u", v);
127
128         res = ast_db_put(db_family, key, value);
129
130         if (res)
131                 ast_verb(4, "AlarmReceiver: database_increment write error\n");
132
133         return;
134 }
135
136
137 /*
138 * Build a MuLaw data block for a single frequency tone
139 */
140 static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x)
141 {
142         int     i;
143         float   val;
144
145         for (i = 0; i < len; i++) {
146                 val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0);
147                 data[i] = AST_LIN2MU((int)val);
148         }
149
150         /* wrap back around from 8000 */
151
152         if (*x >= 8000)
153                 *x = 0;
154         return;
155 }
156
157 /*
158 * Send a single tone burst for a specifed duration and frequency.
159 * Returns 0 if successful
160 */
161 static int send_tone_burst(struct ast_channel *chan, float freq, int duration, int tldn)
162 {
163         int res = 0;
164         int i = 0;
165         int x = 0;
166         struct ast_frame *f, wf;
167         
168         struct {
169                 unsigned char offset[AST_FRIENDLY_OFFSET];
170                 unsigned char buf[640];
171         } tone_block;
172
173         for (;;) {
174
175                 if (ast_waitfor(chan, -1) < 0) {
176                         res = -1;
177                         break;
178                 }
179
180                 f = ast_read(chan);
181                 if (!f) {
182                         res = -1;
183                         break;
184                 }
185
186                 if (f->frametype == AST_FRAME_VOICE) {
187                         wf.frametype = AST_FRAME_VOICE;
188                         wf.subclass = AST_FORMAT_ULAW;
189                         wf.offset = AST_FRIENDLY_OFFSET;
190                         wf.mallocd = 0;
191                         wf.data.ptr = tone_block.buf;
192                         wf.datalen = f->datalen;
193                         wf.samples = wf.datalen;
194                         
195                         make_tone_burst(tone_block.buf, freq, (float) tldn, wf.datalen, &x);
196
197                         i += wf.datalen / 8;
198                         if (i > duration) {
199                                 ast_frfree(f);
200                                 break;
201                         }
202                         if (ast_write(chan, &wf)) {
203                                 ast_verb(4, "AlarmReceiver: Failed to write frame on %s\n", chan->name);
204                                 ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",chan->name);
205                                 res = -1;
206                                 ast_frfree(f);
207                                 break;
208                         }
209                 }
210
211                 ast_frfree(f);
212         }
213         return res;
214 }
215
216 /*
217 * Receive a string of DTMF digits where the length of the digit string is known in advance. Do not give preferential
218 * treatment to any digit value, and allow separate time out values to be specified for the first digit and all subsequent
219 * digits.
220 *
221 * Returns 0 if all digits successfully received.
222 * Returns 1 if a digit time out occurred
223 * Returns -1 if the caller hung up or there was a channel error.
224 *
225 */
226 static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto)
227 {
228         int res = 0;
229         int i = 0;
230         int r;
231         struct ast_frame *f;
232         struct timeval lastdigittime;
233
234         lastdigittime = ast_tvnow();
235         for (;;) {
236                 /* if outa time, leave */
237                 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((i > 0) ? sdto : fdto)) {
238                         ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", chan->name);
239                         ast_debug(1,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name);
240                         res = 1;
241                         break;
242                 }
243
244                 if ((r = ast_waitfor(chan, -1) < 0)) {
245                         ast_debug(1, "Waitfor returned %d\n", r);
246                         continue;
247                 }
248
249                 f = ast_read(chan);
250
251                 if (f == NULL) {
252                         res = -1;
253                         break;
254                 }
255
256                 /* If they hung up, leave */
257                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
258                         if (f->data.uint32) {
259                                 chan->hangupcause = f->data.uint32;
260                         }
261                         ast_frfree(f);
262                         res = -1;
263                         break;
264                 }
265
266                 /* if not DTMF, just do it again */
267                 if (f->frametype != AST_FRAME_DTMF) {
268                         ast_frfree(f);
269                         continue;
270                 }
271
272                 digit_string[i++] = f->subclass;  /* save digit */
273
274                 ast_frfree(f);
275
276                 /* If we have all the digits we expect, leave */
277                 if(i >= length)
278                         break;
279
280                 lastdigittime = ast_tvnow();
281         }
282
283         digit_string[i] = '\0'; /* Nul terminate the end of the digit string */
284         return res;
285 }
286
287 /*
288 * Write the metadata to the log file
289 */
290 static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
291 {
292         int res = 0;
293         struct timeval t;
294         struct ast_tm now;
295         char *cl,*cn;
296         char workstring[80];
297         char timestamp[80];
298         
299         /* Extract the caller ID location */
300         if (chan->cid.cid_num)
301                 ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
302         workstring[sizeof(workstring) - 1] = '\0';
303
304         ast_callerid_parse(workstring, &cn, &cl);
305         if (cl)
306                 ast_shrink_phone_number(cl);
307
308         /* Get the current time */
309         t = ast_tvnow();
310         ast_localtime(&t, &now, NULL);
311
312         /* Format the time */
313         ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
314
315         res = fprintf(logfile, "\n\n[metadata]\n\n");
316
317         if (res >= 0)
318                 res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
319
320         if (res >= 0)
321                 res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "<unknown>" : cl);
322
323         if (res >- 0)
324                 res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "<unknown>" : cn);
325
326         if (res >= 0)
327                 res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
328
329         if (res >= 0)
330                 res = fprintf(logfile, "[events]\n\n");
331
332         if (res < 0) {
333                 ast_verb(3, "AlarmReceiver: can't write metadata\n");
334                 ast_debug(1,"AlarmReceiver: can't write metadata\n");
335         } else
336                 res = 0;
337
338         return res;
339 }
340
341 /*
342 * Write a single event to the log file
343 */
344 static int write_event( FILE *logfile,  event_node_t *event)
345 {
346         int res = 0;
347
348         if (fprintf(logfile, "%s\n", event->data) < 0)
349                 res = -1;
350
351         return res;
352 }
353
354 /*
355 * If we are configured to log events, do so here.
356 *
357 */
358 static int log_events(struct ast_channel *chan,  char *signalling_type, event_node_t *event)
359 {
360
361         int res = 0;
362         char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
363         int fd;
364         FILE *logfile;
365         event_node_t *elp = event;
366         
367         if (!ast_strlen_zero(event_spool_dir)) {
368                 
369                 /* Make a template */
370                 ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
371                 strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
372                 
373                 /* Make the temporary file */
374                 fd = mkstemp(workstring);
375                 
376                 if (fd == -1) {
377                         ast_verb(3, "AlarmReceiver: can't make temporary file\n");
378                         ast_debug(1,"AlarmReceiver: can't make temporary file\n");
379                         res = -1;
380                 }
381
382                 if (!res) {
383                         logfile = fdopen(fd, "w");
384                         if (logfile) {
385                                 /* Write the file */
386                                 res = write_metadata(logfile, signalling_type, chan);
387                                 if (!res)
388                                         while ((!res) && (elp != NULL)) {
389                                                 res = write_event(logfile, elp);
390                                                 elp = elp->next;
391                                         }
392                                 if (!res) {
393                                         if (fflush(logfile) == EOF)
394                                                 res = -1;
395                                         if (!res) {
396                                                 if (fclose(logfile) == EOF)
397                                                         res = -1;
398                                         }
399                                 }
400                         } else
401                                 res = -1;
402                 }
403         }
404
405         return res;
406 }
407
408 /*
409 * This function implements the logic to receive the Ademco contact ID  format.
410 *
411 * The function will return 0 when the caller hangs up, else a -1 if there was a problem.
412 */
413 static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
414 {
415         int i, j;
416         int res = 0;
417         int checksum;
418         char event[17];
419         event_node_t *enew, *elp;
420         int got_some_digits = 0;
421         int events_received = 0;
422         int ack_retries = 0;
423         
424         static char digit_map[15] = "0123456789*#ABC";
425         static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
426
427         database_increment("calls-received");
428
429         /* Wait for first event */
430         ast_verb(4, "AlarmReceiver: Waiting for first event from panel\n");
431
432         while (res >= 0) {
433                 if (got_some_digits == 0) {
434                         /* Send ACK tone sequence */
435                         ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
436                         res = send_tone_burst(chan, 1400.0, 100, tldn);
437                         if (!res)
438                                 res = ast_safe_sleep(chan, 100);
439                         if (!res) {
440                                 ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
441                                 res = send_tone_burst(chan, 2300.0, 100, tldn);
442                         }
443                 }
444                 if ( res >= 0)
445                         res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
446                 if (res < 0) {
447                         if (events_received == 0) {
448                                 /* Hangup with no events received should be logged in the DB */
449                                 database_increment("no-events-received");
450                         } else {
451                                 if (ack_retries) {
452                                         ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
453                                         database_increment("ack-retries");
454                                 }
455                         }
456                         ast_verb(4, "AlarmReceiver: App exiting...\n");
457                         res = -1;
458                         break;
459                 }
460
461                 if (res != 0) {
462                         /* Didn't get all of the digits */
463                         ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
464
465                         if (!got_some_digits) {
466                                 got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
467                                 ack_retries++;
468                         }
469                         continue;
470                 }
471
472                 got_some_digits = 1;
473
474                 ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
475                 ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
476
477                 /* Calculate checksum */
478
479                 for (j = 0, checksum = 0; j < 16; j++) {
480                         for (i = 0; i < sizeof(digit_map); i++) {
481                                 if (digit_map[i] == event[j])
482                                         break;
483                         }
484
485                         if (i == 16)
486                                 break;
487
488                         checksum += digit_weights[i];
489                 }
490                 if (i == 16) {
491                         ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
492                         continue; /* Bad character */
493                 }
494
495                 /* Checksum is mod(15) of the total */
496
497                 checksum = checksum % 15;
498
499                 if (checksum) {
500                         database_increment("checksum-errors");
501                         ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
502                         ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
503                         continue;
504                 }
505
506                 /* Check the message type for correctness */
507
508                 if (strncmp(event + 4, "18", 2)) {
509                         if (strncmp(event + 4, "98", 2)) {
510                                 database_increment("format-errors");
511                                 ast_verb(2, "AlarmReceiver: Wrong message type\n");
512                                 ast_debug(1, "AlarmReceiver: Wrong message type\n");
513                         continue;
514                         }
515                 }
516
517                 events_received++;
518
519                 /* Queue the Event */
520                 if (!(enew = ast_calloc(1, sizeof(*enew)))) {
521                         res = -1;
522                         break;
523                 }
524
525                 enew->next = NULL;
526                 ast_copy_string(enew->data, event, sizeof(enew->data));
527
528                 /*
529                 * Insert event onto end of list
530                 */
531                 if (*ehead == NULL)
532                         *ehead = enew;
533                 else {
534                         for(elp = *ehead; elp->next != NULL; elp = elp->next)
535                         ;
536                         elp->next = enew;
537                 }
538
539                 if (res > 0)
540                         res = 0;
541
542                 /* Let the user have the option of logging the single event before sending the kissoff tone */
543                 if ((res == 0) && (log_individual_events))
544                         res = log_events(chan, ADEMCO_CONTACT_ID, enew);
545                 /* Wait 200 msec before sending kissoff */
546                 if (res == 0)
547                         res = ast_safe_sleep(chan, 200);
548
549                 /* Send the kissoff tone */
550                 if (res == 0)
551                         res = send_tone_burst(chan, 1400.0, 900, tldn);
552         }
553
554         return res;
555 }
556
557 /*
558 * This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
559 * This function will always return 0.
560 */
561 static int alarmreceiver_exec(struct ast_channel *chan, void *data)
562 {
563         int res = 0;
564         event_node_t *elp, *efree;
565         char signalling_type[64] = "";
566         event_node_t *event_head = NULL;
567
568         /* Set write and read formats to ULAW */
569         ast_verb(4, "AlarmReceiver: Setting read and write formats to ULAW\n");
570
571         if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
572                 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
573                 return -1;
574         }
575
576         if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
577                 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
578                 return -1;
579         }
580
581         /* Set default values for this invocation of the application */
582         ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
583
584         /* Answer the channel if it is not already */
585         ast_verb(4, "AlarmReceiver: Answering channel\n");
586         if (chan->_state != AST_STATE_UP) {
587                 if ((res = ast_answer(chan)))
588                         return -1;
589         }
590
591         /* Wait for the connection to settle post-answer */
592         ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
593         res = ast_safe_sleep(chan, 1250);
594
595         /* Attempt to receive the events */
596         if (!res) {
597                 /* Determine the protocol to receive in advance */
598                 /* Note: Ademco contact is the only one supported at this time */
599                 /* Others may be added later */
600                 if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
601                         receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
602                 else
603                         res = -1;
604         }
605
606         /* Events queued by receiver, write them all out here if so configured */
607         if ((!res) && (log_individual_events == 0))
608                 res = log_events(chan, signalling_type, event_head);
609
610         /*
611         * Do we exec a command line at the end?
612         */
613         if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
614                 ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
615                 ast_safe_system(event_app);
616         }
617
618         /*
619         * Free up the data allocated in our linked list
620         */
621         for (elp = event_head; (elp != NULL);) {
622                 efree = elp;
623                 elp = elp->next;
624                 ast_free(efree);
625         }
626
627         return 0;
628 }
629
630 /*
631 * Load the configuration from the configuration file
632 */
633 static int load_config(void)
634 {
635         struct ast_config *cfg;
636         const char *p;
637         struct ast_flags config_flags = { 0 };
638
639         /* Read in the config file */
640         cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
641
642         if (!cfg) {
643                 ast_verb(4, "AlarmReceiver: No config file\n");
644                 return 0;
645         } else if (cfg == CONFIG_STATUS_FILEINVALID) {
646                 ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", ALMRCV_CONFIG);
647                 return 0;
648         } else {
649                 p = ast_variable_retrieve(cfg, "general", "eventcmd");
650                 if (p) {
651                         ast_copy_string(event_app, p, sizeof(event_app));
652                         event_app[sizeof(event_app) - 1] = '\0';
653                 }
654                 p = ast_variable_retrieve(cfg, "general", "loudness");
655                 if (p) {
656                         toneloudness = atoi(p);
657                         if(toneloudness < 100)
658                                 toneloudness = 100;
659                         if(toneloudness > 8192)
660                                 toneloudness = 8192;
661                 }
662                 p = ast_variable_retrieve(cfg, "general", "fdtimeout");
663                 if (p) {
664                         fdtimeout = atoi(p);
665                         if(fdtimeout < 1000)
666                                 fdtimeout = 1000;
667                         if(fdtimeout > 10000)
668                                 fdtimeout = 10000;
669                 }
670
671                 p = ast_variable_retrieve(cfg, "general", "sdtimeout");
672                 if (p) {
673                         sdtimeout = atoi(p);
674                         if(sdtimeout < 110)
675                                 sdtimeout = 110;
676                         if(sdtimeout > 4000)
677                                 sdtimeout = 4000;
678                 }
679
680                 p = ast_variable_retrieve(cfg, "general", "logindividualevents");
681                 if (p)
682                         log_individual_events = ast_true(p);
683
684                 p = ast_variable_retrieve(cfg, "general", "eventspooldir");
685                 if (p) {
686                         ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
687                         event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
688                 }
689
690                 p = ast_variable_retrieve(cfg, "general", "timestampformat");
691                 if (p) {
692                         ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
693                         time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
694                 }
695
696                 p = ast_variable_retrieve(cfg, "general", "db-family");
697                 if (p) {
698                         ast_copy_string(db_family, p, sizeof(db_family));
699                         db_family[sizeof(db_family) - 1] = '\0';
700                 }
701                 ast_config_destroy(cfg);
702         }
703         return 1;
704 }
705
706 /*
707 * These functions are required to implement an Asterisk App.
708 */
709 static int unload_module(void)
710 {
711         return ast_unregister_application(app);
712 }
713
714 static int load_module(void)
715 {
716         if (load_config()) {
717                 if (ast_register_application_xml(app, alarmreceiver_exec))
718                         return AST_MODULE_LOAD_FAILURE;
719                 return AST_MODULE_LOAD_SUCCESS;
720         } else
721                 return AST_MODULE_LOAD_DECLINE;
722 }
723
724 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");