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: 102906 $")
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, "%u", &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 = 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 ast_frfree(f);
00256 res = -1;
00257 break;
00258 }
00259
00260
00261 if (f->frametype != AST_FRAME_DTMF) {
00262 ast_frfree(f);
00263 continue;
00264 }
00265
00266 digit_string[i++] = f->subclass;
00267
00268 ast_frfree(f);
00269
00270
00271 if(i >= length)
00272 break;
00273
00274 lastdigittime = ast_tvnow();
00275 }
00276
00277 digit_string[i] = '\0';
00278 return res;
00279 }
00280
00281
00282
00283
00284 static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
00285 {
00286 int res = 0;
00287 struct timeval t;
00288 struct ast_tm now;
00289 char *cl,*cn;
00290 char workstring[80];
00291 char timestamp[80];
00292
00293
00294 if (chan->cid.cid_num)
00295 ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
00296 workstring[sizeof(workstring) - 1] = '\0';
00297
00298 ast_callerid_parse(workstring, &cn, &cl);
00299 if (cl)
00300 ast_shrink_phone_number(cl);
00301
00302
00303 t = ast_tvnow();
00304 ast_localtime(&t, &now, NULL);
00305
00306
00307 ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
00308
00309 res = fprintf(logfile, "\n\n[metadata]\n\n");
00310
00311 if (res >= 0)
00312 res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
00313
00314 if (res >= 0)
00315 res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "<unknown>" : cl);
00316
00317 if (res >- 0)
00318 res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "<unknown>" : cn);
00319
00320 if (res >= 0)
00321 res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
00322
00323 if (res >= 0)
00324 res = fprintf(logfile, "[events]\n\n");
00325
00326 if (res < 0) {
00327 ast_verb(3, "AlarmReceiver: can't write metadata\n");
00328 ast_debug(1,"AlarmReceiver: can't write metadata\n");
00329 } else
00330 res = 0;
00331
00332 return res;
00333 }
00334
00335
00336
00337
00338 static int write_event( FILE *logfile, event_node_t *event)
00339 {
00340 int res = 0;
00341
00342 if (fprintf(logfile, "%s\n", event->data) < 0)
00343 res = -1;
00344
00345 return res;
00346 }
00347
00348
00349
00350
00351
00352 static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event)
00353 {
00354
00355 int res = 0;
00356 char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
00357 int fd;
00358 FILE *logfile;
00359 event_node_t *elp = event;
00360
00361 if (!ast_strlen_zero(event_spool_dir)) {
00362
00363
00364 ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
00365 strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
00366
00367
00368 fd = mkstemp(workstring);
00369
00370 if (fd == -1) {
00371 ast_verb(3, "AlarmReceiver: can't make temporary file\n");
00372 ast_debug(1,"AlarmReceiver: can't make temporary file\n");
00373 res = -1;
00374 }
00375
00376 if (!res) {
00377 logfile = fdopen(fd, "w");
00378 if (logfile) {
00379
00380 res = write_metadata(logfile, signalling_type, chan);
00381 if (!res)
00382 while ((!res) && (elp != NULL)) {
00383 res = write_event(logfile, elp);
00384 elp = elp->next;
00385 }
00386 if (!res) {
00387 if (fflush(logfile) == EOF)
00388 res = -1;
00389 if (!res) {
00390 if (fclose(logfile) == EOF)
00391 res = -1;
00392 }
00393 }
00394 } else
00395 res = -1;
00396 }
00397 }
00398
00399 return res;
00400 }
00401
00402
00403
00404
00405
00406
00407 static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
00408 {
00409 int i, j;
00410 int res = 0;
00411 int checksum;
00412 char event[17];
00413 event_node_t *enew, *elp;
00414 int got_some_digits = 0;
00415 int events_received = 0;
00416 int ack_retries = 0;
00417
00418 static char digit_map[15] = "0123456789*#ABC";
00419 static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
00420
00421 database_increment("calls-received");
00422
00423
00424 ast_verb(4, "AlarmReceiver: Waiting for first event from panel\n");
00425
00426 while (res >= 0) {
00427 if (got_some_digits == 0) {
00428
00429 ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
00430 res = send_tone_burst(chan, 1400.0, 100, tldn);
00431 if (!res)
00432 res = ast_safe_sleep(chan, 100);
00433 if (!res) {
00434 ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
00435 res = send_tone_burst(chan, 2300.0, 100, tldn);
00436 }
00437 }
00438 if ( res >= 0)
00439 res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
00440 if (res < 0) {
00441 if (events_received == 0) {
00442
00443 database_increment("no-events-received");
00444 } else {
00445 if (ack_retries) {
00446 ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
00447 database_increment("ack-retries");
00448 }
00449 }
00450 ast_verb(4, "AlarmReceiver: App exiting...\n");
00451 res = -1;
00452 break;
00453 }
00454
00455 if (res != 0) {
00456
00457 ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
00458
00459 if (!got_some_digits) {
00460 got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
00461 ack_retries++;
00462 }
00463 continue;
00464 }
00465
00466 got_some_digits = 1;
00467
00468 ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
00469 ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
00470
00471
00472
00473 for (j = 0, checksum = 0; j < 16; j++) {
00474 for (i = 0; i < sizeof(digit_map); i++) {
00475 if (digit_map[i] == event[j])
00476 break;
00477 }
00478
00479 if (i == 16)
00480 break;
00481
00482 checksum += digit_weights[i];
00483 }
00484 if (i == 16) {
00485 ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
00486 continue;
00487 }
00488
00489
00490
00491 checksum = checksum % 15;
00492
00493 if (checksum) {
00494 database_increment("checksum-errors");
00495 ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
00496 ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
00497 continue;
00498 }
00499
00500
00501
00502 if (strncmp(event + 4, "18", 2)) {
00503 if (strncmp(event + 4, "98", 2)) {
00504 database_increment("format-errors");
00505 ast_verb(2, "AlarmReceiver: Wrong message type\n");
00506 ast_debug(1, "AlarmReceiver: Wrong message type\n");
00507 continue;
00508 }
00509 }
00510
00511 events_received++;
00512
00513
00514 if (!(enew = ast_calloc(1, sizeof(*enew)))) {
00515 res = -1;
00516 break;
00517 }
00518
00519 enew->next = NULL;
00520 ast_copy_string(enew->data, event, sizeof(enew->data));
00521
00522
00523
00524
00525 if (*ehead == NULL)
00526 *ehead = enew;
00527 else {
00528 for(elp = *ehead; elp->next != NULL; elp = elp->next)
00529 ;
00530 elp->next = enew;
00531 }
00532
00533 if (res > 0)
00534 res = 0;
00535
00536
00537 if ((res == 0) && (log_individual_events))
00538 res = log_events(chan, ADEMCO_CONTACT_ID, enew);
00539
00540 if (res == 0)
00541 res = ast_safe_sleep(chan, 200);
00542
00543
00544 if (res == 0)
00545 res = send_tone_burst(chan, 1400.0, 900, tldn);
00546 }
00547
00548 return res;
00549 }
00550
00551
00552
00553
00554
00555 static int alarmreceiver_exec(struct ast_channel *chan, void *data)
00556 {
00557 int res = 0;
00558 event_node_t *elp, *efree;
00559 char signalling_type[64] = "";
00560 event_node_t *event_head = NULL;
00561
00562
00563 ast_verb(4, "AlarmReceiver: Setting read and write formats to ULAW\n");
00564
00565 if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
00566 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
00567 return -1;
00568 }
00569
00570 if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
00571 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
00572 return -1;
00573 }
00574
00575
00576 ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
00577
00578
00579 ast_verb(4, "AlarmReceiver: Answering channel\n");
00580 if (chan->_state != AST_STATE_UP) {
00581 if ((res = ast_answer(chan)))
00582 return -1;
00583 }
00584
00585
00586 ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
00587 res = ast_safe_sleep(chan, 1250);
00588
00589
00590 if (!res) {
00591
00592
00593
00594 if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
00595 receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
00596 else
00597 res = -1;
00598 }
00599
00600
00601 if ((!res) && (log_individual_events == 0))
00602 res = log_events(chan, signalling_type, event_head);
00603
00604
00605
00606
00607 if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
00608 ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
00609 ast_safe_system(event_app);
00610 }
00611
00612
00613
00614
00615 for (elp = event_head; (elp != NULL);) {
00616 efree = elp;
00617 elp = elp->next;
00618 ast_free(efree);
00619 }
00620
00621 return 0;
00622 }
00623
00624
00625
00626
00627 static int load_config(void)
00628 {
00629 struct ast_config *cfg;
00630 const char *p;
00631 struct ast_flags config_flags = { 0 };
00632
00633
00634 cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
00635
00636 if (!cfg) {
00637 ast_verb(4, "AlarmReceiver: No config file\n");
00638 return 0;
00639 } else {
00640 p = ast_variable_retrieve(cfg, "general", "eventcmd");
00641 if (p) {
00642 ast_copy_string(event_app, p, sizeof(event_app));
00643 event_app[sizeof(event_app) - 1] = '\0';
00644 }
00645 p = ast_variable_retrieve(cfg, "general", "loudness");
00646 if (p) {
00647 toneloudness = atoi(p);
00648 if(toneloudness < 100)
00649 toneloudness = 100;
00650 if(toneloudness > 8192)
00651 toneloudness = 8192;
00652 }
00653 p = ast_variable_retrieve(cfg, "general", "fdtimeout");
00654 if (p) {
00655 fdtimeout = atoi(p);
00656 if(fdtimeout < 1000)
00657 fdtimeout = 1000;
00658 if(fdtimeout > 10000)
00659 fdtimeout = 10000;
00660 }
00661
00662 p = ast_variable_retrieve(cfg, "general", "sdtimeout");
00663 if (p) {
00664 sdtimeout = atoi(p);
00665 if(sdtimeout < 110)
00666 sdtimeout = 110;
00667 if(sdtimeout > 4000)
00668 sdtimeout = 4000;
00669 }
00670
00671 p = ast_variable_retrieve(cfg, "general", "logindividualevents");
00672 if (p)
00673 log_individual_events = ast_true(p);
00674
00675 p = ast_variable_retrieve(cfg, "general", "eventspooldir");
00676 if (p) {
00677 ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
00678 event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
00679 }
00680
00681 p = ast_variable_retrieve(cfg, "general", "timestampformat");
00682 if (p) {
00683 ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
00684 time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
00685 }
00686
00687 p = ast_variable_retrieve(cfg, "general", "db-family");
00688 if (p) {
00689 ast_copy_string(db_family, p, sizeof(db_family));
00690 db_family[sizeof(db_family) - 1] = '\0';
00691 }
00692 ast_config_destroy(cfg);
00693 }
00694 return 1;
00695 }
00696
00697
00698
00699
00700 static int unload_module(void)
00701 {
00702 return ast_unregister_application(app);
00703 }
00704
00705 static int load_module(void)
00706 {
00707 if (load_config()) {
00708 if (ast_register_application(app, alarmreceiver_exec, synopsis, descrip))
00709 return AST_MODULE_LOAD_FAILURE;
00710 return AST_MODULE_LOAD_SUCCESS;
00711 } else
00712 return AST_MODULE_LOAD_DECLINE;
00713 }
00714
00715 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");