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