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