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