00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "asterisk.h"
00033
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211569 $")
00035
00036 #include <math.h>
00037 #include <sys/wait.h>
00038 #include <sys/time.h>
00039
00040 #include "asterisk/lock.h"
00041 #include "asterisk/file.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/pbx.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/translate.h"
00046 #include "asterisk/ulaw.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/dsp.h"
00049 #include "asterisk/config.h"
00050 #include "asterisk/localtime.h"
00051 #include "asterisk/callerid.h"
00052 #include "asterisk/astdb.h"
00053 #include "asterisk/utils.h"
00054
00055 #define ALMRCV_CONFIG "alarmreceiver.conf"
00056 #define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
00057
00058 struct event_node{
00059 char data[17];
00060 struct event_node *next;
00061 };
00062
00063 typedef struct event_node event_node_t;
00064
00065 static char *app = "AlarmReceiver";
00066
00067 static char *synopsis = "Provide support for receiving alarm reports from a burglar or fire alarm panel";
00068 static char *descrip =
00069 " AlarmReceiver(): Only 1 signalling format is supported at this time: Ademco\n"
00070 "Contact ID. This application should be called whenever there is an alarm\n"
00071 "panel calling in to dump its events. The application will handshake with the\n"
00072 "alarm panel, and receive events, validate them, handshake them, and store them\n"
00073 "until the panel hangs up. Once the panel hangs up, the application will run the\n"
00074 "system command specified by the eventcmd setting in alarmreceiver.conf and pipe\n"
00075 "the events to the standard input of the application. The configuration file also\n"
00076 "contains settings for DTMF timing, and for the loudness of the acknowledgement\n"
00077 "tones.\n";
00078
00079
00080 static int fdtimeout = 2000;
00081 static int sdtimeout = 200;
00082 static int toneloudness = 4096;
00083 static int log_individual_events = 0;
00084 static char event_spool_dir[128] = {'\0'};
00085 static char event_app[128] = {'\0'};
00086 static char db_family[128] = {'\0'};
00087 static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
00088
00089
00090 static char event_file[14] = "/event-XXXXXX";
00091
00092
00093
00094
00095
00096
00097
00098
00099 static void database_increment( char *key )
00100 {
00101 int res = 0;
00102 unsigned v;
00103 char value[16];
00104
00105
00106 if (ast_strlen_zero(db_family))
00107 return;
00108
00109 res = ast_db_get(db_family, key, value, sizeof(value) - 1);
00110
00111 if (res) {
00112 ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
00113
00114 res = ast_db_put(db_family, key, "1");
00115 return;
00116 }
00117
00118 sscanf(value, "%30u", &v);
00119 v++;
00120
00121 ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v);
00122
00123 snprintf(value, sizeof(value), "%u", v);
00124
00125 res = ast_db_put(db_family, key, value);
00126
00127 if (res)
00128 ast_verb(4, "AlarmReceiver: database_increment write error\n");
00129
00130 return;
00131 }
00132
00133
00134
00135
00136
00137 static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x)
00138 {
00139 int i;
00140 float val;
00141
00142 for (i = 0; i < len; i++) {
00143 val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0);
00144 data[i] = AST_LIN2MU((int)val);
00145 }
00146
00147
00148
00149 if (*x >= 8000)
00150 *x = 0;
00151 return;
00152 }
00153
00154
00155
00156
00157
00158 static int send_tone_burst(struct ast_channel *chan, float freq, int duration, int tldn)
00159 {
00160 int res = 0;
00161 int i = 0;
00162 int x = 0;
00163 struct ast_frame *f, wf;
00164
00165 struct {
00166 unsigned char offset[AST_FRIENDLY_OFFSET];
00167 unsigned char buf[640];
00168 } tone_block;
00169
00170 for (;;) {
00171
00172 if (ast_waitfor(chan, -1) < 0) {
00173 res = -1;
00174 break;
00175 }
00176
00177 f = ast_read(chan);
00178 if (!f) {
00179 res = -1;
00180 break;
00181 }
00182
00183 if (f->frametype == AST_FRAME_VOICE) {
00184 wf.frametype = AST_FRAME_VOICE;
00185 wf.subclass = AST_FORMAT_ULAW;
00186 wf.offset = AST_FRIENDLY_OFFSET;
00187 wf.mallocd = 0;
00188 wf.data.ptr = tone_block.buf;
00189 wf.datalen = f->datalen;
00190 wf.samples = wf.datalen;
00191
00192 make_tone_burst(tone_block.buf, freq, (float) tldn, wf.datalen, &x);
00193
00194 i += wf.datalen / 8;
00195 if (i > duration) {
00196 ast_frfree(f);
00197 break;
00198 }
00199 if (ast_write(chan, &wf)) {
00200 ast_verb(4, "AlarmReceiver: Failed to write frame on %s\n", chan->name);
00201 ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",chan->name);
00202 res = -1;
00203 ast_frfree(f);
00204 break;
00205 }
00206 }
00207
00208 ast_frfree(f);
00209 }
00210 return res;
00211 }
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223 static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto)
00224 {
00225 int res = 0;
00226 int i = 0;
00227 int r;
00228 struct ast_frame *f;
00229 struct timeval lastdigittime;
00230
00231 lastdigittime = ast_tvnow();
00232 for (;;) {
00233
00234 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((i > 0) ? sdto : fdto)) {
00235 ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", chan->name);
00236 ast_debug(1,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name);
00237 res = 1;
00238 break;
00239 }
00240
00241 if ((r = ast_waitfor(chan, -1) < 0)) {
00242 ast_debug(1, "Waitfor returned %d\n", r);
00243 continue;
00244 }
00245
00246 f = ast_read(chan);
00247
00248 if (f == NULL) {
00249 res = -1;
00250 break;
00251 }
00252
00253
00254 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
00255 if (f->data.uint32) {
00256 chan->hangupcause = f->data.uint32;
00257 }
00258 ast_frfree(f);
00259 res = -1;
00260 break;
00261 }
00262
00263
00264 if (f->frametype != AST_FRAME_DTMF) {
00265 ast_frfree(f);
00266 continue;
00267 }
00268
00269 digit_string[i++] = f->subclass;
00270
00271 ast_frfree(f);
00272
00273
00274 if(i >= length)
00275 break;
00276
00277 lastdigittime = ast_tvnow();
00278 }
00279
00280 digit_string[i] = '\0';
00281 return res;
00282 }
00283
00284
00285
00286
00287 static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
00288 {
00289 int res = 0;
00290 struct timeval t;
00291 struct ast_tm now;
00292 char *cl,*cn;
00293 char workstring[80];
00294 char timestamp[80];
00295
00296
00297 if (chan->cid.cid_num)
00298 ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
00299 workstring[sizeof(workstring) - 1] = '\0';
00300
00301 ast_callerid_parse(workstring, &cn, &cl);
00302 if (cl)
00303 ast_shrink_phone_number(cl);
00304
00305
00306 t = ast_tvnow();
00307 ast_localtime(&t, &now, NULL);
00308
00309
00310 ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
00311
00312 res = fprintf(logfile, "\n\n[metadata]\n\n");
00313
00314 if (res >= 0)
00315 res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
00316
00317 if (res >= 0)
00318 res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "<unknown>" : cl);
00319
00320 if (res >- 0)
00321 res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "<unknown>" : cn);
00322
00323 if (res >= 0)
00324 res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
00325
00326 if (res >= 0)
00327 res = fprintf(logfile, "[events]\n\n");
00328
00329 if (res < 0) {
00330 ast_verb(3, "AlarmReceiver: can't write metadata\n");
00331 ast_debug(1,"AlarmReceiver: can't write metadata\n");
00332 } else
00333 res = 0;
00334
00335 return res;
00336 }
00337
00338
00339
00340
00341 static int write_event( FILE *logfile, event_node_t *event)
00342 {
00343 int res = 0;
00344
00345 if (fprintf(logfile, "%s\n", event->data) < 0)
00346 res = -1;
00347
00348 return res;
00349 }
00350
00351
00352
00353
00354
00355 static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event)
00356 {
00357
00358 int res = 0;
00359 char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
00360 int fd;
00361 FILE *logfile;
00362 event_node_t *elp = event;
00363
00364 if (!ast_strlen_zero(event_spool_dir)) {
00365
00366
00367 ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
00368 strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
00369
00370
00371 fd = mkstemp(workstring);
00372
00373 if (fd == -1) {
00374 ast_verb(3, "AlarmReceiver: can't make temporary file\n");
00375 ast_debug(1,"AlarmReceiver: can't make temporary file\n");
00376 res = -1;
00377 }
00378
00379 if (!res) {
00380 logfile = fdopen(fd, "w");
00381 if (logfile) {
00382
00383 res = write_metadata(logfile, signalling_type, chan);
00384 if (!res)
00385 while ((!res) && (elp != NULL)) {
00386 res = write_event(logfile, elp);
00387 elp = elp->next;
00388 }
00389 if (!res) {
00390 if (fflush(logfile) == EOF)
00391 res = -1;
00392 if (!res) {
00393 if (fclose(logfile) == EOF)
00394 res = -1;
00395 }
00396 }
00397 } else
00398 res = -1;
00399 }
00400 }
00401
00402 return res;
00403 }
00404
00405
00406
00407
00408
00409
00410 static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
00411 {
00412 int i, j;
00413 int res = 0;
00414 int checksum;
00415 char event[17];
00416 event_node_t *enew, *elp;
00417 int got_some_digits = 0;
00418 int events_received = 0;
00419 int ack_retries = 0;
00420
00421 static char digit_map[15] = "0123456789*#ABC";
00422 static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
00423
00424 database_increment("calls-received");
00425
00426
00427 ast_verb(4, "AlarmReceiver: Waiting for first event from panel\n");
00428
00429 while (res >= 0) {
00430 if (got_some_digits == 0) {
00431
00432 ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
00433 res = send_tone_burst(chan, 1400.0, 100, tldn);
00434 if (!res)
00435 res = ast_safe_sleep(chan, 100);
00436 if (!res) {
00437 ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
00438 res = send_tone_burst(chan, 2300.0, 100, tldn);
00439 }
00440 }
00441 if ( res >= 0)
00442 res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
00443 if (res < 0) {
00444 if (events_received == 0) {
00445
00446 database_increment("no-events-received");
00447 } else {
00448 if (ack_retries) {
00449 ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
00450 database_increment("ack-retries");
00451 }
00452 }
00453 ast_verb(4, "AlarmReceiver: App exiting...\n");
00454 res = -1;
00455 break;
00456 }
00457
00458 if (res != 0) {
00459
00460 ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
00461
00462 if (!got_some_digits) {
00463 got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
00464 ack_retries++;
00465 }
00466 continue;
00467 }
00468
00469 got_some_digits = 1;
00470
00471 ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
00472 ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
00473
00474
00475
00476 for (j = 0, checksum = 0; j < 16; j++) {
00477 for (i = 0; i < sizeof(digit_map); i++) {
00478 if (digit_map[i] == event[j])
00479 break;
00480 }
00481
00482 if (i == 16)
00483 break;
00484
00485 checksum += digit_weights[i];
00486 }
00487 if (i == 16) {
00488 ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
00489 continue;
00490 }
00491
00492
00493
00494 checksum = checksum % 15;
00495
00496 if (checksum) {
00497 database_increment("checksum-errors");
00498 ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
00499 ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
00500 continue;
00501 }
00502
00503
00504
00505 if (strncmp(event + 4, "18", 2)) {
00506 if (strncmp(event + 4, "98", 2)) {
00507 database_increment("format-errors");
00508 ast_verb(2, "AlarmReceiver: Wrong message type\n");
00509 ast_debug(1, "AlarmReceiver: Wrong message type\n");
00510 continue;
00511 }
00512 }
00513
00514 events_received++;
00515
00516
00517 if (!(enew = ast_calloc(1, sizeof(*enew)))) {
00518 res = -1;
00519 break;
00520 }
00521
00522 enew->next = NULL;
00523 ast_copy_string(enew->data, event, sizeof(enew->data));
00524
00525
00526
00527
00528 if (*ehead == NULL)
00529 *ehead = enew;
00530 else {
00531 for(elp = *ehead; elp->next != NULL; elp = elp->next)
00532 ;
00533 elp->next = enew;
00534 }
00535
00536 if (res > 0)
00537 res = 0;
00538
00539
00540 if ((res == 0) && (log_individual_events))
00541 res = log_events(chan, ADEMCO_CONTACT_ID, enew);
00542
00543 if (res == 0)
00544 res = ast_safe_sleep(chan, 200);
00545
00546
00547 if (res == 0)
00548 res = send_tone_burst(chan, 1400.0, 900, tldn);
00549 }
00550
00551 return res;
00552 }
00553
00554
00555
00556
00557
00558 static int alarmreceiver_exec(struct ast_channel *chan, void *data)
00559 {
00560 int res = 0;
00561 event_node_t *elp, *efree;
00562 char signalling_type[64] = "";
00563 event_node_t *event_head = NULL;
00564
00565
00566 ast_verb(4, "AlarmReceiver: Setting read and write formats to ULAW\n");
00567
00568 if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
00569 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
00570 return -1;
00571 }
00572
00573 if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
00574 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
00575 return -1;
00576 }
00577
00578
00579 ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
00580
00581
00582 ast_verb(4, "AlarmReceiver: Answering channel\n");
00583 if (chan->_state != AST_STATE_UP) {
00584 if ((res = ast_answer(chan)))
00585 return -1;
00586 }
00587
00588
00589 ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
00590 res = ast_safe_sleep(chan, 1250);
00591
00592
00593 if (!res) {
00594
00595
00596
00597 if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
00598 receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
00599 else
00600 res = -1;
00601 }
00602
00603
00604 if ((!res) && (log_individual_events == 0))
00605 res = log_events(chan, signalling_type, event_head);
00606
00607
00608
00609
00610 if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
00611 ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
00612 ast_safe_system(event_app);
00613 }
00614
00615
00616
00617
00618 for (elp = event_head; (elp != NULL);) {
00619 efree = elp;
00620 elp = elp->next;
00621 ast_free(efree);
00622 }
00623
00624 return 0;
00625 }
00626
00627
00628
00629
00630 static int load_config(void)
00631 {
00632 struct ast_config *cfg;
00633 const char *p;
00634 struct ast_flags config_flags = { 0 };
00635
00636
00637 cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
00638
00639 if (!cfg) {
00640 ast_verb(4, "AlarmReceiver: No config file\n");
00641 return 0;
00642 } else {
00643 p = ast_variable_retrieve(cfg, "general", "eventcmd");
00644 if (p) {
00645 ast_copy_string(event_app, p, sizeof(event_app));
00646 event_app[sizeof(event_app) - 1] = '\0';
00647 }
00648 p = ast_variable_retrieve(cfg, "general", "loudness");
00649 if (p) {
00650 toneloudness = atoi(p);
00651 if(toneloudness < 100)
00652 toneloudness = 100;
00653 if(toneloudness > 8192)
00654 toneloudness = 8192;
00655 }
00656 p = ast_variable_retrieve(cfg, "general", "fdtimeout");
00657 if (p) {
00658 fdtimeout = atoi(p);
00659 if(fdtimeout < 1000)
00660 fdtimeout = 1000;
00661 if(fdtimeout > 10000)
00662 fdtimeout = 10000;
00663 }
00664
00665 p = ast_variable_retrieve(cfg, "general", "sdtimeout");
00666 if (p) {
00667 sdtimeout = atoi(p);
00668 if(sdtimeout < 110)
00669 sdtimeout = 110;
00670 if(sdtimeout > 4000)
00671 sdtimeout = 4000;
00672 }
00673
00674 p = ast_variable_retrieve(cfg, "general", "logindividualevents");
00675 if (p)
00676 log_individual_events = ast_true(p);
00677
00678 p = ast_variable_retrieve(cfg, "general", "eventspooldir");
00679 if (p) {
00680 ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
00681 event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
00682 }
00683
00684 p = ast_variable_retrieve(cfg, "general", "timestampformat");
00685 if (p) {
00686 ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
00687 time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
00688 }
00689
00690 p = ast_variable_retrieve(cfg, "general", "db-family");
00691 if (p) {
00692 ast_copy_string(db_family, p, sizeof(db_family));
00693 db_family[sizeof(db_family) - 1] = '\0';
00694 }
00695 ast_config_destroy(cfg);
00696 }
00697 return 1;
00698 }
00699
00700
00701
00702
00703 static int unload_module(void)
00704 {
00705 return ast_unregister_application(app);
00706 }
00707
00708 static int load_module(void)
00709 {
00710 if (load_config()) {
00711 if (ast_register_application(app, alarmreceiver_exec, synopsis, descrip))
00712 return AST_MODULE_LOAD_FAILURE;
00713 return AST_MODULE_LOAD_SUCCESS;
00714 } else
00715 return AST_MODULE_LOAD_DECLINE;
00716 }
00717
00718 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");