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