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