56f604f2774fdf1b7d8b747181202170e351c612
[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                         if (option_debug)
255                                 ast_log(LOG_DEBUG,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name);
256                                 
257                         res = 1;
258                         break;
259                 }
260                 
261                 if ((r = ast_waitfor(chan, -1) < 0)) {
262                         if (option_debug)
263                                 ast_log(LOG_DEBUG, "Waitfor returned %d\n", r);
264                         continue;
265                 }
266                         
267                 f = ast_read(chan);
268                 
269                 if (f == NULL){
270                         res = -1;
271                         break;
272                 }
273                 
274                 /* If they hung up, leave */
275                 if ((f->frametype == AST_FRAME_CONTROL) &&
276                     (f->subclass == AST_CONTROL_HANGUP)){
277                         ast_frfree(f);
278                         res = -1;
279                         break;
280                 }
281                 
282                 /* if not DTMF, just do it again */
283                 if (f->frametype != AST_FRAME_DTMF){
284                         ast_frfree(f);
285                         continue;
286                 }
287
288                 digit_string[i++] = f->subclass;  /* save digit */
289                 
290                 ast_frfree(f);
291                 
292                 /* If we have all the digits we expect, leave */
293                 if(i >= length)
294                         break;
295                 
296                 lastdigittime = ast_tvnow();
297         }
298         
299         digit_string[i] = '\0'; /* Nul terminate the end of the digit string */
300         return res;
301
302 }
303
304 /*
305 * Write the metadata to the log file
306 */
307
308 static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
309 {
310         int res = 0;
311         time_t t;
312         struct tm now;
313         char *cl,*cn;
314         char workstring[80];
315         char timestamp[80];
316         
317         /* Extract the caller ID location */
318         if (chan->cid.cid_num)
319                 ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
320         workstring[sizeof(workstring) - 1] = '\0';
321         
322         ast_callerid_parse(workstring, &cn, &cl);
323         if (cl) 
324                 ast_shrink_phone_number(cl);
325                 
326
327         /* Get the current time */
328                 
329         time(&t);
330         ast_localtime(&t, &now, NULL);
331         
332         /* Format the time */
333         
334         strftime(timestamp, sizeof(timestamp), time_stamp_format, &now); 
335
336         
337         res = fprintf(logfile, "\n\n[metadata]\n\n");
338         
339         if(res >= 0)
340                 res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
341                 
342         if(res >= 0)    
343                 res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "<unknown>" : cl);
344                 
345         if(res >- 0)
346                 res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "<unknown>" : cn);
347                 
348         if(res >= 0)
349                 res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
350         
351         if(res >= 0)
352                 res = fprintf(logfile, "[events]\n\n");
353         
354         if(res < 0){
355                 if (option_verbose >= 3 )
356                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't write metadata\n");  
357                 
358                 if (option_debug)
359                         ast_log(LOG_DEBUG,"AlarmReceiver: can't write metadata\n");
360         }
361         else
362                 res = 0;
363
364         return res;
365 }
366
367 /*
368 * Write a single event to the log file
369 */
370
371 static int write_event( FILE *logfile,  event_node_t *event)
372 {
373         int res = 0;
374
375         if( fprintf(logfile, "%s\n", event->data) < 0)
376                 res = -1;
377                         
378         return res;
379 }
380
381
382 /*
383 * If we are configured to log events, do so here.
384 *
385 */
386
387 static int log_events(struct ast_channel *chan,  char *signalling_type, event_node_t *event)
388 {
389
390         int res = 0;
391         char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
392         int fd;
393         FILE *logfile;
394         event_node_t *elp = event;
395         
396         if (!ast_strlen_zero(event_spool_dir)) {
397                 
398                 /* Make a template */
399                 
400                 ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
401                 strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
402                 
403                 /* Make the temporary file */
404                 
405                 fd = mkstemp(workstring);
406                 
407                 if(fd == -1) {
408                         if (option_verbose >= 3)
409                                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't make temporary file\n");     
410                         if (option_debug)
411                                 ast_log(LOG_DEBUG,"AlarmReceiver: can't make temporary file\n");
412                         res = -1;
413                 }
414                 
415                 if(!res){
416                         logfile = fdopen(fd, "w");
417                         if(logfile){
418                                 /* Write the file */
419                                 res = write_metadata(logfile, signalling_type, chan);
420                                 if(!res)
421                                         while((!res) && (elp != NULL)){
422                                                 res = write_event(logfile, elp);
423                                                 elp = elp->next;
424                                         }
425                                 if(!res){
426                                         if(fflush(logfile) == EOF)
427                                                 res = -1;
428                                         if(!res){
429                                                 if(fclose(logfile) == EOF)
430                                                         res = -1;
431                                         }                               
432                                 }
433                         }
434                         else
435                                 res = -1;
436                 }
437         }
438
439         return res;     
440 }
441
442 /*
443 * This function implements the logic to receive the Ademco contact ID  format.
444 *
445 * The function will return 0 when the caller hangs up, else a -1 if there was a problem.
446 */
447
448 static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
449 {
450         int i,j;
451         int res = 0;
452         int checksum;
453         char event[17];
454         event_node_t *enew, *elp;
455         int got_some_digits = 0;
456         int events_received = 0;
457         int ack_retries = 0;
458         
459         static char digit_map[15] = "0123456789*#ABC";
460         static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
461                                                                                                                       
462         database_increment("calls-received");
463
464         /* Wait for first event */
465
466         if(option_verbose >= 4)
467                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for first event from panel\n");
468
469         while(res >= 0){
470
471                 if(got_some_digits == 0){
472
473                         /* Send ACK tone sequence */
474                         
475                                                                                                                                     
476                         if(option_verbose >= 4)
477                                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
478                                                                                                                                             
479                                                                                                                                             
480                         res = send_tone_burst(chan, 1400.0, 100, tldn);
481                                                                                                                                             
482                         if(!res)
483                                 res = ast_safe_sleep(chan, 100);
484                                                                                                                                             
485                         if(!res){
486                                 if(option_verbose >= 4)
487                                         ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
488                                                                                                                                             
489                                 res = send_tone_burst(chan, 2300.0, 100, tldn);
490                         }
491                                                                                                                                             
492                 }
493
494                 if( res >= 0)
495                         res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
496                 
497                 if (res < 0){
498                 
499                         if(events_received == 0)
500                                 /* Hangup with no events received should be logged in the DB */
501                                 database_increment("no-events-received");
502                         else{
503                                 if(ack_retries){
504                                         if(option_verbose >= 4)
505                                                 ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
506                                         
507                                         database_increment("ack-retries");
508                                 }
509                         }
510                         if(option_verbose >= 4)
511                                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: App exiting...\n");
512                         res = -1;
513                         break;
514                 }
515                 
516                 if(res != 0){
517                          /* Didn't get all of the digits */
518                         if(option_verbose >= 2)
519                                 ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
520
521                         if(!got_some_digits){
522                                 got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
523                                 ack_retries++;
524                         }
525                         continue;       
526                 }               
527                 
528                 got_some_digits = 1;
529
530                 if (option_verbose >= 2)
531                         ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Received Event %s\n", event);
532                 if (option_debug)
533                         ast_log(LOG_DEBUG, "AlarmReceiver: Received event: %s\n", event);
534                 
535                 /* Calculate checksum */
536                 
537                 for(j = 0, checksum = 0; j < 16; j++){
538                         for(i = 0 ; i < sizeof(digit_map) ; i++){
539                                 if(digit_map[i] == event[j])
540                                         break;
541                         }
542                         
543                         if(i == 16)
544                                 break;
545                                 
546                         checksum += digit_weights[i];
547                 }
548                 
549                 if(i == 16){
550                         if(option_verbose >= 2)
551                                 ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
552                         continue; /* Bad character */
553                 }
554
555                 /* Checksum is mod(15) of the total */
556
557                 checksum = checksum % 15;
558
559                 if (checksum) {
560                         database_increment("checksum-errors");
561                         if (option_verbose >= 2)
562                                 ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Nonzero checksum\n");
563                         if (option_debug)
564                                 ast_log(LOG_DEBUG, "AlarmReceiver: Nonzero checksum\n");
565                         continue;
566                 }
567
568                 /* Check the message type for correctness */
569
570                 if(strncmp(event + 4, "18", 2)){
571                         if(strncmp(event + 4, "98", 2)){
572                                 database_increment("format-errors");
573                                 if(option_verbose >= 2)
574                                         ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Wrong message type\n");
575                                 if (option_debug)
576                                         ast_log(LOG_DEBUG, "AlarmReceiver: Wrong message type\n");
577                         continue;
578                         }
579                 }
580
581                 events_received++;
582                 
583                 /* Queue the Event */           
584                 if (!(enew = ast_calloc(1, sizeof(*enew)))) {
585                         res = -1;
586                         break;
587                 }
588                 
589                 enew->next = NULL;
590                 ast_copy_string(enew->data, event, sizeof(enew->data));
591
592                 /*
593                 * Insert event onto end of list
594                 */
595                 
596                 if(*ehead == NULL){
597                         *ehead = enew;
598                 }
599                 else{
600                         for(elp = *ehead; elp->next != NULL; elp = elp->next)
601                         ;
602                         
603                         elp->next = enew;
604                 }
605                 
606                 if(res > 0)
607                         res = 0;
608                 
609                 /* Let the user have the option of logging the single event before sending the kissoff tone */
610
611                 if((res == 0) && (log_individual_events))
612                         res = log_events(chan, ADEMCO_CONTACT_ID, enew);
613         
614                 /* Wait 200 msec before sending kissoff */      
615                         
616                 if(res == 0)    
617                         res = ast_safe_sleep(chan, 200);
618
619                 /* Send the kissoff tone */
620
621                 if(res == 0)            
622                         res = send_tone_burst(chan, 1400.0, 900, tldn);
623         }
624
625         
626         return res;
627 }
628
629
630 /*
631 * This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
632 * This function will always return 0.
633 */
634
635 static int alarmreceiver_exec(struct ast_channel *chan, void *data)
636 {
637         int res = 0;
638         struct ast_module_user *u;
639         event_node_t *elp, *efree;
640         char signalling_type[64] = "";
641
642         event_node_t *event_head = NULL;
643
644         u = ast_module_user_add(chan);
645
646         /* Set write and read formats to ULAW */
647
648         if(option_verbose >= 4)
649                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Setting read and write formats to ULAW\n");
650
651         if (ast_set_write_format(chan,AST_FORMAT_ULAW)){
652                 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
653                 ast_module_user_remove(u);
654                 return -1;
655         }
656         
657         if (ast_set_read_format(chan,AST_FORMAT_ULAW)){
658                 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
659                 ast_module_user_remove(u);
660                 return -1;
661         }
662
663         /* Set default values for this invocation of the application */
664         
665         ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
666
667
668         /* Answer the channel if it is not already */
669
670         if(option_verbose >= 4)
671                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Answering channel\n");
672
673         if (chan->_state != AST_STATE_UP) {
674         
675                 res = ast_answer(chan);
676                 
677                 if (res) {
678                         ast_module_user_remove(u);
679                         return -1;
680                 }
681         }
682
683         /* Wait for the connection to settle post-answer */
684
685         if(option_verbose >= 4)
686                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for connection to stabilize\n");
687
688         res = ast_safe_sleep(chan, 1250);
689
690         /* Attempt to receive the events */
691
692         if(!res){
693         
694                 /* Determine the protocol to receive in advance */
695                 /* Note: Ademco contact is the only one supported at this time */
696                 /* Others may be added later */
697                 
698                 if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
699                         receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
700                 else
701                         res = -1;
702         }
703         
704                 
705         
706         /* Events queued by receiver, write them all out here if so configured */
707
708         if((!res) && (log_individual_events == 0)){
709                 res = log_events(chan, signalling_type, event_head);
710
711         }
712
713         /*
714         * Do we exec a command line at the end?
715         */
716         
717         if((!res) && (!ast_strlen_zero(event_app)) && (event_head)){
718                 if (option_debug)
719                         ast_log(LOG_DEBUG,"Alarmreceiver: executing: %s\n", event_app);
720                 ast_safe_system(event_app);
721         }
722
723         /*
724         * Free up the data allocated in our linked list
725         */
726                 
727         for(elp = event_head; (elp != NULL);){
728                 efree = elp;
729                 elp = elp->next;
730                 ast_free(efree);
731         }
732
733
734         ast_module_user_remove(u);
735
736         return 0;
737 }
738
739 /* 
740 * Load the configuration from the configuration file
741 */
742
743 static int load_config(void)
744 {
745         struct ast_config *cfg;
746         const char *p;
747
748         /* Read in the config file */
749
750         cfg = ast_config_load(ALMRCV_CONFIG);
751                                                                                                                                   
752         if(!cfg){
753         
754                 if(option_verbose >= 4)
755                         ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: No config file\n");
756                 return 0;
757         }
758         else{
759
760                 
761                 p = ast_variable_retrieve(cfg, "general", "eventcmd");
762                 
763                 if(p){
764                         ast_copy_string(event_app, p, sizeof(event_app));
765                         event_app[sizeof(event_app) - 1] = '\0';
766                 }
767                 
768                 p = ast_variable_retrieve(cfg, "general", "loudness");
769                 if(p){
770                         toneloudness = atoi(p);
771                         if(toneloudness < 100)
772                                 toneloudness = 100;
773                         if(toneloudness > 8192)
774                                 toneloudness = 8192;
775                 }
776                 p = ast_variable_retrieve(cfg, "general", "fdtimeout");
777                 if(p){
778                         fdtimeout = atoi(p);
779                         if(fdtimeout < 1000)
780                                 fdtimeout = 1000;
781                         if(fdtimeout > 10000)
782                                 fdtimeout = 10000;      
783                 }
784                 
785                 p = ast_variable_retrieve(cfg, "general", "sdtimeout");
786                 if(p){
787                         sdtimeout = atoi(p);
788                         if(sdtimeout < 110)
789                                 sdtimeout = 110;
790                         if(sdtimeout > 4000)
791                                 sdtimeout = 4000;                       
792
793                 }
794                 
795                 p = ast_variable_retrieve(cfg, "general", "logindividualevents");
796                 if(p){
797                         log_individual_events = ast_true(p);
798
799                 }
800                 
801                 p = ast_variable_retrieve(cfg, "general", "eventspooldir");
802                         
803                 if(p){
804                         ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
805                         event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
806                 }
807                 
808                 p = ast_variable_retrieve(cfg, "general", "timestampformat");
809                         
810                 if(p){
811                         ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
812                         time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
813                 }
814
815                 p = ast_variable_retrieve(cfg, "general", "db-family");
816                                                                                                                                             
817                 if(p){
818                         ast_copy_string(db_family, p, sizeof(db_family));
819                         db_family[sizeof(db_family) - 1] = '\0';
820                 }
821                 ast_config_destroy(cfg);
822         }
823         return 1;
824
825 }
826
827 /*
828 * These functions are required to implement an Asterisk App.
829 */
830
831
832 static int unload_module(void)
833 {
834         int res;
835
836         res = ast_unregister_application(app);
837
838         ast_module_user_hangup_all();
839
840         return res;
841 }
842
843 static int load_module(void)
844 {
845         if(load_config())
846                 return ast_register_application(app, alarmreceiver_exec, synopsis, descrip);
847         else
848                 return AST_MODULE_LOAD_DECLINE;
849 }
850
851 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");