Merge rgagnon's pedantic string checks (apps a-m, bug #2035)
[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), "%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                 strncpy(workstring, event_spool_dir, sizeof(workstring) - 1);
402                 strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
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                 memset(enew, 0, sizeof(event_node_t));
591                 
592                 enew->next = NULL;
593                 strncpy(enew->data, event, sizeof(enew->data) - 1);
594
595                 /*
596                 * Insert event onto end of list
597                 */
598                 
599                 if(*ehead == NULL){
600                         *ehead = enew;
601                 }
602                 else{
603                         for(elp = *ehead; elp->next != NULL; elp = elp->next)
604                         ;
605                         
606                         elp->next = enew;
607                 }
608                 
609                 if(res > 0)
610                         res = 0;
611                 
612                 /* Let the user have the option of logging the single event before sending the kissoff tone */
613
614                 if((res == 0) && (log_individual_events))
615                         res = log_events(chan, ADEMCO_CONTACT_ID, enew);
616         
617                 /* Wait 200 msec before sending kissoff */      
618                         
619                 if(res == 0)    
620                         res = ast_safe_sleep(chan, 200);
621
622                 /* Send the kissoff tone */
623
624                 if(res == 0)            
625                         res = send_tone_burst(chan, 1400.0, 900, tldn);
626         }
627
628         
629         return res;
630 }
631
632
633 /*
634 * This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
635 * This function will always return 0.
636 */
637
638 static int alarmreceiver_exec(struct ast_channel *chan, void *data)
639 {
640         int res = 0;
641         struct localuser *u;
642         event_node_t *elp, *efree;
643         char signalling_type[64] = "";
644
645         event_node_t *event_head = NULL;
646
647         LOCAL_USER_ADD(u);
648
649         /* Set write and read formats to ULAW */
650
651         if(option_verbose >= 4)
652                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Setting read and write formats to ULAW\n");
653
654         if (ast_set_write_format(chan,AST_FORMAT_ULAW)){
655                 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
656                 return -1;
657         }
658         
659         if (ast_set_read_format(chan,AST_FORMAT_ULAW)){
660                 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
661                 return -1;
662         }
663
664         /* Set default values for this invokation of the application */
665         
666         strncpy(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type) - 1);
667
668
669         /* Answer the channel if it is not already */
670
671         if(option_verbose >= 4)
672                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Answering channel\n");
673
674         if (chan->_state != AST_STATE_UP) {
675         
676                 res = ast_answer(chan);
677                 
678                 if (res) {
679                         LOCAL_USER_REMOVE(u);
680                         return -1;
681                 }
682         }
683
684         /* Wait for the connection to settle post-answer */
685
686         if(option_verbose >= 4)
687                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for connection to stabilize\n");
688
689         res = ast_safe_sleep(chan, 1250);
690
691         /* Attempt to receive the events */
692
693         if(!res){
694         
695                 /* Determine the protocol to receive in advance */
696                 /* Note: Ademco contact is the only one supported at this time */
697                 /* Others may be added later */
698                 
699                 if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
700                         receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
701                 else
702                         res = -1;
703         }
704         
705                 
706         
707         /* Events queued by receiver, write them all out here if so configured */
708
709         if((!res) && (log_individual_events == 0)){
710                 res = log_events(chan, signalling_type, event_head);
711
712         }
713
714         /*
715         * Do we exec a command line at the end?
716         */
717         
718         if((!res) && (strlen(event_app)) && (event_head)){
719                 ast_log(LOG_DEBUG,"Alarmreceiver: executing: %s\n", event_app);
720                 ast_safe_system(event_app);
721         }
722
723         /*
724         * Free up the data allocated in our linked list
725         */
726                 
727         for(elp = event_head; (elp != NULL);){
728                 efree = elp;
729                 elp = elp->next;
730                 free(efree);
731         }
732
733
734         LOCAL_USER_REMOVE(u);
735
736         return 0;
737 }
738
739 /* 
740 * Load the configuration from the configuration file
741 */
742
743 static int load_config(void)
744 {
745         struct ast_config *cfg;
746         char *p;
747
748         /* Read in the config file */
749
750         cfg = ast_load(ALMRCV_CONFIG);
751                                                                                                                                   
752         if(!cfg){
753         
754                 if(option_verbose >= 4)
755                         ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: No config file\n");
756         }
757         else{
758
759                 
760                 p = ast_variable_retrieve(cfg, "general", "eventcmd");
761                 
762                 if(p){
763                         strncpy(event_app, p, sizeof(event_app) - 1);
764                         event_app[sizeof(event_app) - 1] = '\0';
765                 }
766                 
767                 p = ast_variable_retrieve(cfg, "general", "loudness");
768                 if(p){
769                         toneloudness = atoi(p);
770                         if(toneloudness < 100)
771                                 toneloudness = 100;
772                         if(toneloudness > 8192)
773                                 toneloudness = 8192;
774                 }
775                 p = ast_variable_retrieve(cfg, "general", "fdtimeout");
776                 if(p){
777                         fdtimeout = atoi(p);
778                         if(fdtimeout < 1000)
779                                 fdtimeout = 1000;
780                         if(fdtimeout > 10000)
781                                 fdtimeout = 10000;      
782                 }
783                 
784                 p = ast_variable_retrieve(cfg, "general", "sdtimeout");
785                 if(p){
786                         sdtimeout = atoi(p);
787                         if(sdtimeout < 110)
788                                 sdtimeout = 110;
789                         if(sdtimeout > 4000)
790                                 sdtimeout = 4000;                       
791
792                 }
793                 
794                 p = ast_variable_retrieve(cfg, "general", "logindividualevents");
795                 if(p){
796                         log_individual_events = ast_true(p);
797
798                 }
799                 
800                 p = ast_variable_retrieve(cfg, "general", "eventspooldir");
801                         
802                 if(p){
803                         strncpy(event_spool_dir, p, sizeof(event_spool_dir) - 1);
804                         event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
805                 }
806                 
807                 p = ast_variable_retrieve(cfg, "general", "timestampformat");
808                         
809                 if(p){
810                         strncpy(time_stamp_format, p, sizeof(time_stamp_format) - 1);
811                         time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
812                 }
813
814                 p = ast_variable_retrieve(cfg, "general", "db-family");
815                                                                                                                                             
816                 if(p){
817                         strncpy(db_family, p, sizeof(db_family) - 1);
818                         db_family[sizeof(db_family) - 1] = '\0';
819                 }
820
821         }
822         return 0;
823
824 }
825
826 /*
827 * These functions are required to implement an Asterisk App.
828 */
829
830
831 int unload_module(void)
832 {
833         STANDARD_HANGUP_LOCALUSERS;
834         return ast_unregister_application(app);
835 }
836
837 int load_module(void)
838 {
839         load_config();
840         return ast_register_application(app, alarmreceiver_exec, synopsis, descrip);
841 }
842
843 char *description(void)
844 {
845         return tdesc;
846 }
847
848 int usecount(void)
849 {
850         int res;
851         STANDARD_USECOUNT(res);
852         return res;
853 }
854
855 char *key()
856 {
857         return ASTERISK_GPL_KEY;
858 }