2006-06-06 07:29:57 +00:00
|
|
|
/*
|
2012-10-14 21:45:16 +00:00
|
|
|
* Asterisk -- An open source telephony toolkit.
|
2006-06-06 07:29:57 +00:00
|
|
|
*
|
|
|
|
* A full-featured Find-Me/Follow-Me Application
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005-2006, BJ Weschke All Rights Reserved.
|
|
|
|
*
|
|
|
|
* BJ Weschke <bweschke@btwtech.com>
|
|
|
|
*
|
|
|
|
* This code is released by the author with no restrictions on usage.
|
|
|
|
*
|
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file
|
|
|
|
*
|
|
|
|
* \brief Find-Me Follow-Me application
|
|
|
|
*
|
|
|
|
* \author BJ Weschke <bweschke@btwtech.com>
|
|
|
|
*
|
|
|
|
* \ingroup applications
|
|
|
|
*/
|
|
|
|
|
2012-10-14 21:45:16 +00:00
|
|
|
/*! \li \ref app_followme.c uses the configuration file \ref followme.conf
|
2012-10-01 23:22:50 +00:00
|
|
|
* \addtogroup configuration_file Configuration Files
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \page followme.conf followme.conf
|
|
|
|
* \verbinclude followme.conf.sample
|
|
|
|
*/
|
|
|
|
|
2008-01-22 17:46:43 +00:00
|
|
|
/*** MODULEINFO
|
2011-07-14 20:28:54 +00:00
|
|
|
<support_level>core</support_level>
|
2008-01-22 17:46:43 +00:00
|
|
|
***/
|
|
|
|
|
2006-06-07 18:54:56 +00:00
|
|
|
#include "asterisk.h"
|
|
|
|
|
2006-09-06 21:37:12 +00:00
|
|
|
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
2006-06-06 07:29:57 +00:00
|
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
2007-11-20 23:16:15 +00:00
|
|
|
#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
|
2006-06-06 07:29:57 +00:00
|
|
|
#include "asterisk/lock.h"
|
|
|
|
#include "asterisk/file.h"
|
|
|
|
#include "asterisk/channel.h"
|
|
|
|
#include "asterisk/pbx.h"
|
|
|
|
#include "asterisk/module.h"
|
|
|
|
#include "asterisk/translate.h"
|
|
|
|
#include "asterisk/say.h"
|
|
|
|
#include "asterisk/features.h"
|
|
|
|
#include "asterisk/musiconhold.h"
|
|
|
|
#include "asterisk/cli.h"
|
|
|
|
#include "asterisk/manager.h"
|
|
|
|
#include "asterisk/config.h"
|
|
|
|
#include "asterisk/utils.h"
|
|
|
|
#include "asterisk/causes.h"
|
|
|
|
#include "asterisk/astdb.h"
|
2008-03-05 16:23:44 +00:00
|
|
|
#include "asterisk/dsp.h"
|
2006-06-06 07:29:57 +00:00
|
|
|
#include "asterisk/app.h"
|
2013-05-22 18:11:57 +00:00
|
|
|
#include "asterisk/stasis_channels.h"
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-11-05 02:08:39 +00:00
|
|
|
/*** DOCUMENTATION
|
|
|
|
<application name="FollowMe" language="en_US">
|
|
|
|
<synopsis>
|
|
|
|
Find-Me/Follow-Me application.
|
|
|
|
</synopsis>
|
|
|
|
<syntax>
|
|
|
|
<parameter name="followmeid" required="true" />
|
|
|
|
<parameter name="options">
|
|
|
|
<optionlist>
|
|
|
|
<option name="a">
|
|
|
|
<para>Record the caller's name so it can be announced to the
|
|
|
|
callee on each step.</para>
|
|
|
|
</option>
|
2012-05-15 16:53:09 +00:00
|
|
|
<option name="B" argsep="^">
|
|
|
|
<para>Before initiating the outgoing call(s), Gosub to the specified
|
|
|
|
location using the current channel.</para>
|
|
|
|
<argument name="context" required="false" />
|
|
|
|
<argument name="exten" required="false" />
|
|
|
|
<argument name="priority" required="true" hasparams="optional" argsep="^">
|
|
|
|
<argument name="arg1" multiple="true" required="true" />
|
|
|
|
<argument name="argN" />
|
|
|
|
</argument>
|
|
|
|
</option>
|
|
|
|
<option name="b" argsep="^">
|
|
|
|
<para>Before initiating an outgoing call, Gosub to the specified
|
|
|
|
location using the newly created channel. The Gosub will be
|
|
|
|
executed for each destination channel.</para>
|
|
|
|
<argument name="context" required="false" />
|
|
|
|
<argument name="exten" required="false" />
|
|
|
|
<argument name="priority" required="true" hasparams="optional" argsep="^">
|
|
|
|
<argument name="arg1" multiple="true" required="true" />
|
|
|
|
<argument name="argN" />
|
|
|
|
</argument>
|
|
|
|
</option>
|
2009-11-23 22:37:39 +00:00
|
|
|
<option name="d">
|
|
|
|
<para>Disable the 'Please hold while we try to connect your call' announcement.</para>
|
|
|
|
</option>
|
2012-01-11 21:56:12 +00:00
|
|
|
<option name="I">
|
|
|
|
<para>Asterisk will ignore any connected line update requests
|
|
|
|
it may receive on this dial attempt.</para>
|
|
|
|
</option>
|
2011-03-18 19:05:20 +00:00
|
|
|
<option name="l">
|
|
|
|
<para>Disable local call optimization so that applications with
|
|
|
|
audio hooks between the local bridge don't get dropped when the
|
|
|
|
calls get joined directly.</para>
|
|
|
|
</option>
|
2012-01-11 21:56:12 +00:00
|
|
|
<option name="N">
|
|
|
|
<para>Don't answer the incoming call until we're ready to
|
|
|
|
connect the caller or give up.</para>
|
|
|
|
<note>
|
|
|
|
<para>This option is ignored if the call is already answered.</para>
|
|
|
|
</note>
|
|
|
|
<note>
|
|
|
|
<para>If the call is not already answered, the 'a' and 's'
|
|
|
|
options are ignored while the 'd' option is implicitly enabled.</para>
|
|
|
|
</note>
|
|
|
|
</option>
|
|
|
|
<option name="n">
|
|
|
|
<para>Playback the unreachable status message if we've run out
|
|
|
|
of steps or the callee has elected not to be reachable.</para>
|
|
|
|
</option>
|
|
|
|
<option name="s">
|
|
|
|
<para>Playback the incoming status message prior to starting
|
|
|
|
the follow-me step(s)</para>
|
|
|
|
</option>
|
2008-11-05 02:08:39 +00:00
|
|
|
</optionlist>
|
|
|
|
</parameter>
|
|
|
|
</syntax>
|
|
|
|
<description>
|
|
|
|
<para>This application performs Find-Me/Follow-Me functionality for the caller
|
|
|
|
as defined in the profile matching the <replaceable>followmeid</replaceable> parameter in
|
|
|
|
<filename>followme.conf</filename>. If the specified <replaceable>followmeid</replaceable>
|
|
|
|
profile doesn't exist in <filename>followme.conf</filename>, execution will be returned
|
|
|
|
to the dialplan and call execution will continue at the next priority.</para>
|
|
|
|
<para>Returns -1 on hangup.</para>
|
|
|
|
</description>
|
|
|
|
</application>
|
|
|
|
***/
|
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
static char *app = "FollowMe";
|
|
|
|
|
2012-05-08 18:16:04 +00:00
|
|
|
/*! Maximum accept/decline DTMF string plus terminator. */
|
|
|
|
#define MAX_YN_STRING 20
|
|
|
|
|
2006-09-05 20:36:27 +00:00
|
|
|
/*! \brief Number structure */
|
2006-06-06 07:29:57 +00:00
|
|
|
struct number {
|
|
|
|
char number[512]; /*!< Phone Number(s) and/or Extension(s) */
|
|
|
|
long timeout; /*!< Dial Timeout, if used. */
|
|
|
|
int order; /*!< The order to dial in */
|
|
|
|
AST_LIST_ENTRY(number) entry; /*!< Next Number record */
|
|
|
|
};
|
|
|
|
|
2006-09-05 20:36:27 +00:00
|
|
|
/*! \brief Data structure for followme scripts */
|
|
|
|
struct call_followme {
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_mutex_t lock;
|
|
|
|
char name[AST_MAX_EXTENSION]; /*!< Name - FollowMeID */
|
2012-04-25 10:49:13 +00:00
|
|
|
char moh[MAX_MUSICCLASS]; /*!< Music On Hold Class to be used */
|
2006-06-06 07:29:57 +00:00
|
|
|
char context[AST_MAX_CONTEXT]; /*!< Context to dial from */
|
|
|
|
unsigned int active; /*!< Profile is active (1), or disabled (0). */
|
2008-08-25 16:02:56 +00:00
|
|
|
int realtime; /*!< Cached from realtime */
|
2012-05-08 18:16:04 +00:00
|
|
|
char takecall[MAX_YN_STRING]; /*!< Digit mapping to take a call */
|
|
|
|
char nextindp[MAX_YN_STRING]; /*!< Digit mapping to decline a call */
|
2006-09-05 20:36:27 +00:00
|
|
|
char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
|
|
|
char norecordingprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
|
|
|
char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
|
|
|
char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
|
|
|
char statusprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
|
|
|
char sorryprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
2006-06-06 07:29:57 +00:00
|
|
|
|
|
|
|
AST_LIST_HEAD_NOLOCK(numbers, number) numbers; /*!< Head of the list of follow-me numbers */
|
|
|
|
AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers; /*!< Head of the list of black-listed numbers */
|
|
|
|
AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers; /*!< Head of the list of white-listed numbers */
|
2006-09-05 20:36:27 +00:00
|
|
|
AST_LIST_ENTRY(call_followme) entry; /*!< Next Follow-Me record */
|
2006-06-06 07:29:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct fm_args {
|
|
|
|
char *mohclass;
|
|
|
|
AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
|
2012-05-15 16:53:09 +00:00
|
|
|
/*! Gosub app arguments for outgoing calls. NULL if not supplied. */
|
|
|
|
const char *predial_callee;
|
2012-01-11 21:56:12 +00:00
|
|
|
/*! Accumulated connected line information from inbound call. */
|
|
|
|
struct ast_party_connected_line connected_in;
|
|
|
|
/*! Accumulated connected line information from outbound call. */
|
|
|
|
struct ast_party_connected_line connected_out;
|
|
|
|
/*! TRUE if connected line information from inbound call changed. */
|
2012-05-08 18:16:04 +00:00
|
|
|
unsigned int pending_in_connected_update:1;
|
2012-01-11 21:56:12 +00:00
|
|
|
/*! TRUE if connected line information from outbound call is available. */
|
2012-05-08 18:16:04 +00:00
|
|
|
unsigned int pending_out_connected_update:1;
|
2012-05-09 01:59:14 +00:00
|
|
|
/*! TRUE if caller has a pending hold request for the winning call. */
|
|
|
|
unsigned int pending_hold:1;
|
|
|
|
/*! Music On Hold Class suggested by caller hold for winning call. */
|
|
|
|
char suggested_moh[MAX_MUSICCLASS];
|
2006-06-06 07:29:57 +00:00
|
|
|
char context[AST_MAX_CONTEXT];
|
2012-05-08 20:32:11 +00:00
|
|
|
char namerecloc[PATH_MAX];
|
2012-05-08 18:16:04 +00:00
|
|
|
char takecall[MAX_YN_STRING]; /*!< Digit mapping to take a call */
|
|
|
|
char nextindp[MAX_YN_STRING]; /*!< Digit mapping to decline a call */
|
2006-09-05 20:36:27 +00:00
|
|
|
char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
|
|
|
char norecordingprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
|
|
|
char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
|
|
|
char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
|
|
|
char statusprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
|
|
|
char sorryprompt[PATH_MAX]; /*!< Sound prompt name and path */
|
2006-06-06 07:29:57 +00:00
|
|
|
struct ast_flags followmeflags;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct findme_user {
|
|
|
|
struct ast_channel *ochan;
|
2012-01-11 21:56:12 +00:00
|
|
|
/*! Accumulated connected line information from outgoing call. */
|
|
|
|
struct ast_party_connected_line connected;
|
|
|
|
long digts;
|
|
|
|
int ynidx;
|
2006-06-06 07:29:57 +00:00
|
|
|
int state;
|
|
|
|
char dialarg[256];
|
2012-05-08 18:16:04 +00:00
|
|
|
/*! Collected digits to accept/decline the call. */
|
|
|
|
char yn[MAX_YN_STRING];
|
2012-05-09 02:35:29 +00:00
|
|
|
/*! TRUE if the outgoing call is answered. */
|
|
|
|
unsigned int answered:1;
|
2012-01-11 21:56:12 +00:00
|
|
|
/*! TRUE if connected line information is available. */
|
2012-05-08 18:16:04 +00:00
|
|
|
unsigned int pending_connected_update:1;
|
2008-03-05 16:23:44 +00:00
|
|
|
AST_LIST_ENTRY(findme_user) entry;
|
2006-06-06 07:29:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
FOLLOWMEFLAG_STATUSMSG = (1 << 0),
|
|
|
|
FOLLOWMEFLAG_RECORDNAME = (1 << 1),
|
2009-11-23 22:37:39 +00:00
|
|
|
FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
|
2011-01-26 23:41:55 +00:00
|
|
|
FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
|
2011-03-18 19:05:20 +00:00
|
|
|
FOLLOWMEFLAG_NOANSWER = (1 << 4),
|
|
|
|
FOLLOWMEFLAG_DISABLEOPTIMIZATION = (1 << 5),
|
2012-01-11 21:56:12 +00:00
|
|
|
FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 6),
|
2012-05-15 16:53:09 +00:00
|
|
|
FOLLOWMEFLAG_PREDIAL_CALLER = (1 << 7),
|
|
|
|
FOLLOWMEFLAG_PREDIAL_CALLEE = (1 << 8),
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
FOLLOWMEFLAG_ARG_PREDIAL_CALLER,
|
|
|
|
FOLLOWMEFLAG_ARG_PREDIAL_CALLEE,
|
|
|
|
|
|
|
|
/* note: this entry _MUST_ be the last one in the enum */
|
|
|
|
FOLLOWMEFLAG_ARG_ARRAY_SIZE
|
2006-06-06 07:29:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
AST_APP_OPTIONS(followme_opts, {
|
2012-01-11 21:56:12 +00:00
|
|
|
AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME),
|
2012-05-15 16:53:09 +00:00
|
|
|
AST_APP_OPTION_ARG('B', FOLLOWMEFLAG_PREDIAL_CALLER, FOLLOWMEFLAG_ARG_PREDIAL_CALLER),
|
|
|
|
AST_APP_OPTION_ARG('b', FOLLOWMEFLAG_PREDIAL_CALLEE, FOLLOWMEFLAG_ARG_PREDIAL_CALLEE),
|
2012-01-11 21:56:12 +00:00
|
|
|
AST_APP_OPTION('d', FOLLOWMEFLAG_DISABLEHOLDPROMPT),
|
|
|
|
AST_APP_OPTION('I', FOLLOWMEFLAG_IGNORE_CONNECTEDLINE),
|
|
|
|
AST_APP_OPTION('l', FOLLOWMEFLAG_DISABLEOPTIMIZATION),
|
|
|
|
AST_APP_OPTION('N', FOLLOWMEFLAG_NOANSWER),
|
|
|
|
AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG),
|
|
|
|
AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG),
|
2006-06-06 07:29:57 +00:00
|
|
|
});
|
|
|
|
|
2006-09-20 20:57:57 +00:00
|
|
|
static const char *featuredigittostr;
|
2006-06-06 07:29:57 +00:00
|
|
|
static int featuredigittimeout = 5000; /*!< Feature Digit Timeout */
|
|
|
|
static const char *defaultmoh = "default"; /*!< Default Music-On-Hold Class */
|
|
|
|
|
2012-05-08 18:16:04 +00:00
|
|
|
static char takecall[MAX_YN_STRING] = "1";
|
|
|
|
static char nextindp[MAX_YN_STRING] = "2";
|
2006-06-09 20:40:10 +00:00
|
|
|
static char callfromprompt[PATH_MAX] = "followme/call-from";
|
|
|
|
static char norecordingprompt[PATH_MAX] = "followme/no-recording";
|
2006-08-07 18:47:33 +00:00
|
|
|
static char optionsprompt[PATH_MAX] = "followme/options";
|
2006-06-09 20:40:10 +00:00
|
|
|
static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
|
2006-08-07 18:47:33 +00:00
|
|
|
static char statusprompt[PATH_MAX] = "followme/status";
|
|
|
|
static char sorryprompt[PATH_MAX] = "followme/sorry";
|
2006-06-06 07:29:57 +00:00
|
|
|
|
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
static AST_RWLIST_HEAD_STATIC(followmes, call_followme);
|
2006-06-06 21:03:18 +00:00
|
|
|
AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2006-09-05 20:36:27 +00:00
|
|
|
static void free_numbers(struct call_followme *f)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
|
|
|
/* Free numbers attached to the profile */
|
|
|
|
struct number *prev;
|
|
|
|
|
|
|
|
while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
|
|
|
|
/* Free the number */
|
2007-06-06 21:20:11 +00:00
|
|
|
ast_free(prev);
|
2006-06-06 07:29:57 +00:00
|
|
|
AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
|
|
|
|
|
|
|
|
while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
|
|
|
|
/* Free the blacklisted number */
|
2007-06-06 21:20:11 +00:00
|
|
|
ast_free(prev);
|
2006-06-06 07:29:57 +00:00
|
|
|
AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
|
|
|
|
|
|
|
|
while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
|
|
|
|
/* Free the whitelisted number */
|
2007-06-06 21:20:11 +00:00
|
|
|
ast_free(prev);
|
2006-06-06 07:29:57 +00:00
|
|
|
AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-05 20:36:27 +00:00
|
|
|
/*! \brief Allocate and initialize followme profile */
|
|
|
|
static struct call_followme *alloc_profile(const char *fmname)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
2006-09-05 20:36:27 +00:00
|
|
|
struct call_followme *f;
|
|
|
|
|
|
|
|
if (!(f = ast_calloc(1, sizeof(*f))))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ast_mutex_init(&f->lock);
|
|
|
|
ast_copy_string(f->name, fmname, sizeof(f->name));
|
|
|
|
f->moh[0] = '\0';
|
|
|
|
f->context[0] = '\0';
|
|
|
|
ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
|
|
|
|
ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
|
|
|
|
ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
|
|
|
|
ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
|
|
|
|
ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
|
|
|
|
ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
|
|
|
|
ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
|
|
|
|
ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
|
|
|
|
AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
|
|
|
|
AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
|
|
|
|
AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
|
2006-06-06 07:29:57 +00:00
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2006-09-05 20:36:27 +00:00
|
|
|
static void init_profile(struct call_followme *f)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
|
|
|
f->active = 1;
|
|
|
|
ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-09-05 20:36:27 +00:00
|
|
|
/*! \brief Set parameter in profile from configuration file */
|
|
|
|
static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
|
|
|
|
ast_copy_string(f->moh, val, sizeof(f->moh));
|
2006-09-05 20:36:27 +00:00
|
|
|
else if (!strcasecmp(param, "context"))
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(f->context, val, sizeof(f->context));
|
2006-09-05 20:36:27 +00:00
|
|
|
else if (!strcasecmp(param, "takecall"))
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(f->takecall, val, sizeof(f->takecall));
|
2006-09-05 20:36:27 +00:00
|
|
|
else if (!strcasecmp(param, "declinecall"))
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
|
2008-08-25 16:02:56 +00:00
|
|
|
else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
|
2008-08-25 16:02:56 +00:00
|
|
|
else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt"))
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
|
2008-08-25 16:02:56 +00:00
|
|
|
else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt"))
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
|
2008-11-25 21:49:42 +00:00
|
|
|
else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
|
2008-08-25 16:02:56 +00:00
|
|
|
else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt"))
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
|
2008-08-25 16:02:56 +00:00
|
|
|
else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt"))
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
|
2006-09-05 20:36:27 +00:00
|
|
|
else if (failunknown) {
|
2006-06-06 07:29:57 +00:00
|
|
|
if (linenum >= 0)
|
|
|
|
ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
|
|
|
|
else
|
|
|
|
ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-05 20:36:27 +00:00
|
|
|
/*! \brief Add a new number */
|
2009-05-21 21:13:09 +00:00
|
|
|
static struct number *create_followme_number(const char *number, int timeout, int numorder)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
|
|
|
struct number *cur;
|
2009-05-21 21:13:09 +00:00
|
|
|
char *buf = ast_strdupa(number);
|
2006-06-06 07:29:57 +00:00
|
|
|
char *tmp;
|
|
|
|
|
2006-09-05 20:36:27 +00:00
|
|
|
if (!(cur = ast_calloc(1, sizeof(*cur))))
|
|
|
|
return NULL;
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2006-09-05 20:36:27 +00:00
|
|
|
cur->timeout = timeout;
|
2009-05-21 21:13:09 +00:00
|
|
|
if ((tmp = strchr(buf, ',')))
|
2006-09-05 20:36:27 +00:00
|
|
|
*tmp = '\0';
|
2009-05-21 21:13:09 +00:00
|
|
|
ast_copy_string(cur->number, buf, sizeof(cur->number));
|
2006-09-05 20:36:27 +00:00
|
|
|
cur->order = numorder;
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
|
2006-06-06 07:29:57 +00:00
|
|
|
|
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
|
2006-09-05 20:36:27 +00:00
|
|
|
/*! \brief Reload followme application module */
|
2007-08-16 21:09:46 +00:00
|
|
|
static int reload_followme(int reload)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
2006-09-05 20:36:27 +00:00
|
|
|
struct call_followme *f;
|
2006-06-06 07:29:57 +00:00
|
|
|
struct ast_config *cfg;
|
2006-06-07 20:02:07 +00:00
|
|
|
char *cat = NULL, *tmp;
|
2006-06-06 07:29:57 +00:00
|
|
|
struct ast_variable *var;
|
|
|
|
struct number *cur, *nm;
|
2012-09-06 01:02:17 +00:00
|
|
|
char *numberstr;
|
2006-06-06 07:29:57 +00:00
|
|
|
int timeout;
|
2008-03-05 16:23:44 +00:00
|
|
|
int numorder;
|
2006-09-20 20:57:57 +00:00
|
|
|
const char *takecallstr;
|
|
|
|
const char *declinecallstr;
|
|
|
|
const char *tmpstr;
|
2007-08-16 21:09:46 +00:00
|
|
|
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2007-08-16 21:09:46 +00:00
|
|
|
if (!(cfg = ast_config_load("followme.conf", config_flags))) {
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
|
|
|
|
return 0;
|
2008-09-12 23:30:03 +00:00
|
|
|
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
|
2007-08-16 21:09:46 +00:00
|
|
|
return 0;
|
2008-09-12 23:30:03 +00:00
|
|
|
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
|
|
|
|
ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format. Aborting.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
AST_RWLIST_WRLOCK(&followmes);
|
2006-06-06 07:29:57 +00:00
|
|
|
|
|
|
|
/* Reset Global Var Values */
|
|
|
|
featuredigittimeout = 5000;
|
|
|
|
|
|
|
|
/* Mark all profiles as inactive for the moment */
|
2007-07-30 16:02:01 +00:00
|
|
|
AST_RWLIST_TRAVERSE(&followmes, f, entry) {
|
2006-06-06 07:29:57 +00:00
|
|
|
f->active = 0;
|
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2006-06-07 19:42:37 +00:00
|
|
|
if (!ast_strlen_zero(featuredigittostr)) {
|
2009-08-10 19:20:57 +00:00
|
|
|
if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
|
2006-06-06 07:29:57 +00:00
|
|
|
featuredigittimeout = 5000;
|
2006-06-07 19:42:37 +00:00
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-08-26 18:05:58 +00:00
|
|
|
if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(takecall, takecallstr, sizeof(takecall));
|
2008-08-26 18:05:58 +00:00
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2008-08-26 18:05:58 +00:00
|
|
|
if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
|
2008-08-26 18:05:58 +00:00
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-08-26 18:05:58 +00:00
|
|
|
if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
|
|
|
|
ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
|
|
|
|
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
|
2008-08-26 18:05:58 +00:00
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-08-26 18:05:58 +00:00
|
|
|
if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
|
2008-08-26 18:05:58 +00:00
|
|
|
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
|
2010-03-05 19:10:47 +00:00
|
|
|
ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
|
2008-08-26 18:05:58 +00:00
|
|
|
}
|
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-08-26 18:05:58 +00:00
|
|
|
if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
|
2008-08-26 18:05:58 +00:00
|
|
|
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
|
|
|
|
ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
|
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-08-26 18:05:58 +00:00
|
|
|
if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
|
2010-03-05 19:10:47 +00:00
|
|
|
ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
|
2008-08-26 18:05:58 +00:00
|
|
|
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
|
2010-03-05 19:10:47 +00:00
|
|
|
ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
|
2008-08-26 18:05:58 +00:00
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-08-26 18:05:58 +00:00
|
|
|
if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
|
2010-03-05 19:10:47 +00:00
|
|
|
ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
|
2008-08-26 18:05:58 +00:00
|
|
|
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
|
2010-03-05 19:10:47 +00:00
|
|
|
ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
|
2008-08-26 18:05:58 +00:00
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-08-26 18:05:58 +00:00
|
|
|
if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
|
2010-03-05 19:10:47 +00:00
|
|
|
ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
|
2008-08-26 18:05:58 +00:00
|
|
|
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
|
2010-03-05 19:10:47 +00:00
|
|
|
ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
|
2008-08-26 18:05:58 +00:00
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
|
|
|
/* Chug through config file */
|
2006-06-07 20:02:07 +00:00
|
|
|
while ((cat = ast_category_browse(cfg, cat))) {
|
2007-07-30 16:02:01 +00:00
|
|
|
int new = 0;
|
|
|
|
|
2006-06-07 20:02:07 +00:00
|
|
|
if (!strcasecmp(cat, "general"))
|
|
|
|
continue;
|
2007-07-30 16:02:01 +00:00
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
/* Look for an existing one */
|
|
|
|
AST_LIST_TRAVERSE(&followmes, f, entry) {
|
|
|
|
if (!strcasecmp(f->name, cat))
|
|
|
|
break;
|
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "New profile %s.\n", cat);
|
2007-07-30 16:02:01 +00:00
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
if (!f) {
|
|
|
|
/* Make one then */
|
|
|
|
f = alloc_profile(cat);
|
|
|
|
new = 1;
|
2007-07-30 16:02:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Totally fail if we fail to find/create an entry */
|
|
|
|
if (!f)
|
|
|
|
continue;
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
if (!new)
|
|
|
|
ast_mutex_lock(&f->lock);
|
|
|
|
/* Re-initialize the profile */
|
|
|
|
init_profile(f);
|
|
|
|
free_numbers(f);
|
|
|
|
var = ast_variable_browse(cfg, cat);
|
2008-08-26 18:05:58 +00:00
|
|
|
while (var) {
|
2007-07-30 16:02:01 +00:00
|
|
|
if (!strcasecmp(var->name, "number")) {
|
|
|
|
int idx = 0;
|
|
|
|
|
|
|
|
/* Add a new number */
|
2012-09-06 01:02:17 +00:00
|
|
|
numberstr = ast_strdupa(var->value);
|
2007-07-30 16:02:01 +00:00
|
|
|
if ((tmp = strchr(numberstr, ','))) {
|
|
|
|
*tmp++ = '\0';
|
2011-12-13 23:10:42 +00:00
|
|
|
timeout = atoi(tmp);
|
|
|
|
if (timeout < 0) {
|
|
|
|
timeout = 25;
|
|
|
|
}
|
|
|
|
if ((tmp = strchr(tmp, ','))) {
|
2007-07-30 16:02:01 +00:00
|
|
|
*tmp++ = '\0';
|
|
|
|
numorder = atoi(tmp);
|
|
|
|
if (numorder < 0)
|
2006-06-06 07:29:57 +00:00
|
|
|
numorder = 0;
|
2007-07-30 16:02:01 +00:00
|
|
|
} else
|
2006-06-06 07:29:57 +00:00
|
|
|
numorder = 0;
|
|
|
|
} else {
|
2007-07-30 16:02:01 +00:00
|
|
|
timeout = 25;
|
|
|
|
numorder = 0;
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
|
|
|
if (!numorder) {
|
2007-07-30 16:02:01 +00:00
|
|
|
idx = 1;
|
|
|
|
AST_LIST_TRAVERSE(&f->numbers, nm, entry)
|
|
|
|
idx++;
|
|
|
|
numorder = idx;
|
|
|
|
}
|
|
|
|
cur = create_followme_number(numberstr, timeout, numorder);
|
2011-12-13 23:10:42 +00:00
|
|
|
if (cur) {
|
|
|
|
AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
|
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
} else {
|
|
|
|
profile_set_param(f, var->name, var->value, var->lineno, 1);
|
|
|
|
ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
|
|
|
|
}
|
|
|
|
var = var->next;
|
|
|
|
} /* End while(var) loop */
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
if (!new)
|
|
|
|
ast_mutex_unlock(&f->lock);
|
|
|
|
else
|
|
|
|
AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
ast_config_destroy(cfg);
|
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
AST_RWLIST_UNLOCK(&followmes);
|
2006-06-06 07:29:57 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-05-22 18:11:57 +00:00
|
|
|
static void publish_dial_end_event(struct ast_channel *in, struct findme_user_listptr *findme_user_list, struct ast_channel *exception, const char *status)
|
|
|
|
{
|
|
|
|
struct findme_user *tmpuser;
|
|
|
|
|
|
|
|
AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
|
|
|
|
if (tmpuser->ochan && tmpuser->ochan != exception) {
|
|
|
|
ast_channel_publish_dial(in, tmpuser->ochan, NULL, status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
static void clear_caller(struct findme_user *tmpuser)
|
|
|
|
{
|
|
|
|
struct ast_channel *outbound;
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-05-09 01:36:07 +00:00
|
|
|
if (!tmpuser->ochan) {
|
|
|
|
/* Call already cleared. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
outbound = tmpuser->ochan;
|
|
|
|
ast_hangup(outbound);
|
|
|
|
tmpuser->ochan = NULL;
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
|
|
|
|
2012-05-09 02:35:29 +00:00
|
|
|
static void clear_unanswered_calls(struct findme_user_listptr *findme_user_list)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
|
|
|
struct findme_user *tmpuser;
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
|
2012-05-09 02:35:29 +00:00
|
|
|
if (!tmpuser->answered) {
|
|
|
|
clear_caller(tmpuser);
|
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-09 01:36:07 +00:00
|
|
|
static void destroy_calling_node(struct findme_user *node)
|
|
|
|
{
|
|
|
|
clear_caller(node);
|
|
|
|
ast_party_connected_line_free(&node->connected);
|
|
|
|
ast_free(node);
|
|
|
|
}
|
|
|
|
|
2011-12-13 23:10:42 +00:00
|
|
|
static void destroy_calling_tree(struct findme_user_listptr *findme_user_list)
|
|
|
|
{
|
|
|
|
struct findme_user *fmuser;
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2011-12-13 23:10:42 +00:00
|
|
|
while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
|
2012-05-09 01:36:07 +00:00
|
|
|
destroy_calling_node(fmuser);
|
2011-12-13 23:10:42 +00:00
|
|
|
}
|
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2012-05-09 01:36:07 +00:00
|
|
|
static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, struct fm_args *tpargs)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
2012-01-11 21:56:12 +00:00
|
|
|
struct ast_party_connected_line connected;
|
2006-06-06 07:29:57 +00:00
|
|
|
struct ast_channel *watchers[256];
|
|
|
|
int pos;
|
|
|
|
struct ast_channel *winner;
|
|
|
|
struct ast_frame *f;
|
|
|
|
struct findme_user *tmpuser;
|
2006-06-06 20:18:01 +00:00
|
|
|
int to = 0;
|
2012-05-09 01:36:07 +00:00
|
|
|
int livechannels;
|
2006-06-06 07:29:57 +00:00
|
|
|
int tmpto;
|
2007-07-30 16:02:01 +00:00
|
|
|
long totalwait = 0, wtd = 0, towas = 0;
|
2006-06-06 07:29:57 +00:00
|
|
|
char *callfromname;
|
|
|
|
char *pressbuttonname;
|
|
|
|
|
|
|
|
/* ------------ wait_for_winner_channel start --------------- */
|
|
|
|
|
|
|
|
callfromname = ast_strdupa(tpargs->callfromprompt);
|
2008-03-05 16:23:44 +00:00
|
|
|
pressbuttonname = ast_strdupa(tpargs->optionsprompt);
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
totalwait = nm->timeout * 1000;
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-05-09 01:36:07 +00:00
|
|
|
for (;;) {
|
2007-07-30 16:02:01 +00:00
|
|
|
to = 1000;
|
|
|
|
pos = 1;
|
|
|
|
livechannels = 0;
|
|
|
|
watchers[0] = caller;
|
2008-03-05 16:23:44 +00:00
|
|
|
|
|
|
|
winner = NULL;
|
2007-07-30 16:02:01 +00:00
|
|
|
AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
|
2012-05-09 01:36:07 +00:00
|
|
|
if (!tmpuser->ochan) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (tmpuser->state == 3) {
|
|
|
|
tmpuser->digts += (towas - wtd);
|
|
|
|
}
|
|
|
|
if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
|
|
|
|
ast_verb(3, "<%s> We've been waiting for digits longer than we should have.\n",
|
|
|
|
ast_channel_name(tmpuser->ochan));
|
|
|
|
if (!ast_strlen_zero(tpargs->namerecloc)) {
|
|
|
|
tmpuser->state = 1;
|
|
|
|
tmpuser->digts = 0;
|
|
|
|
if (!ast_streamfile(tmpuser->ochan, callfromname, ast_channel_language(tmpuser->ochan))) {
|
|
|
|
ast_sched_runq(ast_channel_sched(tmpuser->ochan));
|
2007-07-30 16:02:01 +00:00
|
|
|
} else {
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
|
|
|
|
clear_caller(tmpuser);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tmpuser->state = 2;
|
|
|
|
tmpuser->digts = 0;
|
|
|
|
if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
|
|
|
|
ast_sched_runq(ast_channel_sched(tmpuser->ochan));
|
|
|
|
else {
|
|
|
|
ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
|
|
|
|
clear_caller(tmpuser);
|
|
|
|
continue;
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
}
|
2012-05-09 01:36:07 +00:00
|
|
|
}
|
|
|
|
if (ast_channel_stream(tmpuser->ochan)) {
|
|
|
|
ast_sched_runq(ast_channel_sched(tmpuser->ochan));
|
|
|
|
tmpto = ast_sched_wait(ast_channel_sched(tmpuser->ochan));
|
|
|
|
if (tmpto > 0 && tmpto < to)
|
|
|
|
to = tmpto;
|
|
|
|
else if (tmpto < 0 && !ast_channel_timingfunc(tmpuser->ochan)) {
|
|
|
|
ast_stopstream(tmpuser->ochan);
|
|
|
|
switch (tmpuser->state) {
|
|
|
|
case 1:
|
|
|
|
ast_verb(3, "<%s> Playback of the call-from file appears to be done.\n",
|
|
|
|
ast_channel_name(tmpuser->ochan));
|
|
|
|
if (!ast_streamfile(tmpuser->ochan, tpargs->namerecloc, ast_channel_language(tmpuser->ochan))) {
|
|
|
|
tmpuser->state = 2;
|
|
|
|
} else {
|
|
|
|
ast_log(LOG_NOTICE, "<%s> Unable to playback %s. Maybe the caller didn't record their name?\n",
|
|
|
|
ast_channel_name(tmpuser->ochan), tpargs->namerecloc);
|
2007-07-30 16:02:01 +00:00
|
|
|
memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
|
|
|
|
tmpuser->ynidx = 0;
|
2012-05-09 01:36:07 +00:00
|
|
|
if (!ast_streamfile(tmpuser->ochan, pressbuttonname, ast_channel_language(tmpuser->ochan)))
|
2007-07-30 16:02:01 +00:00
|
|
|
tmpuser->state = 3;
|
2012-05-09 01:36:07 +00:00
|
|
|
else {
|
|
|
|
ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
|
|
|
|
clear_caller(tmpuser);
|
|
|
|
continue;
|
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
2012-05-09 01:36:07 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
ast_verb(3, "<%s> Playback of name file appears to be done.\n",
|
|
|
|
ast_channel_name(tmpuser->ochan));
|
|
|
|
memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
|
|
|
|
tmpuser->ynidx = 0;
|
|
|
|
if (!ast_streamfile(tmpuser->ochan, pressbuttonname, ast_channel_language(tmpuser->ochan))) {
|
|
|
|
tmpuser->state = 3;
|
|
|
|
} else {
|
|
|
|
clear_caller(tmpuser);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
ast_verb(3, "<%s> Playback of the next step file appears to be done.\n",
|
|
|
|
ast_channel_name(tmpuser->ochan));
|
|
|
|
tmpuser->digts = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-09 01:36:07 +00:00
|
|
|
watchers[pos++] = tmpuser->ochan;
|
|
|
|
livechannels++;
|
|
|
|
}
|
|
|
|
if (!livechannels) {
|
|
|
|
ast_verb(3, "No live channels left for this step.\n");
|
|
|
|
return NULL;
|
2007-07-30 16:02:01 +00:00
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
tmpto = to;
|
|
|
|
if (to < 0) {
|
|
|
|
to = 1000;
|
|
|
|
tmpto = 1000;
|
|
|
|
}
|
|
|
|
towas = to;
|
|
|
|
winner = ast_waitfor_n(watchers, pos, &to);
|
|
|
|
tmpto -= to;
|
|
|
|
totalwait -= tmpto;
|
2008-03-05 16:23:44 +00:00
|
|
|
wtd = to;
|
2007-07-30 16:02:01 +00:00
|
|
|
if (totalwait <= 0) {
|
2012-05-09 02:35:29 +00:00
|
|
|
ast_verb(3, "We've hit our timeout for this step. Dropping unanswered calls and starting the next step.\n");
|
|
|
|
clear_unanswered_calls(findme_user_list);
|
2007-07-30 16:02:01 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (winner) {
|
|
|
|
/* Need to find out which channel this is */
|
2012-05-09 01:36:07 +00:00
|
|
|
if (winner != caller) {
|
2012-01-11 21:56:12 +00:00
|
|
|
/* The winner is an outgoing channel. */
|
|
|
|
AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
|
|
|
|
if (tmpuser->ochan == winner) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tmpuser = NULL;
|
|
|
|
}
|
2012-05-09 01:36:07 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
f = ast_read(winner);
|
|
|
|
if (f) {
|
|
|
|
if (f->frametype == AST_FRAME_CONTROL) {
|
2009-11-04 14:05:12 +00:00
|
|
|
switch (f->subclass.integer) {
|
2007-07-30 16:02:01 +00:00
|
|
|
case AST_CONTROL_HANGUP:
|
2012-01-09 22:15:50 +00:00
|
|
|
ast_verb(3, "%s received a hangup frame.\n", ast_channel_name(winner));
|
2008-05-22 16:29:54 +00:00
|
|
|
if (f->data.uint32) {
|
2012-02-20 23:43:27 +00:00
|
|
|
ast_channel_hangupcause_set(winner, f->data.uint32);
|
2008-04-24 22:16:48 +00:00
|
|
|
}
|
2012-05-09 01:36:07 +00:00
|
|
|
if (!tmpuser) {
|
|
|
|
ast_verb(3, "The calling channel hungup. Need to drop everyone.\n");
|
2013-05-22 18:11:57 +00:00
|
|
|
publish_dial_end_event(caller, findme_user_list, NULL, "CANCEL");
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_frfree(f);
|
|
|
|
return NULL;
|
2007-07-30 16:02:01 +00:00
|
|
|
}
|
2012-05-09 01:36:07 +00:00
|
|
|
clear_caller(tmpuser);
|
2006-06-06 07:29:57 +00:00
|
|
|
break;
|
2007-07-30 16:02:01 +00:00
|
|
|
case AST_CONTROL_ANSWER:
|
2012-05-09 01:36:07 +00:00
|
|
|
if (!tmpuser) {
|
|
|
|
/* The caller answered? We want an outgoing channel to answer. */
|
|
|
|
break;
|
|
|
|
}
|
2012-01-09 22:15:50 +00:00
|
|
|
ast_verb(3, "%s answered %s\n", ast_channel_name(winner), ast_channel_name(caller));
|
2013-05-22 18:11:57 +00:00
|
|
|
ast_channel_publish_dial(caller, winner, NULL, "ANSWER");
|
|
|
|
publish_dial_end_event(caller, findme_user_list, winner, "CANCEL");
|
2012-05-09 02:35:29 +00:00
|
|
|
tmpuser->answered = 1;
|
2007-07-30 16:02:01 +00:00
|
|
|
/* If call has been answered, then the eventual hangup is likely to be normal hangup */
|
2012-02-20 23:43:27 +00:00
|
|
|
ast_channel_hangupcause_set(winner, AST_CAUSE_NORMAL_CLEARING);
|
|
|
|
ast_channel_hangupcause_set(caller, AST_CAUSE_NORMAL_CLEARING);
|
2007-07-30 16:02:01 +00:00
|
|
|
ast_verb(3, "Starting playback of %s\n", callfromname);
|
2012-05-09 01:36:07 +00:00
|
|
|
if (!ast_strlen_zero(tpargs->namerecloc)) {
|
|
|
|
if (!ast_streamfile(winner, callfromname, ast_channel_language(winner))) {
|
|
|
|
ast_sched_runq(ast_channel_sched(winner));
|
|
|
|
tmpuser->state = 1;
|
2008-03-05 16:23:44 +00:00
|
|
|
} else {
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
|
|
|
|
clear_caller(tmpuser);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tmpuser->state = 2;
|
|
|
|
if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
|
|
|
|
ast_sched_runq(ast_channel_sched(tmpuser->ochan));
|
|
|
|
else {
|
|
|
|
ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
|
|
|
|
clear_caller(tmpuser);
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
|
|
|
case AST_CONTROL_BUSY:
|
2012-01-09 22:15:50 +00:00
|
|
|
ast_verb(3, "%s is busy\n", ast_channel_name(winner));
|
2012-05-09 01:36:07 +00:00
|
|
|
if (tmpuser) {
|
|
|
|
/* Outbound call was busy. Drop it. */
|
2013-05-22 18:11:57 +00:00
|
|
|
ast_channel_publish_dial(caller, winner, NULL, "BUSY");
|
2012-05-09 01:36:07 +00:00
|
|
|
clear_caller(tmpuser);
|
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
|
|
|
case AST_CONTROL_CONGESTION:
|
2012-01-09 22:15:50 +00:00
|
|
|
ast_verb(3, "%s is circuit-busy\n", ast_channel_name(winner));
|
2012-05-09 01:36:07 +00:00
|
|
|
if (tmpuser) {
|
|
|
|
/* Outbound call was congested. Drop it. */
|
2013-05-22 18:11:57 +00:00
|
|
|
ast_channel_publish_dial(caller, winner, NULL, "CONGESTION");
|
2012-05-09 01:36:07 +00:00
|
|
|
clear_caller(tmpuser);
|
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
|
|
|
case AST_CONTROL_RINGING:
|
2012-01-09 22:15:50 +00:00
|
|
|
ast_verb(3, "%s is ringing\n", ast_channel_name(winner));
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
|
|
|
case AST_CONTROL_PROGRESS:
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_verb(3, "%s is making progress\n", ast_channel_name(winner));
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
|
|
|
case AST_CONTROL_VIDUPDATE:
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_verb(3, "%s requested a video update\n", ast_channel_name(winner));
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
2008-03-05 22:43:22 +00:00
|
|
|
case AST_CONTROL_SRCUPDATE:
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_verb(3, "%s requested a source update\n", ast_channel_name(winner));
|
2008-03-05 22:43:22 +00:00
|
|
|
break;
|
2007-07-30 16:02:01 +00:00
|
|
|
case AST_CONTROL_PROCEEDING:
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_verb(3, "%s is proceeding\n", ast_channel_name(winner));
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
|
|
|
case AST_CONTROL_HOLD:
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_verb(3, "%s placed call on hold\n", ast_channel_name(winner));
|
2012-05-09 01:59:14 +00:00
|
|
|
if (!tmpuser) {
|
|
|
|
/* Caller placed outgoing calls on hold. */
|
|
|
|
tpargs->pending_hold = 1;
|
|
|
|
if (f->data.ptr) {
|
|
|
|
ast_copy_string(tpargs->suggested_moh, f->data.ptr,
|
|
|
|
sizeof(tpargs->suggested_moh));
|
|
|
|
} else {
|
|
|
|
tpargs->suggested_moh[0] = '\0';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Outgoing call placed caller on hold.
|
|
|
|
*
|
|
|
|
* Ignore because the outgoing call should not be able to place
|
|
|
|
* the caller on hold until after they are bridged.
|
|
|
|
*/
|
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
|
|
|
case AST_CONTROL_UNHOLD:
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_verb(3, "%s removed call from hold\n", ast_channel_name(winner));
|
2012-05-09 01:59:14 +00:00
|
|
|
if (!tmpuser) {
|
|
|
|
/* Caller removed outgoing calls from hold. */
|
|
|
|
tpargs->pending_hold = 0;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Outgoing call removed caller from hold.
|
|
|
|
*
|
|
|
|
* Ignore because the outgoing call should not be able to place
|
|
|
|
* the caller on hold until after they are bridged.
|
|
|
|
*/
|
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
|
|
|
case AST_CONTROL_OFFHOOK:
|
|
|
|
case AST_CONTROL_FLASH:
|
|
|
|
/* Ignore going off hook and flash */
|
|
|
|
break;
|
2012-01-11 21:56:12 +00:00
|
|
|
case AST_CONTROL_CONNECTED_LINE:
|
|
|
|
if (!tmpuser) {
|
|
|
|
/*
|
|
|
|
* Hold connected line update from caller until we have a
|
|
|
|
* winner.
|
|
|
|
*/
|
|
|
|
ast_verb(3,
|
|
|
|
"%s connected line has changed. Saving it until we have a winner.\n",
|
|
|
|
ast_channel_name(winner));
|
|
|
|
ast_party_connected_line_set_init(&connected, &tpargs->connected_in);
|
|
|
|
if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
|
|
|
|
ast_party_connected_line_set(&tpargs->connected_in,
|
|
|
|
&connected, NULL);
|
|
|
|
tpargs->pending_in_connected_update = 1;
|
|
|
|
}
|
|
|
|
ast_party_connected_line_free(&connected);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE)) {
|
|
|
|
ast_verb(3, "Connected line update from %s prevented.\n",
|
|
|
|
ast_channel_name(winner));
|
|
|
|
} else {
|
|
|
|
ast_verb(3,
|
|
|
|
"%s connected line has changed. Saving it until answer.\n",
|
|
|
|
ast_channel_name(winner));
|
|
|
|
ast_party_connected_line_set_init(&connected, &tmpuser->connected);
|
|
|
|
if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
|
|
|
|
ast_party_connected_line_set(&tmpuser->connected,
|
|
|
|
&connected, NULL);
|
|
|
|
tmpuser->pending_connected_update = 1;
|
|
|
|
}
|
|
|
|
ast_party_connected_line_free(&connected);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AST_CONTROL_REDIRECTING:
|
|
|
|
/*
|
|
|
|
* Ignore because we are masking the FollowMe search progress to
|
|
|
|
* the caller.
|
|
|
|
*/
|
|
|
|
break;
|
2012-05-14 19:44:27 +00:00
|
|
|
case AST_CONTROL_PVT_CAUSE_CODE:
|
|
|
|
ast_indicate_data(caller, f->subclass.integer, f->data.ptr, f->datalen);
|
|
|
|
break;
|
2007-07-30 16:02:01 +00:00
|
|
|
case -1:
|
2012-01-09 22:15:50 +00:00
|
|
|
ast_verb(3, "%s stopped sounds\n", ast_channel_name(winner));
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
|
|
|
default:
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_debug(1, "Dunno what to do with control type %d from %s\n",
|
|
|
|
f->subclass.integer, ast_channel_name(winner));
|
2007-07-30 16:02:01 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
|
2012-05-09 17:58:11 +00:00
|
|
|
int cmp_len;
|
|
|
|
|
2012-02-20 23:43:27 +00:00
|
|
|
if (ast_channel_stream(winner))
|
2007-07-30 16:02:01 +00:00
|
|
|
ast_stopstream(winner);
|
|
|
|
tmpuser->digts = 0;
|
2009-11-04 14:05:12 +00:00
|
|
|
ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
|
2012-05-08 18:16:04 +00:00
|
|
|
if (tmpuser->ynidx < ARRAY_LEN(tmpuser->yn) - 1) {
|
2012-05-09 17:58:11 +00:00
|
|
|
tmpuser->yn[tmpuser->ynidx++] = f->subclass.integer;
|
|
|
|
} else {
|
|
|
|
/* Discard oldest digit. */
|
|
|
|
memmove(tmpuser->yn, tmpuser->yn + 1,
|
|
|
|
sizeof(tmpuser->yn) - 2 * sizeof(tmpuser->yn[0]));
|
|
|
|
tmpuser->yn[ARRAY_LEN(tmpuser->yn) - 2] = f->subclass.integer;
|
2012-05-08 18:16:04 +00:00
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
|
2012-05-09 17:58:11 +00:00
|
|
|
cmp_len = strlen(tpargs->takecall);
|
|
|
|
if (cmp_len <= tmpuser->ynidx
|
|
|
|
&& !strcmp(tmpuser->yn + (tmpuser->ynidx - cmp_len), tpargs->takecall)) {
|
2012-05-08 18:16:04 +00:00
|
|
|
ast_debug(1, "Match to take the call!\n");
|
|
|
|
ast_frfree(f);
|
|
|
|
return tmpuser->ochan;
|
|
|
|
}
|
2012-05-09 17:58:11 +00:00
|
|
|
cmp_len = strlen(tpargs->nextindp);
|
|
|
|
if (cmp_len <= tmpuser->ynidx
|
|
|
|
&& !strcmp(tmpuser->yn + (tmpuser->ynidx - cmp_len), tpargs->nextindp)) {
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_debug(1, "Declined to take the call.\n");
|
|
|
|
clear_caller(tmpuser);
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
ast_frfree(f);
|
|
|
|
} else {
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_debug(1, "we didn't get a frame. hanging up.\n");
|
|
|
|
if (!tmpuser) {
|
2012-01-11 21:56:12 +00:00
|
|
|
/* Caller hung up. */
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_verb(3, "The calling channel hungup. Need to drop everyone.\n");
|
2012-01-11 21:56:12 +00:00
|
|
|
return NULL;
|
2007-07-30 16:02:01 +00:00
|
|
|
}
|
2012-05-09 01:36:07 +00:00
|
|
|
/* Outgoing channel hung up. */
|
2013-05-22 18:11:57 +00:00
|
|
|
ast_channel_publish_dial(caller, winner, NULL, "NOANSWER");
|
2012-05-09 01:36:07 +00:00
|
|
|
clear_caller(tmpuser);
|
2008-03-05 16:23:44 +00:00
|
|
|
}
|
2012-01-11 21:56:12 +00:00
|
|
|
} else {
|
2007-07-30 16:02:01 +00:00
|
|
|
ast_debug(1, "timed out waiting for action\n");
|
2012-01-11 21:56:12 +00:00
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-05-09 01:36:07 +00:00
|
|
|
/* Unreachable. */
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
|
|
|
|
2012-05-08 21:15:58 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Find an extension willing to take the call.
|
|
|
|
*
|
|
|
|
* \param tpargs Active Followme config.
|
|
|
|
* \param caller Channel initiating the outgoing calls.
|
|
|
|
*
|
|
|
|
* \retval winner Winning outgoing call.
|
|
|
|
* \retval NULL if could not find someone to take the call.
|
|
|
|
*/
|
|
|
|
static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel *caller)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
|
|
|
struct number *nm;
|
2006-06-06 16:54:52 +00:00
|
|
|
struct ast_channel *winner = NULL;
|
2011-12-13 23:10:42 +00:00
|
|
|
char num[512];
|
2006-06-06 07:29:57 +00:00
|
|
|
int dg, idx;
|
|
|
|
char *rest, *number;
|
|
|
|
struct findme_user *tmpuser;
|
|
|
|
struct findme_user *fmuser;
|
2012-05-08 22:25:42 +00:00
|
|
|
struct findme_user_listptr findme_user_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
|
2012-05-09 01:36:07 +00:00
|
|
|
struct findme_user_listptr new_user_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2012-01-11 21:56:12 +00:00
|
|
|
for (idx = 1; !ast_check_hangup(caller); ++idx) {
|
2011-12-13 23:10:42 +00:00
|
|
|
/* Find next followme numbers to dial. */
|
|
|
|
AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
|
|
|
|
if (nm->order == idx) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!nm) {
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_verb(3, "No more steps left.\n");
|
2006-06-06 07:29:57 +00:00
|
|
|
break;
|
2011-12-13 23:10:42 +00:00
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_debug(2, "Number(s) %s timeout %ld\n", nm->number, nm->timeout);
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2012-05-09 02:35:29 +00:00
|
|
|
/*
|
|
|
|
* Put all active outgoing channels into autoservice.
|
|
|
|
*
|
|
|
|
* This needs to be done because ast_exists_extension() may put
|
|
|
|
* the caller into autoservice.
|
|
|
|
*/
|
|
|
|
AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
|
|
|
|
if (tmpuser->ochan) {
|
|
|
|
ast_autoservice_start(tmpuser->ochan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create all new outgoing calls */
|
2011-12-13 23:10:42 +00:00
|
|
|
ast_copy_string(num, nm->number, sizeof(num));
|
|
|
|
for (number = num; number; number = rest) {
|
2012-05-09 01:36:07 +00:00
|
|
|
struct ast_channel *outbound;
|
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
rest = strchr(number, '&');
|
|
|
|
if (rest) {
|
2011-12-13 23:10:42 +00:00
|
|
|
*rest++ = 0;
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
|
|
|
|
2011-12-13 23:10:42 +00:00
|
|
|
/* We check if the extension exists, before creating the ast_channel struct */
|
2012-02-29 16:52:47 +00:00
|
|
|
if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(ast_channel_caller(caller)->id.number.valid, ast_channel_caller(caller)->id.number.str, NULL))) {
|
2010-12-07 00:31:20 +00:00
|
|
|
ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
|
2011-12-13 23:10:42 +00:00
|
|
|
continue;
|
2010-12-07 00:31:20 +00:00
|
|
|
}
|
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
tmpuser = ast_calloc(1, sizeof(*tmpuser));
|
|
|
|
if (!tmpuser) {
|
2011-12-13 23:10:42 +00:00
|
|
|
continue;
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-05-09 01:36:07 +00:00
|
|
|
if (ast_strlen_zero(tpargs->context)) {
|
|
|
|
snprintf(tmpuser->dialarg, sizeof(tmpuser->dialarg), "%s%s",
|
|
|
|
number,
|
|
|
|
ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_DISABLEOPTIMIZATION)
|
2012-05-09 01:59:14 +00:00
|
|
|
? "/n" : "/m");
|
2011-12-13 23:10:42 +00:00
|
|
|
} else {
|
2012-05-09 01:36:07 +00:00
|
|
|
snprintf(tmpuser->dialarg, sizeof(tmpuser->dialarg), "%s@%s%s",
|
|
|
|
number, tpargs->context,
|
|
|
|
ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_DISABLEOPTIMIZATION)
|
2012-05-09 01:59:14 +00:00
|
|
|
? "/n" : "/m");
|
2012-05-09 01:36:07 +00:00
|
|
|
}
|
|
|
|
|
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
|
|
|
outbound = ast_request("Local", ast_channel_nativeformats(caller), NULL, caller,
|
2012-05-09 01:36:07 +00:00
|
|
|
tmpuser->dialarg, &dg);
|
|
|
|
if (!outbound) {
|
|
|
|
ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n",
|
|
|
|
tmpuser->dialarg, ast_cause2str(dg));
|
2011-12-13 23:10:42 +00:00
|
|
|
ast_free(tmpuser);
|
2012-05-09 01:36:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_channel_lock_both(caller, outbound);
|
|
|
|
ast_connected_line_copy_from_caller(ast_channel_connected(outbound), ast_channel_caller(caller));
|
|
|
|
ast_channel_inherit_variables(caller, outbound);
|
|
|
|
ast_channel_datastore_inherit(caller, outbound);
|
|
|
|
ast_channel_language_set(outbound, ast_channel_language(caller));
|
accountcode: Slightly change accountcode propagation.
The previous behavior was to simply set the accountcode of an outgoing
channel to the accountcode of the channel initiating the call. It was
done this way a long time ago to allow the accountcode set on the SIP/100
channel to be propagated to a local channel so the dialplan execution on
the Local;2 channel would have the SIP/100 accountcode available.
SIP/100 -> Local;1/Local;2 -> SIP/200
Propagating the SIP/100 accountcode to the local channels is very useful.
Without any dialplan manipulation, all channels in this call would have
the same accountcode.
Using dialplan, you can set a different accountcode on the SIP/200 channel
either by setting the accountcode on the Local;2 channel or by the Dial
application's b(pre-dial), M(macro) or U(gosub) options, or by the
FollowMe application's b(pre-dial) option, or by the Queue application's
macro or gosub options. Before Asterisk v12, the altered accountcode on
SIP/200 will remain until the local channels optimize out and the
accountcode would change to the SIP/100 accountcode.
Asterisk v1.8 attempted to add peeraccount support but ultimately had to
punt on the support. The peeraccount support was rendered useless because
of how the CDR code needed to unconditionally force the caller's
accountcode onto the peer channel's accountcode. The CEL events were thus
intentionally made to always use the channel's accountcode as the
peeraccount value.
With the arrival of Asterisk v12, the situation has improved somewhat so
peeraccount support can be made to work. Using the indicated example, the
the accountcode values become as follows when the peeraccount is set on
SIP/100 before calling SIP/200:
SIP/100 ---> Local;1 ---- Local;2 ---> SIP/200
acct: 100 \/ acct: 200 \/ acct: 100 \/ acct: 200
peer: 200 /\ peer: 100 /\ peer: 200 /\ peer: 100
If a channel already has an accountcode it can only change by the
following explicit user actions:
1) A channel originate method that can specify an accountcode to use.
2) The calling channel propagating its non-empty peeraccount or its
non-empty accountcode if the peeraccount was empty to the outgoing
channel's accountcode before initiating the dial. e.g., Dial and
FollowMe. The exception to this propagation method is Queue. Queue will
only propagate peeraccounts this way only if the outgoing channel does not
have an accountcode.
3) Dialplan using CHANNEL(accountcode).
4) Dialplan using CHANNEL(peeraccount) on the other end of a local
channel pair.
If a channel does not have an accountcode it can get one from the
following places:
1) The channel driver's configuration at channel creation.
2) Explicit user action as already indicated.
3) Entering a basic or stasis-mixing bridge from a peer channel's
peeraccount value.
You can specify the accountcode for an outgoing channel by setting the
CHANNEL(peeraccount) before using the Dial, FollowMe, and Queue
applications. Queue adds the wrinkle that it will not overwrite an
existing accountcode on the outgoing channel with the calling channels
values.
Accountcode and peeraccount values propagate to an outgoing channel before
dialing. Accountcodes also propagate when channels enter or leave a basic
or stasis-mixing bridge. The peeraccount value only makes sense for
mixing bridges with two channels; it is meaningless otherwise.
* Made peeraccount functional by changing accountcode propagation as
described above.
* Fixed CEL extracting the wrong ie value for the peeraccount. This was
done intentionally in Asterisk v1.8 when that version had to punt on
peeraccount.
* Fixed a few places dealing with accountcodes that were reading from
channels without the lock held.
AFS-65 #close
Review: https://reviewboard.asterisk.org/r/3601/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419520 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-24 22:48:38 +00:00
|
|
|
ast_channel_req_accountcodes(outbound, caller, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_channel_musicclass_set(outbound, ast_channel_musicclass(caller));
|
|
|
|
ast_channel_unlock(outbound);
|
|
|
|
ast_channel_unlock(caller);
|
|
|
|
|
|
|
|
tmpuser->ochan = outbound;
|
|
|
|
tmpuser->state = 0;
|
2012-05-09 02:35:29 +00:00
|
|
|
AST_LIST_INSERT_TAIL(&new_user_list, tmpuser, entry);
|
|
|
|
}
|
2012-05-09 01:36:07 +00:00
|
|
|
|
2012-05-15 16:53:09 +00:00
|
|
|
/*
|
|
|
|
* PREDIAL: Run gosub on all of the new callee channels
|
|
|
|
*
|
|
|
|
* We run the callee predial before ast_call() in case the user
|
|
|
|
* wishes to do something on the newly created channels before
|
|
|
|
* the channel does anything important.
|
|
|
|
*/
|
|
|
|
if (tpargs->predial_callee && !AST_LIST_EMPTY(&new_user_list)) {
|
|
|
|
/* Put caller into autoservice. */
|
|
|
|
ast_autoservice_start(caller);
|
|
|
|
|
|
|
|
/* Run predial on all new outgoing calls. */
|
|
|
|
AST_LIST_TRAVERSE(&new_user_list, tmpuser, entry) {
|
|
|
|
ast_pre_call(tmpuser->ochan, tpargs->predial_callee);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Take caller out of autoservice. */
|
|
|
|
if (ast_autoservice_stop(caller)) {
|
|
|
|
/*
|
|
|
|
* Caller hungup.
|
|
|
|
*
|
|
|
|
* Destoy all new outgoing calls.
|
|
|
|
*/
|
|
|
|
while ((tmpuser = AST_LIST_REMOVE_HEAD(&new_user_list, entry))) {
|
|
|
|
destroy_calling_node(tmpuser);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Take all active outgoing channels out of autoservice. */
|
|
|
|
AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
|
|
|
|
if (tmpuser->ochan) {
|
|
|
|
ast_autoservice_stop(tmpuser->ochan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-09 02:35:29 +00:00
|
|
|
/* Start all new outgoing calls */
|
|
|
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&new_user_list, tmpuser, entry) {
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_verb(3, "calling Local/%s\n", tmpuser->dialarg);
|
2012-05-09 02:35:29 +00:00
|
|
|
if (ast_call(tmpuser->ochan, tmpuser->dialarg, 0)) {
|
2012-05-09 01:36:07 +00:00
|
|
|
ast_verb(3, "couldn't reach at this number.\n");
|
2012-05-09 02:35:29 +00:00
|
|
|
AST_LIST_REMOVE_CURRENT(entry);
|
2012-05-09 01:36:07 +00:00
|
|
|
|
|
|
|
/* Destroy this failed new outgoing call. */
|
|
|
|
destroy_calling_node(tmpuser);
|
2013-05-22 18:11:57 +00:00
|
|
|
continue;
|
2011-12-13 23:10:42 +00:00
|
|
|
}
|
2013-05-22 18:11:57 +00:00
|
|
|
|
|
|
|
ast_channel_publish_dial(caller, tmpuser->ochan, tmpuser->dialarg, NULL);
|
2011-12-13 23:10:42 +00:00
|
|
|
}
|
2012-05-09 02:35:29 +00:00
|
|
|
AST_LIST_TRAVERSE_SAFE_END;
|
|
|
|
|
|
|
|
/* Take all active outgoing channels out of autoservice. */
|
|
|
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
|
|
|
|
if (tmpuser->ochan && ast_autoservice_stop(tmpuser->ochan)) {
|
|
|
|
/* Existing outgoing call hungup. */
|
|
|
|
AST_LIST_REMOVE_CURRENT(entry);
|
|
|
|
destroy_calling_node(tmpuser);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AST_LIST_TRAVERSE_SAFE_END;
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-05-09 01:36:07 +00:00
|
|
|
if (AST_LIST_EMPTY(&new_user_list)) {
|
|
|
|
/* No new channels remain at this order level. If there were any at all. */
|
2011-12-13 23:10:42 +00:00
|
|
|
continue;
|
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-05-09 01:36:07 +00:00
|
|
|
/* Add new outgoing channels to the findme list. */
|
|
|
|
AST_LIST_APPEND_LIST(&findme_user_list, &new_user_list, entry);
|
|
|
|
|
|
|
|
winner = wait_for_winner(&findme_user_list, nm, caller, tpargs);
|
2012-01-11 21:56:12 +00:00
|
|
|
if (!winner) {
|
2012-05-09 02:35:29 +00:00
|
|
|
/* Remove all dead outgoing nodes. */
|
|
|
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
|
|
|
|
if (!tmpuser->ochan) {
|
|
|
|
AST_LIST_REMOVE_CURRENT(entry);
|
|
|
|
destroy_calling_node(tmpuser);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AST_LIST_TRAVERSE_SAFE_END;
|
2012-01-11 21:56:12 +00:00
|
|
|
continue;
|
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-01-11 21:56:12 +00:00
|
|
|
/* Destroy losing calls up to the winner. The rest will be destroyed later. */
|
2012-05-08 22:25:42 +00:00
|
|
|
while ((fmuser = AST_LIST_REMOVE_HEAD(&findme_user_list, entry))) {
|
2012-01-11 21:56:12 +00:00
|
|
|
if (fmuser->ochan == winner) {
|
2012-05-09 01:36:07 +00:00
|
|
|
/*
|
|
|
|
* Pass any connected line info up.
|
|
|
|
*
|
|
|
|
* NOTE: This code must be in line with destroy_calling_node().
|
|
|
|
*/
|
2012-01-11 21:56:12 +00:00
|
|
|
tpargs->connected_out = fmuser->connected;
|
|
|
|
tpargs->pending_out_connected_update = fmuser->pending_connected_update;
|
|
|
|
ast_free(fmuser);
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
/* Destroy losing call. */
|
2012-05-09 01:36:07 +00:00
|
|
|
destroy_calling_node(fmuser);
|
2011-12-13 23:10:42 +00:00
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
2012-01-11 21:56:12 +00:00
|
|
|
break;
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
2012-05-08 22:25:42 +00:00
|
|
|
destroy_calling_tree(&findme_user_list);
|
2012-05-08 21:15:58 +00:00
|
|
|
return winner;
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
|
|
|
|
2008-08-25 16:02:56 +00:00
|
|
|
static struct call_followme *find_realtime(const char *name)
|
|
|
|
{
|
2012-01-06 16:50:08 +00:00
|
|
|
struct ast_variable *var;
|
|
|
|
struct ast_variable *v;
|
2008-08-25 16:02:56 +00:00
|
|
|
struct ast_config *cfg;
|
|
|
|
const char *catg;
|
2012-01-06 16:50:08 +00:00
|
|
|
struct call_followme *new_follower;
|
|
|
|
struct ast_str *str;
|
2008-08-25 16:02:56 +00:00
|
|
|
|
2012-01-06 16:50:08 +00:00
|
|
|
str = ast_str_create(16);
|
|
|
|
if (!str) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ast_load_realtime("followme", "name", name, SENTINEL);
|
2008-08-25 16:02:56 +00:00
|
|
|
if (!var) {
|
2012-01-06 16:50:08 +00:00
|
|
|
ast_free(str);
|
2008-08-25 16:02:56 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-01-06 16:50:08 +00:00
|
|
|
if (!(new_follower = alloc_profile(name))) {
|
|
|
|
ast_variables_destroy(var);
|
|
|
|
ast_free(str);
|
2008-08-25 16:02:56 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (v = var; v; v = v->next) {
|
|
|
|
if (!strcasecmp(v->name, "active")) {
|
|
|
|
if (ast_false(v->value)) {
|
2012-01-06 16:50:08 +00:00
|
|
|
ast_mutex_destroy(&new_follower->lock);
|
|
|
|
ast_free(new_follower);
|
|
|
|
ast_variables_destroy(var);
|
|
|
|
ast_free(str);
|
2008-08-25 16:02:56 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else {
|
2012-01-06 16:50:08 +00:00
|
|
|
profile_set_param(new_follower, v->name, v->value, 0, 0);
|
2008-08-25 16:02:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_variables_destroy(var);
|
2012-01-06 16:50:08 +00:00
|
|
|
new_follower->realtime = 1;
|
2008-08-25 16:02:56 +00:00
|
|
|
|
|
|
|
/* Load numbers */
|
2012-01-06 16:50:08 +00:00
|
|
|
cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
|
|
|
|
name, SENTINEL);
|
|
|
|
if (!cfg) {
|
|
|
|
ast_mutex_destroy(&new_follower->lock);
|
|
|
|
ast_free(new_follower);
|
|
|
|
ast_free(str);
|
2008-08-25 16:02:56 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
|
2012-01-06 16:50:08 +00:00
|
|
|
const char *numstr;
|
|
|
|
const char *timeoutstr;
|
|
|
|
const char *ordstr;
|
2008-08-25 16:02:56 +00:00
|
|
|
int timeout;
|
|
|
|
struct number *cur;
|
2012-01-06 16:50:08 +00:00
|
|
|
|
2008-08-25 16:02:56 +00:00
|
|
|
if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
|
|
|
|
continue;
|
|
|
|
}
|
2012-01-06 16:50:08 +00:00
|
|
|
if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
|
|
|
|
|| sscanf(timeoutstr, "%30d", &timeout) != 1
|
|
|
|
|| timeout < 1) {
|
2008-08-25 16:02:56 +00:00
|
|
|
timeout = 25;
|
|
|
|
}
|
|
|
|
/* This one has to exist; it was part of the query */
|
|
|
|
ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
|
2008-12-13 08:36:35 +00:00
|
|
|
ast_str_set(&str, 0, "%s", numstr);
|
|
|
|
if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
|
2012-01-06 16:50:08 +00:00
|
|
|
AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
|
2008-08-25 16:02:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ast_config_destroy(cfg);
|
|
|
|
|
2012-01-06 16:50:08 +00:00
|
|
|
ast_free(str);
|
|
|
|
return new_follower;
|
2008-08-25 16:02:56 +00:00
|
|
|
}
|
|
|
|
|
2008-11-09 01:27:00 +00:00
|
|
|
static void end_bridge_callback(void *data)
|
|
|
|
{
|
|
|
|
char buf[80];
|
|
|
|
time_t end;
|
|
|
|
struct ast_channel *chan = data;
|
|
|
|
|
|
|
|
time(&end);
|
|
|
|
|
|
|
|
ast_channel_lock(chan);
|
2013-06-17 03:00:38 +00:00
|
|
|
snprintf(buf, sizeof(buf), "%d", ast_channel_get_up_time(chan));
|
|
|
|
pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
|
|
|
|
snprintf(buf, sizeof(buf), "%d", ast_channel_get_duration(chan));
|
|
|
|
pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
|
2008-11-09 01:27:00 +00:00
|
|
|
ast_channel_unlock(chan);
|
|
|
|
}
|
|
|
|
|
2008-11-18 18:31:08 +00:00
|
|
|
static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
|
|
|
|
{
|
|
|
|
bconfig->end_bridge_callback_data = originator;
|
|
|
|
}
|
|
|
|
|
2009-05-21 21:13:09 +00:00
|
|
|
static int app_exec(struct ast_channel *chan, const char *data)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
2012-05-08 21:41:58 +00:00
|
|
|
struct fm_args *targs;
|
2006-06-06 07:29:57 +00:00
|
|
|
struct ast_bridge_config config;
|
2006-09-05 20:36:27 +00:00
|
|
|
struct call_followme *f;
|
2006-06-06 07:29:57 +00:00
|
|
|
struct number *nm, *newnm;
|
|
|
|
int res = 0;
|
|
|
|
char *argstr;
|
|
|
|
struct ast_channel *caller;
|
|
|
|
struct ast_channel *outbound;
|
|
|
|
AST_DECLARE_APP_ARGS(args,
|
|
|
|
AST_APP_ARG(followmeid);
|
|
|
|
AST_APP_ARG(options);
|
|
|
|
);
|
2012-05-15 16:53:09 +00:00
|
|
|
char *opt_args[FOLLOWMEFLAG_ARG_ARRAY_SIZE];
|
2007-07-30 16:02:01 +00:00
|
|
|
|
|
|
|
if (ast_strlen_zero(data)) {
|
|
|
|
ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
|
|
|
|
return -1;
|
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2011-12-13 23:10:42 +00:00
|
|
|
argstr = ast_strdupa((char *) data);
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
AST_STANDARD_APP_ARGS(args, argstr);
|
|
|
|
|
|
|
|
if (ast_strlen_zero(args.followmeid)) {
|
|
|
|
ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
|
2006-06-06 07:29:57 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-05-08 21:41:58 +00:00
|
|
|
targs = ast_calloc(1, sizeof(*targs));
|
|
|
|
if (!targs) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
AST_RWLIST_RDLOCK(&followmes);
|
|
|
|
AST_RWLIST_TRAVERSE(&followmes, f, entry) {
|
2006-09-05 20:36:27 +00:00
|
|
|
if (!strcasecmp(f->name, args.followmeid) && (f->active))
|
|
|
|
break;
|
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
AST_RWLIST_UNLOCK(&followmes);
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "New profile %s.\n", args.followmeid);
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-08-25 16:02:56 +00:00
|
|
|
if (!f) {
|
|
|
|
f = find_realtime(args.followmeid);
|
|
|
|
}
|
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
if (!f) {
|
|
|
|
ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
|
2012-05-08 21:41:58 +00:00
|
|
|
ast_free(targs);
|
2007-07-30 16:02:01 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
/* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
|
2012-05-08 21:41:58 +00:00
|
|
|
if (args.options) {
|
2012-05-15 16:53:09 +00:00
|
|
|
ast_app_parse_options(followme_opts, &targs->followmeflags, opt_args, args.options);
|
2012-05-08 21:41:58 +00:00
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
/* Lock the profile lock and copy out everything we need to run with before unlocking it again */
|
|
|
|
ast_mutex_lock(&f->lock);
|
2012-05-08 21:41:58 +00:00
|
|
|
targs->mohclass = ast_strdupa(f->moh);
|
|
|
|
ast_copy_string(targs->context, f->context, sizeof(targs->context));
|
|
|
|
ast_copy_string(targs->takecall, f->takecall, sizeof(targs->takecall));
|
|
|
|
ast_copy_string(targs->nextindp, f->nextindp, sizeof(targs->nextindp));
|
|
|
|
ast_copy_string(targs->callfromprompt, f->callfromprompt, sizeof(targs->callfromprompt));
|
|
|
|
ast_copy_string(targs->norecordingprompt, f->norecordingprompt, sizeof(targs->norecordingprompt));
|
|
|
|
ast_copy_string(targs->optionsprompt, f->optionsprompt, sizeof(targs->optionsprompt));
|
|
|
|
ast_copy_string(targs->plsholdprompt, f->plsholdprompt, sizeof(targs->plsholdprompt));
|
|
|
|
ast_copy_string(targs->statusprompt, f->statusprompt, sizeof(targs->statusprompt));
|
|
|
|
ast_copy_string(targs->sorryprompt, f->sorryprompt, sizeof(targs->sorryprompt));
|
2007-07-30 16:02:01 +00:00
|
|
|
/* Copy the numbers we're going to use into another list in case the master list should get modified
|
|
|
|
(and locked) while we're trying to do a follow-me */
|
2012-05-08 21:41:58 +00:00
|
|
|
AST_LIST_HEAD_INIT_NOLOCK(&targs->cnumbers);
|
2007-07-30 16:02:01 +00:00
|
|
|
AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
|
|
|
|
newnm = create_followme_number(nm->number, nm->timeout, nm->order);
|
2011-12-13 23:10:42 +00:00
|
|
|
if (newnm) {
|
2012-05-08 21:41:58 +00:00
|
|
|
AST_LIST_INSERT_TAIL(&targs->cnumbers, newnm, entry);
|
2011-12-13 23:10:42 +00:00
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
}
|
|
|
|
ast_mutex_unlock(&f->lock);
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-05-15 16:53:09 +00:00
|
|
|
/* PREDIAL: Preprocess any callee gosub arguments. */
|
|
|
|
if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_PREDIAL_CALLEE)
|
|
|
|
&& !ast_strlen_zero(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE])) {
|
|
|
|
ast_replace_subargument_delimiter(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE]);
|
2012-06-14 23:22:53 +00:00
|
|
|
targs->predial_callee =
|
|
|
|
ast_app_expand_sub_args(chan, opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE]);
|
2012-05-15 16:53:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* PREDIAL: Run gosub on the caller's channel */
|
|
|
|
if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_PREDIAL_CALLER)
|
|
|
|
&& !ast_strlen_zero(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER])) {
|
|
|
|
ast_replace_subargument_delimiter(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER]);
|
2012-06-14 23:22:53 +00:00
|
|
|
ast_app_exec_sub(NULL, chan, opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER], 0);
|
2012-05-15 16:53:09 +00:00
|
|
|
}
|
|
|
|
|
2012-01-11 21:56:12 +00:00
|
|
|
/* Forget the 'N' option if the call is already up. */
|
2012-02-20 23:43:27 +00:00
|
|
|
if (ast_channel_state(chan) == AST_STATE_UP) {
|
2012-05-08 21:41:58 +00:00
|
|
|
ast_clear_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER);
|
2012-01-11 21:56:12 +00:00
|
|
|
}
|
2011-01-26 23:41:55 +00:00
|
|
|
|
2012-05-08 21:41:58 +00:00
|
|
|
if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER)) {
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_indicate(chan, AST_CONTROL_RINGING);
|
2011-01-26 23:41:55 +00:00
|
|
|
} else {
|
|
|
|
/* Answer the call */
|
2012-02-20 23:43:27 +00:00
|
|
|
if (ast_channel_state(chan) != AST_STATE_UP) {
|
2011-01-26 23:41:55 +00:00
|
|
|
ast_answer(chan);
|
2012-01-11 21:56:12 +00:00
|
|
|
}
|
2011-01-26 23:41:55 +00:00
|
|
|
|
2012-05-08 21:41:58 +00:00
|
|
|
if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_STATUSMSG)) {
|
|
|
|
ast_stream_and_wait(chan, targs->statusprompt, "");
|
|
|
|
}
|
2011-01-26 23:41:55 +00:00
|
|
|
|
2012-05-08 21:41:58 +00:00
|
|
|
if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_RECORDNAME)) {
|
2012-01-11 21:56:12 +00:00
|
|
|
int duration = 5;
|
|
|
|
|
2012-05-08 21:41:58 +00:00
|
|
|
snprintf(targs->namerecloc, sizeof(targs->namerecloc), "%s/followme.%s",
|
2012-01-24 20:12:09 +00:00
|
|
|
ast_config_AST_SPOOL_DIR, ast_channel_uniqueid(chan));
|
2012-05-08 21:41:58 +00:00
|
|
|
if (ast_play_and_record(chan, "vm-rec-name", targs->namerecloc, 5, "sln", &duration,
|
2012-01-11 21:56:12 +00:00
|
|
|
NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) {
|
2011-01-26 23:41:55 +00:00
|
|
|
goto outrun;
|
2012-01-11 21:56:12 +00:00
|
|
|
}
|
2012-05-08 21:41:58 +00:00
|
|
|
if (!ast_fileexists(targs->namerecloc, NULL, ast_channel_language(chan))) {
|
|
|
|
targs->namerecloc[0] = '\0';
|
2012-01-11 21:56:12 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-26 23:41:55 +00:00
|
|
|
|
2012-05-08 21:41:58 +00:00
|
|
|
if (!ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) {
|
|
|
|
if (ast_streamfile(chan, targs->plsholdprompt, ast_channel_language(chan))) {
|
2011-01-26 23:41:55 +00:00
|
|
|
goto outrun;
|
2012-05-08 21:41:58 +00:00
|
|
|
}
|
2011-01-26 23:41:55 +00:00
|
|
|
if (ast_waitstream(chan, "") < 0)
|
|
|
|
goto outrun;
|
|
|
|
}
|
2013-08-22 23:15:14 +00:00
|
|
|
ast_moh_start(chan, targs->mohclass, NULL);
|
2009-11-23 22:37:39 +00:00
|
|
|
}
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_channel_lock(chan);
|
2012-05-08 21:41:58 +00:00
|
|
|
ast_connected_line_copy_from_caller(&targs->connected_in, ast_channel_caller(chan));
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_channel_unlock(chan);
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-05-08 21:41:58 +00:00
|
|
|
outbound = findmeexec(targs, chan);
|
2012-05-08 21:15:58 +00:00
|
|
|
if (!outbound) {
|
2012-05-08 21:41:58 +00:00
|
|
|
if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER)) {
|
2012-02-20 23:43:27 +00:00
|
|
|
if (ast_channel_state(chan) != AST_STATE_UP) {
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_answer(chan);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ast_moh_stop(chan);
|
2011-01-26 23:41:55 +00:00
|
|
|
}
|
|
|
|
|
2012-05-08 21:41:58 +00:00
|
|
|
if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) {
|
|
|
|
ast_stream_and_wait(chan, targs->sorryprompt, "");
|
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
res = 0;
|
|
|
|
} else {
|
|
|
|
caller = chan;
|
|
|
|
/* Bridge the two channels. */
|
2008-03-05 16:23:44 +00:00
|
|
|
|
|
|
|
memset(&config, 0, sizeof(config));
|
2007-07-30 16:02:01 +00:00
|
|
|
ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
|
|
|
|
ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
|
|
|
|
ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
|
2008-10-31 18:55:33 +00:00
|
|
|
config.end_bridge_callback = end_bridge_callback;
|
2008-11-09 01:27:00 +00:00
|
|
|
config.end_bridge_callback_data = chan;
|
2008-11-18 18:31:08 +00:00
|
|
|
config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
|
2008-03-05 16:23:44 +00:00
|
|
|
|
2012-01-11 21:56:12 +00:00
|
|
|
/* Update connected line to caller if available. */
|
2012-05-08 21:41:58 +00:00
|
|
|
if (targs->pending_out_connected_update) {
|
|
|
|
if (ast_channel_connected_line_sub(outbound, caller, &targs->connected_out, 0) &&
|
|
|
|
ast_channel_connected_line_macro(outbound, caller, &targs->connected_out, 1, 0)) {
|
|
|
|
ast_channel_update_connected_line(caller, &targs->connected_out, NULL);
|
2012-01-11 21:56:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-08 21:41:58 +00:00
|
|
|
if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER)) {
|
2012-02-20 23:43:27 +00:00
|
|
|
if (ast_channel_state(caller) != AST_STATE_UP) {
|
2012-01-11 21:56:12 +00:00
|
|
|
ast_answer(caller);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ast_moh_stop(caller);
|
|
|
|
}
|
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
/* Be sure no generators are left on it */
|
|
|
|
ast_deactivate_generator(caller);
|
|
|
|
/* Make sure channels are compatible */
|
|
|
|
res = ast_channel_make_compatible(caller, outbound);
|
|
|
|
if (res < 0) {
|
2012-01-09 22:15:50 +00:00
|
|
|
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(caller), ast_channel_name(outbound));
|
2012-06-29 17:02:32 +00:00
|
|
|
ast_autoservice_chan_hangup_peer(caller, outbound);
|
2007-07-30 16:02:01 +00:00
|
|
|
goto outrun;
|
2006-09-05 20:36:27 +00:00
|
|
|
}
|
2012-01-11 21:56:12 +00:00
|
|
|
|
|
|
|
/* Update connected line to winner if changed. */
|
2012-05-08 21:41:58 +00:00
|
|
|
if (targs->pending_in_connected_update) {
|
|
|
|
if (ast_channel_connected_line_sub(caller, outbound, &targs->connected_in, 0) &&
|
|
|
|
ast_channel_connected_line_macro(caller, outbound, &targs->connected_in, 0, 0)) {
|
|
|
|
ast_channel_update_connected_line(outbound, &targs->connected_in, NULL);
|
2012-01-11 21:56:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-09 01:59:14 +00:00
|
|
|
/* Put winner on hold if caller requested. */
|
|
|
|
if (targs->pending_hold) {
|
|
|
|
if (ast_strlen_zero(targs->suggested_moh)) {
|
|
|
|
ast_indicate_data(outbound, AST_CONTROL_HOLD, NULL, 0);
|
|
|
|
} else {
|
|
|
|
ast_indicate_data(outbound, AST_CONTROL_HOLD,
|
|
|
|
targs->suggested_moh, strlen(targs->suggested_moh) + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-05 16:23:44 +00:00
|
|
|
res = ast_bridge_call(caller, outbound, &config);
|
2006-09-05 20:36:27 +00:00
|
|
|
}
|
2007-07-30 16:02:01 +00:00
|
|
|
|
2011-12-13 23:10:42 +00:00
|
|
|
outrun:
|
2012-05-08 21:41:58 +00:00
|
|
|
while ((nm = AST_LIST_REMOVE_HEAD(&targs->cnumbers, entry))) {
|
2012-05-08 20:32:11 +00:00
|
|
|
ast_free(nm);
|
|
|
|
}
|
2012-05-08 21:41:58 +00:00
|
|
|
if (!ast_strlen_zero(targs->namerecloc)) {
|
|
|
|
unlink(targs->namerecloc);
|
2012-05-08 20:32:11 +00:00
|
|
|
}
|
2012-06-14 23:22:53 +00:00
|
|
|
ast_free((char *) targs->predial_callee);
|
2012-05-08 21:41:58 +00:00
|
|
|
ast_party_connected_line_free(&targs->connected_in);
|
|
|
|
ast_party_connected_line_free(&targs->connected_out);
|
|
|
|
ast_free(targs);
|
2012-05-08 20:32:11 +00:00
|
|
|
|
2008-08-25 16:02:56 +00:00
|
|
|
if (f->realtime) {
|
|
|
|
/* Not in list */
|
|
|
|
free_numbers(f);
|
|
|
|
ast_free(f);
|
|
|
|
}
|
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2006-08-21 02:11:39 +00:00
|
|
|
static int unload_module(void)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
2006-09-05 20:36:27 +00:00
|
|
|
struct call_followme *f;
|
2006-08-21 02:11:39 +00:00
|
|
|
|
2006-06-06 20:44:50 +00:00
|
|
|
ast_unregister_application(app);
|
2006-08-21 02:11:39 +00:00
|
|
|
|
2006-06-06 07:29:57 +00:00
|
|
|
/* Free Memory. Yeah! I'm free! */
|
2007-07-30 16:02:01 +00:00
|
|
|
AST_RWLIST_WRLOCK(&followmes);
|
|
|
|
while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
|
2006-06-06 07:29:57 +00:00
|
|
|
free_numbers(f);
|
2007-06-06 21:20:11 +00:00
|
|
|
ast_free(f);
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
2006-08-21 02:11:39 +00:00
|
|
|
|
2007-07-30 16:02:01 +00:00
|
|
|
AST_RWLIST_UNLOCK(&followmes);
|
2006-08-21 02:11:39 +00:00
|
|
|
|
2006-06-06 20:44:50 +00:00
|
|
|
return 0;
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
|
|
|
|
2012-10-01 23:22:50 +00:00
|
|
|
/*!
|
|
|
|
* \brief Load the module
|
|
|
|
*
|
|
|
|
* Module loading including tests for configuration or dependencies.
|
|
|
|
* This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
|
|
|
|
* or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
|
|
|
|
* tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
|
|
|
|
* configuration file or other non-critical problem return
|
|
|
|
* AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
|
|
|
|
*/
|
2006-08-21 02:11:39 +00:00
|
|
|
static int load_module(void)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
2007-08-16 21:09:46 +00:00
|
|
|
if(!reload_followme(0))
|
2006-08-31 21:00:20 +00:00
|
|
|
return AST_MODULE_LOAD_DECLINE;
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-11-05 02:08:39 +00:00
|
|
|
return ast_register_application_xml(app, app_exec);
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
|
|
|
|
2006-08-21 02:11:39 +00:00
|
|
|
static int reload(void)
|
2006-06-06 07:29:57 +00:00
|
|
|
{
|
2007-08-16 21:09:46 +00:00
|
|
|
reload_followme(1);
|
2006-06-06 07:29:57 +00:00
|
|
|
|
2008-03-05 16:23:44 +00:00
|
|
|
return 0;
|
2006-06-06 07:29:57 +00:00
|
|
|
}
|
|
|
|
|
2006-08-21 02:11:39 +00:00
|
|
|
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
|
2014-07-25 16:47:17 +00:00
|
|
|
.support_level = AST_MODULE_SUPPORT_CORE,
|
2006-08-21 02:11:39 +00:00
|
|
|
.load = load_module,
|
|
|
|
.unload = unload_module,
|
|
|
|
.reload = reload,
|
|
|
|
);
|