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