Merge "astobj2: Create function to copy weak proxied objects from container."
[asterisk/asterisk.git] / apps / app_amd.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2003 - 2006, Aheeva Technology.
5  *
6  * Claude Klimos (claude.klimos@aheeva.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  * A license has been granted to Digium (via disclaimer) for the use of
19  * this code.
20  */
21
22 /*! \file
23  *
24  * \brief Answering machine detection
25  *
26  * \author Claude Klimos (claude.klimos@aheeva.com)
27  *
28  * \ingroup applications
29  */
30
31 /*! \li \ref app_amd.c uses the configuration file \ref amd.conf
32  * \addtogroup configuration_file Configuration Files
33  */
34
35 /*!
36  * \page amd.conf amd.conf
37  * \verbinclude amd.conf.sample
38  */
39
40 /*** MODULEINFO
41         <support_level>extended</support_level>
42  ***/
43
44 #include "asterisk.h"
45
46 #include "asterisk/module.h"
47 #include "asterisk/lock.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/dsp.h"
50 #include "asterisk/pbx.h"
51 #include "asterisk/config.h"
52 #include "asterisk/app.h"
53 #include "asterisk/format_cache.h"
54
55 /*** DOCUMENTATION
56         <application name="AMD" language="en_US">
57                 <synopsis>
58                         Attempt to detect answering machines.
59                 </synopsis>
60                 <syntax>
61                         <parameter name="initialSilence" required="false">
62                                 <para>Is maximum initial silence duration before greeting.</para>
63                                 <para>If this is exceeded, the result is detection as a MACHINE</para>
64                         </parameter>
65                         <parameter name="greeting" required="false">
66                                 <para>is the maximum length of a greeting.</para>
67                                 <para>If this is exceeded, the result is detection as a MACHINE</para>
68                         </parameter>
69                         <parameter name="afterGreetingSilence" required="false">
70                                 <para>Is the silence after detecting a greeting.</para>
71                                 <para>If this is exceeded, the result is detection as a HUMAN</para>
72                         </parameter>
73                         <parameter name="totalAnalysis Time" required="false">
74                                 <para>Is the maximum time allowed for the algorithm</para>
75                                 <para>to decide on whether the audio represents a HUMAN, or a MACHINE</para>
76                         </parameter>
77                         <parameter name="miniumWordLength" required="false">
78                                 <para>Is the minimum duration of Voice considered to be a word</para>
79                         </parameter>
80                         <parameter name="betweenWordSilence" required="false">
81                                 <para>Is the minimum duration of silence after a word to
82                                 consider the audio that follows to be a new word</para>
83                         </parameter>
84                         <parameter name="maximumNumberOfWords" required="false">
85                                 <para>Is the maximum number of words in a greeting</para>
86                                 <para>If this is exceeded, then the result is detection as a MACHINE</para>
87                         </parameter>
88                         <parameter name="silenceThreshold" required="false">
89                                 <para>What is the average level of noise from 0 to 32767 which if not exceeded, should be considered silence?</para>
90                         </parameter>
91                         <parameter name="maximumWordLength" required="false">
92                                 <para>Is the maximum duration of a word to accept.</para>
93                                 <para>If exceeded, then the result is detection as a MACHINE</para>
94                         </parameter>
95                 </syntax>
96                 <description>
97                         <para>This application attempts to detect answering machines at the beginning
98                         of outbound calls. Simply call this application after the call
99                         has been answered (outbound only, of course).</para>
100                         <para>When loaded, AMD reads amd.conf and uses the parameters specified as
101                         default values. Those default values get overwritten when the calling AMD
102                         with parameters.</para>
103                         <para>This application sets the following channel variables:</para>
104                         <variablelist>
105                                 <variable name="AMDSTATUS">
106                                         <para>This is the status of the answering machine detection</para>
107                                         <value name="MACHINE" />
108                                         <value name="HUMAN" />
109                                         <value name="NOTSURE" />
110                                         <value name="HANGUP" />
111                                 </variable>
112                                 <variable name="AMDCAUSE">
113                                         <para>Indicates the cause that led to the conclusion</para>
114                                         <value name="TOOLONG">
115                                                 Total Time.
116                                         </value>
117                                         <value name="INITIALSILENCE">
118                                                 Silence Duration - Initial Silence.
119                                         </value>
120                                         <value name="HUMAN">
121                                                 Silence Duration - afterGreetingSilence.
122                                         </value>
123                                         <value name="LONGGREETING">
124                                                 Voice Duration - Greeting.
125                                         </value>
126                                         <value name="MAXWORDLENGTH">
127                                                 Word Length - max length of a single word.
128                                         </value>
129                                         <value name="MAXWORDS">
130                                                 Word Count - maximum number of words.
131                                         </value>
132                                 </variable>
133                         </variablelist>
134                 </description>
135                 <see-also>
136                         <ref type="application">WaitForSilence</ref>
137                         <ref type="application">WaitForNoise</ref>
138                 </see-also>
139         </application>
140
141  ***/
142
143 static const char app[] = "AMD";
144
145 #define STATE_IN_WORD       1
146 #define STATE_IN_SILENCE    2
147
148 /* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
149 static int dfltInitialSilence       = 2500;
150 static int dfltGreeting             = 1500;
151 static int dfltAfterGreetingSilence = 800;
152 static int dfltTotalAnalysisTime    = 5000;
153 static int dfltMinimumWordLength    = 100;
154 static int dfltBetweenWordsSilence  = 50;
155 static int dfltMaximumNumberOfWords = 2;
156 static int dfltSilenceThreshold     = 256;
157 static int dfltMaximumWordLength    = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
158
159 /* Set to the lowest ms value provided in amd.conf or application parameters */
160 static int dfltMaxWaitTimeForFrame  = 50;
161
162 static void isAnsweringMachine(struct ast_channel *chan, const char *data)
163 {
164         int res = 0;
165         struct ast_frame *f = NULL;
166         struct ast_dsp *silenceDetector = NULL;
167         int dspsilence = 0, framelength = 0;
168         RAII_VAR(struct ast_format *, readFormat, NULL, ao2_cleanup);
169         int inInitialSilence = 1;
170         int inGreeting = 0;
171         int voiceDuration = 0;
172         int silenceDuration = 0;
173         int iTotalTime = 0;
174         int iWordsCount = 0;
175         int currentState = STATE_IN_WORD;
176         int consecutiveVoiceDuration = 0;
177         char amdCause[256] = "", amdStatus[256] = "";
178         char *parse = ast_strdupa(data);
179
180         /* Lets set the initial values of the variables that will control the algorithm.
181            The initial values are the default ones. If they are passed as arguments
182            when invoking the application, then the default values will be overwritten
183            by the ones passed as parameters. */
184         int initialSilence       = dfltInitialSilence;
185         int greeting             = dfltGreeting;
186         int afterGreetingSilence = dfltAfterGreetingSilence;
187         int totalAnalysisTime    = dfltTotalAnalysisTime;
188         int minimumWordLength    = dfltMinimumWordLength;
189         int betweenWordsSilence  = dfltBetweenWordsSilence;
190         int maximumNumberOfWords = dfltMaximumNumberOfWords;
191         int silenceThreshold     = dfltSilenceThreshold;
192         int maximumWordLength    = dfltMaximumWordLength;
193         int maxWaitTimeForFrame  = dfltMaxWaitTimeForFrame;
194
195         AST_DECLARE_APP_ARGS(args,
196                 AST_APP_ARG(argInitialSilence);
197                 AST_APP_ARG(argGreeting);
198                 AST_APP_ARG(argAfterGreetingSilence);
199                 AST_APP_ARG(argTotalAnalysisTime);
200                 AST_APP_ARG(argMinimumWordLength);
201                 AST_APP_ARG(argBetweenWordsSilence);
202                 AST_APP_ARG(argMaximumNumberOfWords);
203                 AST_APP_ARG(argSilenceThreshold);
204                 AST_APP_ARG(argMaximumWordLength);
205         );
206
207         ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
208                 S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
209                 S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
210                 ast_format_get_name(ast_channel_readformat(chan)));
211
212         /* Lets parse the arguments. */
213         if (!ast_strlen_zero(parse)) {
214                 /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
215                 AST_STANDARD_APP_ARGS(args, parse);
216                 if (!ast_strlen_zero(args.argInitialSilence))
217                         initialSilence = atoi(args.argInitialSilence);
218                 if (!ast_strlen_zero(args.argGreeting))
219                         greeting = atoi(args.argGreeting);
220                 if (!ast_strlen_zero(args.argAfterGreetingSilence))
221                         afterGreetingSilence = atoi(args.argAfterGreetingSilence);
222                 if (!ast_strlen_zero(args.argTotalAnalysisTime))
223                         totalAnalysisTime = atoi(args.argTotalAnalysisTime);
224                 if (!ast_strlen_zero(args.argMinimumWordLength))
225                         minimumWordLength = atoi(args.argMinimumWordLength);
226                 if (!ast_strlen_zero(args.argBetweenWordsSilence))
227                         betweenWordsSilence = atoi(args.argBetweenWordsSilence);
228                 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
229                         maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
230                 if (!ast_strlen_zero(args.argSilenceThreshold))
231                         silenceThreshold = atoi(args.argSilenceThreshold);
232                 if (!ast_strlen_zero(args.argMaximumWordLength))
233                         maximumWordLength = atoi(args.argMaximumWordLength);
234         } else {
235                 ast_debug(1, "AMD using the default parameters.\n");
236         }
237
238         /* Find lowest ms value, that will be max wait time for a frame */
239         if (maxWaitTimeForFrame > initialSilence)
240                 maxWaitTimeForFrame = initialSilence;
241         if (maxWaitTimeForFrame > greeting)
242                 maxWaitTimeForFrame = greeting;
243         if (maxWaitTimeForFrame > afterGreetingSilence)
244                 maxWaitTimeForFrame = afterGreetingSilence;
245         if (maxWaitTimeForFrame > totalAnalysisTime)
246                 maxWaitTimeForFrame = totalAnalysisTime;
247         if (maxWaitTimeForFrame > minimumWordLength)
248                 maxWaitTimeForFrame = minimumWordLength;
249         if (maxWaitTimeForFrame > betweenWordsSilence)
250                 maxWaitTimeForFrame = betweenWordsSilence;
251
252         /* Now we're ready to roll! */
253         ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
254                 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
255                                 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
256                                 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
257
258         /* Set read format to signed linear so we get signed linear frames in */
259         readFormat = ao2_bump(ast_channel_readformat(chan));
260         if (ast_set_read_format(chan, ast_format_slin) < 0 ) {
261                 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", ast_channel_name(chan));
262                 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
263                 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
264                 return;
265         }
266
267         /* Create a new DSP that will detect the silence */
268         if (!(silenceDetector = ast_dsp_new())) {
269                 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", ast_channel_name(chan));
270                 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
271                 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
272                 return;
273         }
274
275         /* Set silence threshold to specified value */
276         ast_dsp_set_threshold(silenceDetector, silenceThreshold);
277
278         /* Now we go into a loop waiting for frames from the channel */
279         while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
280                 int ms = 0;
281
282                 /* Figure out how long we waited */
283                 if (res > 0) {
284                         ms = 2 * maxWaitTimeForFrame - res;
285                 }
286
287                 /* If we fail to read in a frame, that means they hung up */
288                 if (!(f = ast_read(chan))) {
289                         ast_verb(3, "AMD: Channel [%s]. HANGUP\n", ast_channel_name(chan));
290                         ast_debug(1, "Got hangup\n");
291                         strcpy(amdStatus, "HANGUP");
292                         res = 1;
293                         break;
294                 }
295
296                 if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_CNG) {
297                         /* Figure out how long the frame is in milliseconds */
298                         if (f->frametype == AST_FRAME_VOICE) {
299                                 framelength = (ast_codec_samples_count(f) / DEFAULT_SAMPLES_PER_MS);
300                         } else {
301                                 framelength = ms;
302                         }
303
304                         iTotalTime += framelength;
305
306                         ast_debug(1, "AMD: Channel [%s] frametype [%s] iTotalTime [%d] framelength [%d] totalAnalysisTime [%d]\n",
307                                           ast_channel_name(chan),
308                                           f->frametype == AST_FRAME_VOICE ? "AST_FRAME_VOICE" : "AST_FRAME_CNG",
309                                           iTotalTime, framelength, totalAnalysisTime);
310
311                         /* If the total time exceeds the analysis time then give up as we are not too sure */
312                         if (iTotalTime >= totalAnalysisTime) {
313                                 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
314                                 ast_frfree(f);
315                                 strcpy(amdStatus , "NOTSURE");
316                                 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
317                                 break;
318                         }
319
320                         /* Feed the frame of audio into the silence detector and see if we get a result */
321                         if (f->frametype != AST_FRAME_VOICE)
322                                 dspsilence += framelength;
323                         else {
324                                 dspsilence = 0;
325                                 ast_dsp_silence(silenceDetector, f, &dspsilence);
326                         }
327
328                         if (dspsilence > 0) {
329                                 silenceDuration = dspsilence;
330
331                                 if (silenceDuration >= betweenWordsSilence) {
332                                         if (currentState != STATE_IN_SILENCE ) {
333                                                 ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", ast_channel_name(chan));
334                                         }
335                                         /* Find words less than word duration */
336                                         if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
337                                                 ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", ast_channel_name(chan), consecutiveVoiceDuration);
338                                         }
339                                         currentState  = STATE_IN_SILENCE;
340                                         consecutiveVoiceDuration = 0;
341                                 }
342
343                                 if (inInitialSilence == 1  && silenceDuration >= initialSilence) {
344                                         ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
345                                                 ast_channel_name(chan), silenceDuration, initialSilence);
346                                         ast_frfree(f);
347                                         strcpy(amdStatus , "MACHINE");
348                                         sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
349                                         res = 1;
350                                         break;
351                                 }
352
353                                 if (silenceDuration >= afterGreetingSilence  &&  inGreeting == 1) {
354                                         ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
355                                                 ast_channel_name(chan), silenceDuration, afterGreetingSilence);
356                                         ast_frfree(f);
357                                         strcpy(amdStatus , "HUMAN");
358                                         sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
359                                         res = 1;
360                                         break;
361                                 }
362
363                         } else {
364                                 consecutiveVoiceDuration += framelength;
365                                 voiceDuration += framelength;
366
367                                 /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
368                                    number of words if my previous state was Silence, which means that I moved into a word. */
369                                 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
370                                         iWordsCount++;
371                                         ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
372                                         currentState = STATE_IN_WORD;
373                                 }
374                                 if (consecutiveVoiceDuration >= maximumWordLength){
375                                         ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", ast_channel_name(chan), consecutiveVoiceDuration);
376                                         ast_frfree(f);
377                                         strcpy(amdStatus , "MACHINE");
378                                         sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
379                                         break;
380                                 }
381                                 if (iWordsCount > maximumNumberOfWords) {
382                                         ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
383                                         ast_frfree(f);
384                                         strcpy(amdStatus , "MACHINE");
385                                         sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
386                                         res = 1;
387                                         break;
388                                 }
389
390                                 if (inGreeting == 1 && voiceDuration >= greeting) {
391                                         ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", ast_channel_name(chan), voiceDuration, greeting);
392                                         ast_frfree(f);
393                                         strcpy(amdStatus , "MACHINE");
394                                         sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
395                                         res = 1;
396                                         break;
397                                 }
398
399                                 if (voiceDuration >= minimumWordLength ) {
400                                         if (silenceDuration > 0)
401                                                 ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", ast_channel_name(chan), silenceDuration);
402                                         silenceDuration = 0;
403                                 }
404                                 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
405                                         /* Only go in here once to change the greeting flag when we detect the 1st word */
406                                         if (silenceDuration > 0)
407                                                 ast_verb(3, "AMD: Channel [%s]. Before Greeting Time:  silenceDuration: %d voiceDuration: %d\n", ast_channel_name(chan), silenceDuration, voiceDuration);
408                                         inInitialSilence = 0;
409                                         inGreeting = 1;
410                                 }
411
412                         }
413                 }
414                 ast_frfree(f);
415         }
416
417         if (!res) {
418                 /* It took too long to get a frame back. Giving up. */
419                 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
420                 strcpy(amdStatus , "NOTSURE");
421                 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
422         }
423
424         /* Set the status and cause on the channel */
425         pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
426         pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
427
428         /* Restore channel read format */
429         if (readFormat && ast_set_read_format(chan, readFormat))
430                 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", ast_channel_name(chan));
431
432         /* Free the DSP used to detect silence */
433         ast_dsp_free(silenceDetector);
434
435         return;
436 }
437
438
439 static int amd_exec(struct ast_channel *chan, const char *data)
440 {
441         isAnsweringMachine(chan, data);
442
443         return 0;
444 }
445
446 static int load_config(int reload)
447 {
448         struct ast_config *cfg = NULL;
449         char *cat = NULL;
450         struct ast_variable *var = NULL;
451         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
452
453         dfltSilenceThreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
454
455         if (!(cfg = ast_config_load("amd.conf", config_flags))) {
456                 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
457                 return -1;
458         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
459                 return 0;
460         } else if (cfg == CONFIG_STATUS_FILEINVALID) {
461                 ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format.  Aborting.\n");
462                 return -1;
463         }
464
465         cat = ast_category_browse(cfg, NULL);
466
467         while (cat) {
468                 if (!strcasecmp(cat, "general") ) {
469                         var = ast_variable_browse(cfg, cat);
470                         while (var) {
471                                 if (!strcasecmp(var->name, "initial_silence")) {
472                                         dfltInitialSilence = atoi(var->value);
473                                 } else if (!strcasecmp(var->name, "greeting")) {
474                                         dfltGreeting = atoi(var->value);
475                                 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
476                                         dfltAfterGreetingSilence = atoi(var->value);
477                                 } else if (!strcasecmp(var->name, "silence_threshold")) {
478                                         dfltSilenceThreshold = atoi(var->value);
479                                 } else if (!strcasecmp(var->name, "total_analysis_time")) {
480                                         dfltTotalAnalysisTime = atoi(var->value);
481                                 } else if (!strcasecmp(var->name, "min_word_length")) {
482                                         dfltMinimumWordLength = atoi(var->value);
483                                 } else if (!strcasecmp(var->name, "between_words_silence")) {
484                                         dfltBetweenWordsSilence = atoi(var->value);
485                                 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
486                                         dfltMaximumNumberOfWords = atoi(var->value);
487                                 } else if (!strcasecmp(var->name, "maximum_word_length")) {
488                                         dfltMaximumWordLength = atoi(var->value);
489
490                                 } else {
491                                         ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
492                                                 app, cat, var->name, var->lineno);
493                                 }
494                                 var = var->next;
495                         }
496                 }
497                 cat = ast_category_browse(cfg, cat);
498         }
499
500         ast_config_destroy(cfg);
501
502         ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
503                 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
504                 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
505                 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
506
507         return 0;
508 }
509
510 static int unload_module(void)
511 {
512         return ast_unregister_application(app);
513 }
514
515 /*!
516  * \brief Load the module
517  *
518  * Module loading including tests for configuration or dependencies.
519  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
520  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
521  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
522  * configuration file or other non-critical problem return
523  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
524  */
525 static int load_module(void)
526 {
527         if (load_config(0) || ast_register_application_xml(app, amd_exec)) {
528                 return AST_MODULE_LOAD_DECLINE;
529         }
530
531         return AST_MODULE_LOAD_SUCCESS;
532 }
533
534 static int reload(void)
535 {
536         if (load_config(1))
537                 return AST_MODULE_LOAD_DECLINE;
538         return AST_MODULE_LOAD_SUCCESS;
539 }
540
541 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
542         .support_level = AST_MODULE_SUPPORT_EXTENDED,
543         .load = load_module,
544         .unload = unload_module,
545         .reload = reload,
546 );