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