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