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