app_followme.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * A full-featured Find-Me/Follow-Me Application
00005  * 
00006  * Copyright (C) 2005-2006, BJ Weschke All Rights Reserved.
00007  *
00008  * BJ Weschke <bweschke@btwtech.com>
00009  *
00010  * This code is released by the author with no restrictions on usage.
00011  *
00012  * See http://www.asterisk.org for more information about
00013  * the Asterisk project. Please do not directly contact
00014  * any of the maintainers of this project for assistance;
00015  * the project provides a web site, mailing lists and IRC
00016  * channels for your use.
00017  *
00018  */
00019 
00020 /*! \file
00021  *
00022  * \brief Find-Me Follow-Me application
00023  *
00024  * \author BJ Weschke <bweschke@btwtech.com>
00025  *
00026  * \ingroup applications
00027  */
00028 
00029 /*! \li \ref app_followme.c uses the configuration file \ref followme.conf
00030  * \addtogroup configuration_file Configuration Files
00031  */
00032 
00033 /*! 
00034  * \page followme.conf followme.conf
00035  * \verbinclude followme.conf.sample
00036  */
00037 
00038 /*** MODULEINFO
00039    <support_level>core</support_level>
00040  ***/
00041 
00042 #include "asterisk.h"
00043 
00044 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
00045 
00046 #include <signal.h>
00047 
00048 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00049 #include "asterisk/lock.h"
00050 #include "asterisk/file.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/pbx.h"
00053 #include "asterisk/module.h"
00054 #include "asterisk/translate.h"
00055 #include "asterisk/say.h"
00056 #include "asterisk/features.h"
00057 #include "asterisk/musiconhold.h"
00058 #include "asterisk/cli.h"
00059 #include "asterisk/manager.h"
00060 #include "asterisk/config.h"
00061 #include "asterisk/utils.h"
00062 #include "asterisk/causes.h"
00063 #include "asterisk/astdb.h"
00064 #include "asterisk/dsp.h"
00065 #include "asterisk/app.h"
00066 #include "asterisk/stasis_channels.h"
00067 
00068 /*** DOCUMENTATION
00069    <application name="FollowMe" language="en_US">
00070       <synopsis>
00071          Find-Me/Follow-Me application.
00072       </synopsis>
00073       <syntax>
00074          <parameter name="followmeid" required="true" />
00075          <parameter name="options">
00076             <optionlist>
00077                <option name="a">
00078                   <para>Record the caller's name so it can be announced to the
00079                   callee on each step.</para>
00080                </option>
00081                <option name="B" argsep="^">
00082                   <para>Before initiating the outgoing call(s), Gosub to the specified
00083                   location using the current channel.</para>
00084                   <argument name="context" required="false" />
00085                   <argument name="exten" required="false" />
00086                   <argument name="priority" required="true" hasparams="optional" argsep="^">
00087                      <argument name="arg1" multiple="true" required="true" />
00088                      <argument name="argN" />
00089                   </argument>
00090                </option>
00091                <option name="b" argsep="^">
00092                   <para>Before initiating an outgoing call, Gosub to the specified
00093                   location using the newly created channel.  The Gosub will be
00094                   executed for each destination channel.</para>
00095                   <argument name="context" required="false" />
00096                   <argument name="exten" required="false" />
00097                   <argument name="priority" required="true" hasparams="optional" argsep="^">
00098                      <argument name="arg1" multiple="true" required="true" />
00099                      <argument name="argN" />
00100                   </argument>
00101                </option>
00102                <option name="d">
00103                   <para>Disable the 'Please hold while we try to connect your call' announcement.</para>
00104                </option>
00105                <option name="I">
00106                   <para>Asterisk will ignore any connected line update requests
00107                   it may receive on this dial attempt.</para>
00108                </option>
00109                <option name="l">
00110                   <para>Disable local call optimization so that applications with
00111                   audio hooks between the local bridge don't get dropped when the
00112                   calls get joined directly.</para>
00113                </option>
00114                <option name="N">
00115                   <para>Don't answer the incoming call until we're ready to
00116                   connect the caller or give up.</para>
00117                   <note>
00118                      <para>This option is ignored if the call is already answered.</para>
00119                   </note>
00120                   <note>
00121                      <para>If the call is not already answered, the 'a' and 's'
00122                      options are ignored while the 'd' option is implicitly enabled.</para>
00123                   </note>
00124                </option>
00125                <option name="n">
00126                   <para>Playback the unreachable status message if we've run out
00127                   of steps or the callee has elected not to be reachable.</para>
00128                </option>
00129                <option name="s">
00130                   <para>Playback the incoming status message prior to starting
00131                   the follow-me step(s)</para>
00132                </option>
00133             </optionlist>
00134          </parameter>
00135       </syntax>
00136       <description>
00137          <para>This application performs Find-Me/Follow-Me functionality for the caller
00138          as defined in the profile matching the <replaceable>followmeid</replaceable> parameter in
00139          <filename>followme.conf</filename>. If the specified <replaceable>followmeid</replaceable>
00140          profile doesn't exist in <filename>followme.conf</filename>, execution will be returned
00141          to the dialplan and call execution will continue at the next priority.</para>
00142          <para>Returns -1 on hangup.</para>
00143       </description>
00144    </application>
00145  ***/
00146 
00147 static char *app = "FollowMe";
00148 
00149 /*! Maximum accept/decline DTMF string plus terminator. */
00150 #define MAX_YN_STRING      20
00151 
00152 /*! \brief Number structure */
00153 struct number {
00154    char number[512]; /*!< Phone Number(s) and/or Extension(s) */
00155    long timeout;     /*!< Dial Timeout, if used. */
00156    int order;     /*!< The order to dial in */
00157    AST_LIST_ENTRY(number) entry; /*!< Next Number record */
00158 };
00159 
00160 /*! \brief Data structure for followme scripts */
00161 struct call_followme {
00162    ast_mutex_t lock;
00163    char name[AST_MAX_EXTENSION]; /*!< Name - FollowMeID */
00164    char moh[MAX_MUSICCLASS];  /*!< Music On Hold Class to be used */
00165    char context[AST_MAX_CONTEXT];  /*!< Context to dial from */
00166    unsigned int active;    /*!< Profile is active (1), or disabled (0). */
00167    int realtime;           /*!< Cached from realtime */
00168    char takecall[MAX_YN_STRING]; /*!< Digit mapping to take a call */
00169    char nextindp[MAX_YN_STRING]; /*!< Digit mapping to decline a call */
00170    char callfromprompt[PATH_MAX];   /*!< Sound prompt name and path */
00171    char norecordingprompt[PATH_MAX];   /*!< Sound prompt name and path */
00172    char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
00173    char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
00174    char statusprompt[PATH_MAX];  /*!< Sound prompt name and path */
00175    char sorryprompt[PATH_MAX];   /*!< Sound prompt name and path */
00176 
00177    AST_LIST_HEAD_NOLOCK(numbers, number) numbers;     /*!< Head of the list of follow-me numbers */
00178    AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers; /*!< Head of the list of black-listed numbers */
00179    AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers; /*!< Head of the list of white-listed numbers */
00180    AST_LIST_ENTRY(call_followme) entry;           /*!< Next Follow-Me record */
00181 };
00182 
00183 struct fm_args {
00184    char *mohclass;
00185    AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
00186    /*! Gosub app arguments for outgoing calls.  NULL if not supplied. */
00187    const char *predial_callee;
00188    /*! Accumulated connected line information from inbound call. */
00189    struct ast_party_connected_line connected_in;
00190    /*! Accumulated connected line information from outbound call. */
00191    struct ast_party_connected_line connected_out;
00192    /*! TRUE if connected line information from inbound call changed. */
00193    unsigned int pending_in_connected_update:1;
00194    /*! TRUE if connected line information from outbound call is available. */
00195    unsigned int pending_out_connected_update:1;
00196    /*! TRUE if caller has a pending hold request for the winning call. */
00197    unsigned int pending_hold:1;
00198    /*! Music On Hold Class suggested by caller hold for winning call. */
00199    char suggested_moh[MAX_MUSICCLASS];
00200    char context[AST_MAX_CONTEXT];
00201    char namerecloc[PATH_MAX];
00202    char takecall[MAX_YN_STRING]; /*!< Digit mapping to take a call */
00203    char nextindp[MAX_YN_STRING]; /*!< Digit mapping to decline a call */
00204    char callfromprompt[PATH_MAX];   /*!< Sound prompt name and path */
00205    char norecordingprompt[PATH_MAX];   /*!< Sound prompt name and path */
00206    char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
00207    char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
00208    char statusprompt[PATH_MAX];  /*!< Sound prompt name and path */
00209    char sorryprompt[PATH_MAX];   /*!< Sound prompt name and path */
00210    struct ast_flags followmeflags;
00211 };
00212 
00213 struct findme_user {
00214    struct ast_channel *ochan;
00215    /*! Accumulated connected line information from outgoing call. */
00216    struct ast_party_connected_line connected;
00217    long digts;
00218    int ynidx;
00219    int state;
00220    char dialarg[256];
00221    /*! Collected digits to accept/decline the call. */
00222    char yn[MAX_YN_STRING];
00223    /*! TRUE if the outgoing call is answered. */
00224    unsigned int answered:1;
00225    /*! TRUE if connected line information is available. */
00226    unsigned int pending_connected_update:1;
00227    AST_LIST_ENTRY(findme_user) entry;
00228 };
00229 
00230 enum {
00231    FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00232    FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00233    FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
00234    FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
00235    FOLLOWMEFLAG_NOANSWER = (1 << 4),
00236    FOLLOWMEFLAG_DISABLEOPTIMIZATION = (1 << 5),
00237    FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 6),
00238    FOLLOWMEFLAG_PREDIAL_CALLER = (1 << 7),
00239    FOLLOWMEFLAG_PREDIAL_CALLEE = (1 << 8),
00240 };
00241 
00242 enum {
00243    FOLLOWMEFLAG_ARG_PREDIAL_CALLER,
00244    FOLLOWMEFLAG_ARG_PREDIAL_CALLEE,
00245 
00246    /* note: this entry _MUST_ be the last one in the enum */
00247    FOLLOWMEFLAG_ARG_ARRAY_SIZE
00248 };
00249 
00250 AST_APP_OPTIONS(followme_opts, {
00251    AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME),
00252    AST_APP_OPTION_ARG('B', FOLLOWMEFLAG_PREDIAL_CALLER, FOLLOWMEFLAG_ARG_PREDIAL_CALLER),
00253    AST_APP_OPTION_ARG('b', FOLLOWMEFLAG_PREDIAL_CALLEE, FOLLOWMEFLAG_ARG_PREDIAL_CALLEE),
00254    AST_APP_OPTION('d', FOLLOWMEFLAG_DISABLEHOLDPROMPT),
00255    AST_APP_OPTION('I', FOLLOWMEFLAG_IGNORE_CONNECTEDLINE),
00256    AST_APP_OPTION('l', FOLLOWMEFLAG_DISABLEOPTIMIZATION),
00257    AST_APP_OPTION('N', FOLLOWMEFLAG_NOANSWER),
00258    AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG),
00259    AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG),
00260 });
00261 
00262 static const char *featuredigittostr;
00263 static int featuredigittimeout = 5000;    /*!< Feature Digit Timeout */
00264 static const char *defaultmoh = "default";      /*!< Default Music-On-Hold Class */
00265 
00266 static char takecall[MAX_YN_STRING] = "1";
00267 static char nextindp[MAX_YN_STRING] = "2";
00268 static char callfromprompt[PATH_MAX] = "followme/call-from";
00269 static char norecordingprompt[PATH_MAX] = "followme/no-recording";
00270 static char optionsprompt[PATH_MAX] = "followme/options";
00271 static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
00272 static char statusprompt[PATH_MAX] = "followme/status";
00273 static char sorryprompt[PATH_MAX] = "followme/sorry";
00274 
00275 
00276 static AST_RWLIST_HEAD_STATIC(followmes, call_followme);
00277 AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
00278 
00279 static void free_numbers(struct call_followme *f)
00280 {
00281    /* Free numbers attached to the profile */
00282    struct number *prev;
00283 
00284    while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
00285       /* Free the number */
00286       ast_free(prev);
00287    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00288 
00289    while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
00290       /* Free the blacklisted number */
00291       ast_free(prev);
00292    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00293 
00294    while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
00295       /* Free the whitelisted number */
00296       ast_free(prev);
00297    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00298 }
00299 
00300 
00301 /*! \brief Allocate and initialize followme profile */
00302 static struct call_followme *alloc_profile(const char *fmname)
00303 {
00304    struct call_followme *f;
00305 
00306    if (!(f = ast_calloc(1, sizeof(*f))))
00307       return NULL;
00308 
00309    ast_mutex_init(&f->lock);
00310    ast_copy_string(f->name, fmname, sizeof(f->name));
00311    f->moh[0] = '\0';
00312    f->context[0] = '\0';
00313    ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
00314    ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
00315    ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
00316    ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
00317    ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
00318    ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
00319    ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
00320    ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
00321    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00322    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00323    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00324    return f;
00325 }
00326 
00327 static void init_profile(struct call_followme *f)
00328 {
00329    f->active = 1;
00330    ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00331 }
00332 
00333    
00334    
00335 /*! \brief Set parameter in profile from configuration file */
00336 static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
00337 {
00338 
00339    if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music")) 
00340       ast_copy_string(f->moh, val, sizeof(f->moh));
00341    else if (!strcasecmp(param, "context")) 
00342       ast_copy_string(f->context, val, sizeof(f->context));
00343    else if (!strcasecmp(param, "takecall"))
00344       ast_copy_string(f->takecall, val, sizeof(f->takecall));
00345    else if (!strcasecmp(param, "declinecall"))
00346       ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
00347    else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
00348       ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
00349    else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt")) 
00350       ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
00351    else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt")) 
00352       ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
00353    else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
00354       ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
00355    else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt")) 
00356       ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
00357    else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt")) 
00358       ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
00359    else if (failunknown) {
00360       if (linenum >= 0)
00361          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
00362       else
00363          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
00364    }
00365 }
00366 
00367 /*! \brief Add a new number */
00368 static struct number *create_followme_number(const char *number, int timeout, int numorder)
00369 {
00370    struct number *cur;
00371    char *buf = ast_strdupa(number);
00372    char *tmp;
00373 
00374    if (!(cur = ast_calloc(1, sizeof(*cur))))
00375       return NULL;
00376 
00377    cur->timeout = timeout;
00378    if ((tmp = strchr(buf, ',')))
00379       *tmp = '\0';
00380    ast_copy_string(cur->number, buf, sizeof(cur->number));
00381    cur->order = numorder;
00382    ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
00383 
00384    return cur;
00385 }
00386 
00387 /*! \brief Reload followme application module */
00388 static int reload_followme(int reload)
00389 {
00390    struct call_followme *f;
00391    struct ast_config *cfg;
00392    char *cat = NULL, *tmp;
00393    struct ast_variable *var;
00394    struct number *cur, *nm;
00395    char *numberstr;
00396    int timeout;
00397    int numorder;
00398    const char *takecallstr;
00399    const char *declinecallstr;
00400    const char *tmpstr;
00401    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00402 
00403    if (!(cfg = ast_config_load("followme.conf", config_flags))) {
00404       ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
00405       return 0;
00406    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00407       return 0;
00408    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00409       ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format.  Aborting.\n");
00410       return 0;
00411    }
00412 
00413    AST_RWLIST_WRLOCK(&followmes);
00414 
00415    /* Reset Global Var Values */
00416    featuredigittimeout = 5000;
00417 
00418    /* Mark all profiles as inactive for the moment */
00419    AST_RWLIST_TRAVERSE(&followmes, f, entry) {
00420       f->active = 0;
00421    }
00422 
00423    featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
00424 
00425    if (!ast_strlen_zero(featuredigittostr)) {
00426       if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
00427          featuredigittimeout = 5000;
00428    }
00429 
00430    if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
00431       ast_copy_string(takecall, takecallstr, sizeof(takecall));
00432    }
00433 
00434    if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
00435       ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
00436    }
00437 
00438    if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
00439       ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00440    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
00441       ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00442    }
00443 
00444    if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
00445       ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00446    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
00447       ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00448    }
00449 
00450 
00451    if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
00452       ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00453    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
00454       ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00455    }
00456 
00457    if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
00458       ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00459    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
00460       ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00461    }
00462 
00463    if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
00464       ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00465    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
00466       ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00467    }
00468 
00469    if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
00470       ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00471    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
00472       ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00473    }
00474 
00475    /* Chug through config file */
00476    while ((cat = ast_category_browse(cfg, cat))) {
00477       int new = 0;
00478 
00479       if (!strcasecmp(cat, "general"))
00480          continue;
00481 
00482       /* Look for an existing one */
00483       AST_LIST_TRAVERSE(&followmes, f, entry) {
00484          if (!strcasecmp(f->name, cat))
00485             break;
00486       }
00487 
00488       ast_debug(1, "New profile %s.\n", cat);
00489 
00490       if (!f) {
00491          /* Make one then */
00492          f = alloc_profile(cat);
00493          new = 1;
00494       }
00495 
00496       /* Totally fail if we fail to find/create an entry */
00497       if (!f)
00498          continue;
00499 
00500       if (!new)
00501          ast_mutex_lock(&f->lock);
00502       /* Re-initialize the profile */
00503       init_profile(f);
00504       free_numbers(f);
00505       var = ast_variable_browse(cfg, cat);
00506       while (var) {
00507          if (!strcasecmp(var->name, "number")) {
00508             int idx = 0;
00509 
00510             /* Add a new number */
00511             numberstr = ast_strdupa(var->value);
00512             if ((tmp = strchr(numberstr, ','))) {
00513                *tmp++ = '\0';
00514                timeout = atoi(tmp);
00515                if (timeout < 0) {
00516                   timeout = 25;
00517                }
00518                if ((tmp = strchr(tmp, ','))) {
00519                   *tmp++ = '\0';
00520                   numorder = atoi(tmp);
00521                   if (numorder < 0)
00522                      numorder = 0;
00523                } else 
00524                   numorder = 0;
00525             } else {
00526                timeout = 25;
00527                numorder = 0;
00528             }
00529 
00530             if (!numorder) {
00531                idx = 1;
00532                AST_LIST_TRAVERSE(&f->numbers, nm, entry) 
00533                   idx++;
00534                numorder = idx;
00535             }
00536             cur = create_followme_number(numberstr, timeout, numorder);
00537             if (cur) {
00538                AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
00539             }
00540          } else {
00541             profile_set_param(f, var->name, var->value, var->lineno, 1);
00542             ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
00543          }
00544          var = var->next;
00545       } /* End while(var) loop */
00546 
00547       if (!new) 
00548          ast_mutex_unlock(&f->lock);
00549       else
00550          AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
00551    }
00552 
00553    ast_config_destroy(cfg);
00554 
00555    AST_RWLIST_UNLOCK(&followmes);
00556 
00557    return 1;
00558 }
00559 
00560 static void publish_dial_end_event(struct ast_channel *in, struct findme_user_listptr *findme_user_list, struct ast_channel *exception, const char *status)
00561 {
00562    struct findme_user *tmpuser;
00563 
00564    AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00565       if (tmpuser->ochan && tmpuser->ochan != exception) {
00566          ast_channel_publish_dial(in, tmpuser->ochan, NULL, status);
00567       }
00568    }
00569 }
00570 
00571 static void clear_caller(struct findme_user *tmpuser)
00572 {
00573    struct ast_channel *outbound;
00574 
00575    if (!tmpuser->ochan) {
00576       /* Call already cleared. */
00577       return;
00578    }
00579 
00580    outbound = tmpuser->ochan;
00581    ast_hangup(outbound);
00582    tmpuser->ochan = NULL;
00583 }
00584 
00585 static void clear_unanswered_calls(struct findme_user_listptr *findme_user_list) 
00586 {
00587    struct findme_user *tmpuser;
00588 
00589    AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00590       if (!tmpuser->answered) {
00591          clear_caller(tmpuser);
00592       }
00593    }
00594 }
00595 
00596 static void destroy_calling_node(struct findme_user *node)
00597 {
00598    clear_caller(node);
00599    ast_party_connected_line_free(&node->connected);
00600    ast_free(node);
00601 }
00602 
00603 static void destroy_calling_tree(struct findme_user_listptr *findme_user_list)
00604 {
00605    struct findme_user *fmuser;
00606 
00607    while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
00608       destroy_calling_node(fmuser);
00609    }
00610 }
00611 
00612 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)
00613 {
00614    struct ast_party_connected_line connected;
00615    struct ast_channel *watchers[256];
00616    int pos;
00617    struct ast_channel *winner;
00618    struct ast_frame *f;
00619    struct findme_user *tmpuser;
00620    int to = 0;
00621    int livechannels;
00622    int tmpto;
00623    long totalwait = 0, wtd = 0, towas = 0;
00624    char *callfromname;
00625    char *pressbuttonname;
00626 
00627    /* ------------ wait_for_winner_channel start --------------- */ 
00628 
00629    callfromname = ast_strdupa(tpargs->callfromprompt);
00630    pressbuttonname = ast_strdupa(tpargs->optionsprompt);
00631 
00632    totalwait = nm->timeout * 1000;
00633 
00634    for (;;) {
00635       to = 1000;
00636       pos = 1; 
00637       livechannels = 0;
00638       watchers[0] = caller;
00639 
00640       winner = NULL;
00641       AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00642          if (!tmpuser->ochan) {
00643             continue;
00644          }
00645          if (tmpuser->state == 3) {
00646             tmpuser->digts += (towas - wtd);
00647          }
00648          if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
00649             ast_verb(3, "<%s> We've been waiting for digits longer than we should have.\n",
00650                ast_channel_name(tmpuser->ochan));
00651             if (!ast_strlen_zero(tpargs->namerecloc)) {
00652                tmpuser->state = 1;
00653                tmpuser->digts = 0;
00654                if (!ast_streamfile(tmpuser->ochan, callfromname, ast_channel_language(tmpuser->ochan))) {
00655                   ast_sched_runq(ast_channel_sched(tmpuser->ochan));
00656                } else {
00657                   ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00658                   clear_caller(tmpuser);
00659                   continue;
00660                }
00661             } else {
00662                tmpuser->state = 2;
00663                tmpuser->digts = 0;
00664                if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
00665                   ast_sched_runq(ast_channel_sched(tmpuser->ochan));
00666                else {
00667                   ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00668                   clear_caller(tmpuser);
00669                   continue;
00670                }
00671             }
00672          }
00673          if (ast_channel_stream(tmpuser->ochan)) {
00674             ast_sched_runq(ast_channel_sched(tmpuser->ochan));
00675             tmpto = ast_sched_wait(ast_channel_sched(tmpuser->ochan));
00676             if (tmpto > 0 && tmpto < to)
00677                to = tmpto;
00678             else if (tmpto < 0 && !ast_channel_timingfunc(tmpuser->ochan)) {
00679                ast_stopstream(tmpuser->ochan);
00680                switch (tmpuser->state) {
00681                case 1:
00682                   ast_verb(3, "<%s> Playback of the call-from file appears to be done.\n",
00683                      ast_channel_name(tmpuser->ochan));
00684                   if (!ast_streamfile(tmpuser->ochan, tpargs->namerecloc, ast_channel_language(tmpuser->ochan))) {
00685                      tmpuser->state = 2;
00686                   } else {
00687                      ast_log(LOG_NOTICE, "<%s> Unable to playback %s. Maybe the caller didn't record their name?\n",
00688                         ast_channel_name(tmpuser->ochan), tpargs->namerecloc);
00689                      memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00690                      tmpuser->ynidx = 0;
00691                      if (!ast_streamfile(tmpuser->ochan, pressbuttonname, ast_channel_language(tmpuser->ochan)))
00692                         tmpuser->state = 3;
00693                      else {
00694                         ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
00695                         clear_caller(tmpuser);
00696                         continue;
00697                      }
00698                   }
00699                   break;
00700                case 2:
00701                   ast_verb(3, "<%s> Playback of name file appears to be done.\n",
00702                      ast_channel_name(tmpuser->ochan));
00703                   memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00704                   tmpuser->ynidx = 0;
00705                   if (!ast_streamfile(tmpuser->ochan, pressbuttonname, ast_channel_language(tmpuser->ochan))) {
00706                      tmpuser->state = 3;
00707                   } else {
00708                      clear_caller(tmpuser);
00709                      continue;
00710                   }
00711                   break;
00712                case 3:
00713                   ast_verb(3, "<%s> Playback of the next step file appears to be done.\n",
00714                      ast_channel_name(tmpuser->ochan));
00715                   tmpuser->digts = 0;
00716                   break;
00717                default:
00718                   break;
00719                }
00720             }
00721          }
00722          watchers[pos++] = tmpuser->ochan;
00723          livechannels++;
00724       }
00725       if (!livechannels) {
00726          ast_verb(3, "No live channels left for this step.\n");
00727          return NULL;
00728       }
00729 
00730       tmpto = to;
00731       if (to < 0) {
00732          to = 1000;
00733          tmpto = 1000;
00734       }
00735       towas = to;
00736       winner = ast_waitfor_n(watchers, pos, &to);
00737       tmpto -= to;
00738       totalwait -= tmpto;
00739       wtd = to;
00740       if (totalwait <= 0) {
00741          ast_verb(3, "We've hit our timeout for this step. Dropping unanswered calls and starting the next step.\n");
00742          clear_unanswered_calls(findme_user_list);
00743          return NULL;
00744       }
00745       if (winner) {
00746          /* Need to find out which channel this is */
00747          if (winner != caller) {
00748             /* The winner is an outgoing channel. */
00749             AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00750                if (tmpuser->ochan == winner) {
00751                   break;
00752                }
00753             }
00754          } else {
00755             tmpuser = NULL;
00756          }
00757 
00758          f = ast_read(winner);
00759          if (f) {
00760             if (f->frametype == AST_FRAME_CONTROL) {
00761                switch (f->subclass.integer) {
00762                case AST_CONTROL_HANGUP:
00763                   ast_verb(3, "%s received a hangup frame.\n", ast_channel_name(winner));
00764                   if (f->data.uint32) {
00765                      ast_channel_hangupcause_set(winner, f->data.uint32);
00766                   }
00767                   if (!tmpuser) {
00768                      ast_verb(3, "The calling channel hungup. Need to drop everyone.\n");
00769                      publish_dial_end_event(caller, findme_user_list, NULL, "CANCEL");
00770                      ast_frfree(f);
00771                      return NULL;
00772                   }
00773                   clear_caller(tmpuser);
00774                   break;
00775                case AST_CONTROL_ANSWER:
00776                   if (!tmpuser) {
00777                      /* The caller answered?  We want an outgoing channel to answer. */
00778                      break;
00779                   }
00780                   ast_verb(3, "%s answered %s\n", ast_channel_name(winner), ast_channel_name(caller));
00781                   ast_channel_publish_dial(caller, winner, NULL, "ANSWER");
00782                   publish_dial_end_event(caller, findme_user_list, winner, "CANCEL");
00783                   tmpuser->answered = 1;
00784                   /* If call has been answered, then the eventual hangup is likely to be normal hangup */ 
00785                   ast_channel_hangupcause_set(winner, AST_CAUSE_NORMAL_CLEARING);
00786                   ast_channel_hangupcause_set(caller, AST_CAUSE_NORMAL_CLEARING);
00787                   ast_verb(3, "Starting playback of %s\n", callfromname);
00788                   if (!ast_strlen_zero(tpargs->namerecloc)) {
00789                      if (!ast_streamfile(winner, callfromname, ast_channel_language(winner))) {
00790                         ast_sched_runq(ast_channel_sched(winner));
00791                         tmpuser->state = 1;
00792                      } else {
00793                         ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00794                         clear_caller(tmpuser);
00795                      }
00796                   } else {
00797                      tmpuser->state = 2;
00798                      if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
00799                         ast_sched_runq(ast_channel_sched(tmpuser->ochan));
00800                      else {
00801                         ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00802                         clear_caller(tmpuser);
00803                      }
00804                   }
00805                   break;
00806                case AST_CONTROL_BUSY:
00807                   ast_verb(3, "%s is busy\n", ast_channel_name(winner));
00808                   if (tmpuser) {
00809                      /* Outbound call was busy.  Drop it. */
00810                      ast_channel_publish_dial(caller, winner, NULL, "BUSY");
00811                      clear_caller(tmpuser);
00812                   }
00813                   break;
00814                case AST_CONTROL_CONGESTION:
00815                   ast_verb(3, "%s is circuit-busy\n", ast_channel_name(winner));
00816                   if (tmpuser) {
00817                      /* Outbound call was congested.  Drop it. */
00818                      ast_channel_publish_dial(caller, winner, NULL, "CONGESTION");
00819                      clear_caller(tmpuser);
00820                   }
00821                   break;
00822                case AST_CONTROL_RINGING:
00823                   ast_verb(3, "%s is ringing\n", ast_channel_name(winner));
00824                   break;
00825                case AST_CONTROL_PROGRESS:
00826                   ast_verb(3, "%s is making progress\n", ast_channel_name(winner));
00827                   break;
00828                case AST_CONTROL_VIDUPDATE:
00829                   ast_verb(3, "%s requested a video update\n", ast_channel_name(winner));
00830                   break;
00831                case AST_CONTROL_SRCUPDATE:
00832                   ast_verb(3, "%s requested a source update\n", ast_channel_name(winner));
00833                   break;
00834                case AST_CONTROL_PROCEEDING:
00835                   ast_verb(3, "%s is proceeding\n", ast_channel_name(winner));
00836                   break;
00837                case AST_CONTROL_HOLD:
00838                   ast_verb(3, "%s placed call on hold\n", ast_channel_name(winner));
00839                   if (!tmpuser) {
00840                      /* Caller placed outgoing calls on hold. */
00841                      tpargs->pending_hold = 1;
00842                      if (f->data.ptr) {
00843                         ast_copy_string(tpargs->suggested_moh, f->data.ptr,
00844                            sizeof(tpargs->suggested_moh));
00845                      } else {
00846                         tpargs->suggested_moh[0] = '\0';
00847                      }
00848                   } else {
00849                      /*
00850                       * Outgoing call placed caller on hold.
00851                       *
00852                       * Ignore because the outgoing call should not be able to place
00853                       * the caller on hold until after they are bridged.
00854                       */
00855                   }
00856                   break;
00857                case AST_CONTROL_UNHOLD:
00858                   ast_verb(3, "%s removed call from hold\n", ast_channel_name(winner));
00859                   if (!tmpuser) {
00860                      /* Caller removed outgoing calls from hold. */
00861                      tpargs->pending_hold = 0;
00862                   } else {
00863                      /*
00864                       * Outgoing call removed caller from hold.
00865                       *
00866                       * Ignore because the outgoing call should not be able to place
00867                       * the caller on hold until after they are bridged.
00868                       */
00869                   }
00870                   break;
00871                case AST_CONTROL_OFFHOOK:
00872                case AST_CONTROL_FLASH:
00873                   /* Ignore going off hook and flash */
00874                   break;
00875                case AST_CONTROL_CONNECTED_LINE:
00876                   if (!tmpuser) {
00877                      /*
00878                       * Hold connected line update from caller until we have a
00879                       * winner.
00880                       */
00881                      ast_verb(3,
00882                         "%s connected line has changed. Saving it until we have a winner.\n",
00883                         ast_channel_name(winner));
00884                      ast_party_connected_line_set_init(&connected, &tpargs->connected_in);
00885                      if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
00886                         ast_party_connected_line_set(&tpargs->connected_in,
00887                            &connected, NULL);
00888                         tpargs->pending_in_connected_update = 1;
00889                      }
00890                      ast_party_connected_line_free(&connected);
00891                      break;
00892                   }
00893                   if (ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE)) {
00894                      ast_verb(3, "Connected line update from %s prevented.\n",
00895                         ast_channel_name(winner));
00896                   } else {
00897                      ast_verb(3,
00898                         "%s connected line has changed. Saving it until answer.\n",
00899                         ast_channel_name(winner));
00900                      ast_party_connected_line_set_init(&connected, &tmpuser->connected);
00901                      if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
00902                         ast_party_connected_line_set(&tmpuser->connected,
00903                            &connected, NULL);
00904                         tmpuser->pending_connected_update = 1;
00905                      }
00906                      ast_party_connected_line_free(&connected);
00907                   }
00908                   break;
00909                case AST_CONTROL_REDIRECTING:
00910                   /*
00911                    * Ignore because we are masking the FollowMe search progress to
00912                    * the caller.
00913                    */
00914                   break;
00915                case AST_CONTROL_PVT_CAUSE_CODE:
00916                   ast_indicate_data(caller, f->subclass.integer, f->data.ptr, f->datalen);
00917                   break;
00918                case -1:
00919                   ast_verb(3, "%s stopped sounds\n", ast_channel_name(winner));
00920                   break;
00921                default:
00922                   ast_debug(1, "Dunno what to do with control type %d from %s\n",
00923                      f->subclass.integer, ast_channel_name(winner));
00924                   break;
00925                }
00926             } 
00927             if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
00928                int cmp_len;
00929 
00930                if (ast_channel_stream(winner))
00931                   ast_stopstream(winner);
00932                tmpuser->digts = 0;
00933                ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
00934                if (tmpuser->ynidx < ARRAY_LEN(tmpuser->yn) - 1) {
00935                   tmpuser->yn[tmpuser->ynidx++] = f->subclass.integer;
00936                } else {
00937                   /* Discard oldest digit. */
00938                   memmove(tmpuser->yn, tmpuser->yn + 1,
00939                      sizeof(tmpuser->yn) - 2 * sizeof(tmpuser->yn[0]));
00940                   tmpuser->yn[ARRAY_LEN(tmpuser->yn) - 2] = f->subclass.integer;
00941                }
00942                ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
00943                cmp_len = strlen(tpargs->takecall);
00944                if (cmp_len <= tmpuser->ynidx
00945                   && !strcmp(tmpuser->yn + (tmpuser->ynidx - cmp_len), tpargs->takecall)) {
00946                   ast_debug(1, "Match to take the call!\n");
00947                   ast_frfree(f);
00948                   return tmpuser->ochan;
00949                }
00950                cmp_len = strlen(tpargs->nextindp);
00951                if (cmp_len <= tmpuser->ynidx
00952                   && !strcmp(tmpuser->yn + (tmpuser->ynidx - cmp_len), tpargs->nextindp)) {
00953                   ast_debug(1, "Declined to take the call.\n");
00954                   clear_caller(tmpuser);
00955                }
00956             }
00957 
00958             ast_frfree(f);
00959          } else {
00960             ast_debug(1, "we didn't get a frame. hanging up.\n");
00961             if (!tmpuser) {
00962                /* Caller hung up. */
00963                ast_verb(3, "The calling channel hungup. Need to drop everyone.\n");
00964                return NULL;
00965             }
00966             /* Outgoing channel hung up. */
00967             ast_channel_publish_dial(caller, winner, NULL, "NOANSWER");
00968             clear_caller(tmpuser);
00969          }
00970       } else {
00971          ast_debug(1, "timed out waiting for action\n");
00972       }
00973    }
00974 
00975    /* Unreachable. */
00976 }
00977 
00978 /*!
00979  * \internal
00980  * \brief Find an extension willing to take the call.
00981  *
00982  * \param tpargs Active Followme config.
00983  * \param caller Channel initiating the outgoing calls.
00984  *
00985  * \retval winner Winning outgoing call.
00986  * \retval NULL if could not find someone to take the call.
00987  */
00988 static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel *caller)
00989 {
00990    struct number *nm;
00991    struct ast_channel *winner = NULL;
00992    char num[512];
00993    int dg, idx;
00994    char *rest, *number;
00995    struct findme_user *tmpuser;
00996    struct findme_user *fmuser;
00997    struct findme_user_listptr findme_user_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00998    struct findme_user_listptr new_user_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00999 
01000    for (idx = 1; !ast_check_hangup(caller); ++idx) {
01001       /* Find next followme numbers to dial. */
01002       AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
01003          if (nm->order == idx) {
01004             break;
01005          }
01006       }
01007       if (!nm) {
01008          ast_verb(3, "No more steps left.\n");
01009          break;
01010       }
01011 
01012       ast_debug(2, "Number(s) %s timeout %ld\n", nm->number, nm->timeout);
01013 
01014       /*
01015        * Put all active outgoing channels into autoservice.
01016        *
01017        * This needs to be done because ast_exists_extension() may put
01018        * the caller into autoservice.
01019        */
01020       AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
01021          if (tmpuser->ochan) {
01022             ast_autoservice_start(tmpuser->ochan);
01023          }
01024       }
01025 
01026       /* Create all new outgoing calls */
01027       ast_copy_string(num, nm->number, sizeof(num));
01028       for (number = num; number; number = rest) {
01029          struct ast_channel *outbound;
01030 
01031          rest = strchr(number, '&');
01032          if (rest) {
01033             *rest++ = 0;
01034          }
01035 
01036          /* We check if the extension exists, before creating the ast_channel struct */
01037          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))) {
01038             ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
01039             continue;
01040          }
01041 
01042          tmpuser = ast_calloc(1, sizeof(*tmpuser));
01043          if (!tmpuser) {
01044             continue;
01045          }
01046 
01047          if (ast_strlen_zero(tpargs->context)) {
01048             snprintf(tmpuser->dialarg, sizeof(tmpuser->dialarg), "%s%s",
01049                number,
01050                ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_DISABLEOPTIMIZATION)
01051                   ? "/n" : "/m");
01052          } else {
01053             snprintf(tmpuser->dialarg, sizeof(tmpuser->dialarg), "%s@%s%s",
01054                number, tpargs->context,
01055                ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_DISABLEOPTIMIZATION)
01056                   ? "/n" : "/m");
01057          }
01058 
01059          outbound = ast_request("Local", ast_channel_nativeformats(caller), NULL, caller,
01060             tmpuser->dialarg, &dg);
01061          if (!outbound) {
01062             ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n",
01063                tmpuser->dialarg, ast_cause2str(dg));
01064             ast_free(tmpuser);
01065             continue;
01066          }
01067 
01068          ast_channel_lock_both(caller, outbound);
01069          ast_connected_line_copy_from_caller(ast_channel_connected(outbound), ast_channel_caller(caller));
01070          ast_channel_inherit_variables(caller, outbound);
01071          ast_channel_datastore_inherit(caller, outbound);
01072          ast_channel_language_set(outbound, ast_channel_language(caller));
01073          ast_channel_req_accountcodes(outbound, caller, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
01074          ast_channel_musicclass_set(outbound, ast_channel_musicclass(caller));
01075          ast_channel_unlock(outbound);
01076          ast_channel_unlock(caller);
01077 
01078          tmpuser->ochan = outbound;
01079          tmpuser->state = 0;
01080          AST_LIST_INSERT_TAIL(&new_user_list, tmpuser, entry);
01081       }
01082 
01083       /*
01084        * PREDIAL: Run gosub on all of the new callee channels
01085        *
01086        * We run the callee predial before ast_call() in case the user
01087        * wishes to do something on the newly created channels before
01088        * the channel does anything important.
01089        */
01090       if (tpargs->predial_callee && !AST_LIST_EMPTY(&new_user_list)) {
01091          /* Put caller into autoservice. */
01092          ast_autoservice_start(caller);
01093 
01094          /* Run predial on all new outgoing calls. */
01095          AST_LIST_TRAVERSE(&new_user_list, tmpuser, entry) {
01096             ast_pre_call(tmpuser->ochan, tpargs->predial_callee);
01097          }
01098 
01099          /* Take caller out of autoservice. */
01100          if (ast_autoservice_stop(caller)) {
01101             /*
01102              * Caller hungup.
01103              *
01104              * Destoy all new outgoing calls.
01105              */
01106             while ((tmpuser = AST_LIST_REMOVE_HEAD(&new_user_list, entry))) {
01107                destroy_calling_node(tmpuser);
01108             }
01109 
01110             /* Take all active outgoing channels out of autoservice. */
01111             AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
01112                if (tmpuser->ochan) {
01113                   ast_autoservice_stop(tmpuser->ochan);
01114                }
01115             }
01116             break;
01117          }
01118       }
01119 
01120       /* Start all new outgoing calls */
01121       AST_LIST_TRAVERSE_SAFE_BEGIN(&new_user_list, tmpuser, entry) {
01122          ast_verb(3, "calling Local/%s\n", tmpuser->dialarg);
01123          if (ast_call(tmpuser->ochan, tmpuser->dialarg, 0)) {
01124             ast_verb(3, "couldn't reach at this number.\n");
01125             AST_LIST_REMOVE_CURRENT(entry);
01126 
01127             /* Destroy this failed new outgoing call. */
01128             destroy_calling_node(tmpuser);
01129             continue;
01130          }
01131 
01132          ast_channel_publish_dial(caller, tmpuser->ochan, tmpuser->dialarg, NULL);
01133       }
01134       AST_LIST_TRAVERSE_SAFE_END;
01135 
01136       /* Take all active outgoing channels out of autoservice. */
01137       AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
01138          if (tmpuser->ochan && ast_autoservice_stop(tmpuser->ochan)) {
01139             /* Existing outgoing call hungup. */
01140             AST_LIST_REMOVE_CURRENT(entry);
01141             destroy_calling_node(tmpuser);
01142          }
01143       }
01144       AST_LIST_TRAVERSE_SAFE_END;
01145 
01146       if (AST_LIST_EMPTY(&new_user_list)) {
01147          /* No new channels remain at this order level.  If there were any at all. */
01148          continue;
01149       }
01150 
01151       /* Add new outgoing channels to the findme list. */
01152       AST_LIST_APPEND_LIST(&findme_user_list, &new_user_list, entry);
01153 
01154       winner = wait_for_winner(&findme_user_list, nm, caller, tpargs);
01155       if (!winner) {
01156          /* Remove all dead outgoing nodes. */
01157          AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
01158             if (!tmpuser->ochan) {
01159                AST_LIST_REMOVE_CURRENT(entry);
01160                destroy_calling_node(tmpuser);
01161             }
01162          }
01163          AST_LIST_TRAVERSE_SAFE_END;
01164          continue;
01165       }
01166 
01167       /* Destroy losing calls up to the winner.  The rest will be destroyed later. */
01168       while ((fmuser = AST_LIST_REMOVE_HEAD(&findme_user_list, entry))) {
01169          if (fmuser->ochan == winner) {
01170             /*
01171              * Pass any connected line info up.
01172              *
01173              * NOTE: This code must be in line with destroy_calling_node().
01174              */
01175             tpargs->connected_out = fmuser->connected;
01176             tpargs->pending_out_connected_update = fmuser->pending_connected_update;
01177             ast_free(fmuser);
01178             break;
01179          } else {
01180             /* Destroy losing call. */
01181             destroy_calling_node(fmuser);
01182          }
01183       }
01184       break;
01185    }
01186    destroy_calling_tree(&findme_user_list);
01187    return winner;
01188 }
01189 
01190 static struct call_followme *find_realtime(const char *name)
01191 {
01192    struct ast_variable *var;
01193    struct ast_variable *v;
01194    struct ast_config *cfg;
01195    const char *catg;
01196    struct call_followme *new_follower;
01197    struct ast_str *str;
01198 
01199    str = ast_str_create(16);
01200    if (!str) {
01201       return NULL;
01202    }
01203 
01204    var = ast_load_realtime("followme", "name", name, SENTINEL);
01205    if (!var) {
01206       ast_free(str);
01207       return NULL;
01208    }
01209 
01210    if (!(new_follower = alloc_profile(name))) {
01211       ast_variables_destroy(var);
01212       ast_free(str);
01213       return NULL;
01214    }
01215 
01216    for (v = var; v; v = v->next) {
01217       if (!strcasecmp(v->name, "active")) {
01218          if (ast_false(v->value)) {
01219             ast_mutex_destroy(&new_follower->lock);
01220             ast_free(new_follower);
01221             ast_variables_destroy(var);
01222             ast_free(str);
01223             return NULL;
01224          }
01225       } else {
01226          profile_set_param(new_follower, v->name, v->value, 0, 0);
01227       }
01228    }
01229 
01230    ast_variables_destroy(var);
01231    new_follower->realtime = 1;
01232 
01233    /* Load numbers */
01234    cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
01235       name, SENTINEL);
01236    if (!cfg) {
01237       ast_mutex_destroy(&new_follower->lock);
01238       ast_free(new_follower);
01239       ast_free(str);
01240       return NULL;
01241    }
01242 
01243    for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
01244       const char *numstr;
01245       const char *timeoutstr;
01246       const char *ordstr;
01247       int timeout;
01248       struct number *cur;
01249 
01250       if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
01251          continue;
01252       }
01253       if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
01254          || sscanf(timeoutstr, "%30d", &timeout) != 1
01255          || timeout < 1) {
01256          timeout = 25;
01257       }
01258       /* This one has to exist; it was part of the query */
01259       ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
01260       ast_str_set(&str, 0, "%s", numstr);
01261       if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
01262          AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
01263       }
01264    }
01265    ast_config_destroy(cfg);
01266 
01267    ast_free(str);
01268    return new_follower;
01269 }
01270 
01271 static void end_bridge_callback(void *data)
01272 {
01273    char buf[80];
01274    time_t end;
01275    struct ast_channel *chan = data;
01276 
01277    time(&end);
01278 
01279    ast_channel_lock(chan);
01280    snprintf(buf, sizeof(buf), "%d", ast_channel_get_up_time(chan));
01281    pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
01282    snprintf(buf, sizeof(buf), "%d", ast_channel_get_duration(chan));
01283    pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
01284    ast_channel_unlock(chan);
01285 }
01286 
01287 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
01288 {
01289    bconfig->end_bridge_callback_data = originator;
01290 }
01291 
01292 static int app_exec(struct ast_channel *chan, const char *data)
01293 {
01294    struct fm_args *targs;
01295    struct ast_bridge_config config;
01296    struct call_followme *f;
01297    struct number *nm, *newnm;
01298    int res = 0;
01299    char *argstr;
01300    struct ast_channel *caller;
01301    struct ast_channel *outbound;
01302    AST_DECLARE_APP_ARGS(args,
01303       AST_APP_ARG(followmeid);
01304       AST_APP_ARG(options);
01305    );
01306    char *opt_args[FOLLOWMEFLAG_ARG_ARRAY_SIZE];
01307 
01308    if (ast_strlen_zero(data)) {
01309       ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01310       return -1;
01311    }
01312 
01313    argstr = ast_strdupa((char *) data);
01314 
01315    AST_STANDARD_APP_ARGS(args, argstr);
01316 
01317    if (ast_strlen_zero(args.followmeid)) {
01318       ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01319       return -1;
01320    }
01321 
01322    targs = ast_calloc(1, sizeof(*targs));
01323    if (!targs) {
01324       return -1;
01325    }
01326 
01327    AST_RWLIST_RDLOCK(&followmes);
01328    AST_RWLIST_TRAVERSE(&followmes, f, entry) {
01329       if (!strcasecmp(f->name, args.followmeid) && (f->active))
01330          break;
01331    }
01332    AST_RWLIST_UNLOCK(&followmes);
01333 
01334    ast_debug(1, "New profile %s.\n", args.followmeid);
01335 
01336    if (!f) {
01337       f = find_realtime(args.followmeid);
01338    }
01339 
01340    if (!f) {
01341       ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
01342       ast_free(targs);
01343       return 0;
01344    }
01345 
01346    /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
01347    if (args.options) {
01348       ast_app_parse_options(followme_opts, &targs->followmeflags, opt_args, args.options);
01349    }
01350 
01351    /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
01352    ast_mutex_lock(&f->lock);
01353    targs->mohclass = ast_strdupa(f->moh);
01354    ast_copy_string(targs->context, f->context, sizeof(targs->context));
01355    ast_copy_string(targs->takecall, f->takecall, sizeof(targs->takecall));
01356    ast_copy_string(targs->nextindp, f->nextindp, sizeof(targs->nextindp));
01357    ast_copy_string(targs->callfromprompt, f->callfromprompt, sizeof(targs->callfromprompt));
01358    ast_copy_string(targs->norecordingprompt, f->norecordingprompt, sizeof(targs->norecordingprompt));
01359    ast_copy_string(targs->optionsprompt, f->optionsprompt, sizeof(targs->optionsprompt));
01360    ast_copy_string(targs->plsholdprompt, f->plsholdprompt, sizeof(targs->plsholdprompt));
01361    ast_copy_string(targs->statusprompt, f->statusprompt, sizeof(targs->statusprompt));
01362    ast_copy_string(targs->sorryprompt, f->sorryprompt, sizeof(targs->sorryprompt));
01363    /* Copy the numbers we're going to use into another list in case the master list should get modified 
01364       (and locked) while we're trying to do a follow-me */
01365    AST_LIST_HEAD_INIT_NOLOCK(&targs->cnumbers);
01366    AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
01367       newnm = create_followme_number(nm->number, nm->timeout, nm->order);
01368       if (newnm) {
01369          AST_LIST_INSERT_TAIL(&targs->cnumbers, newnm, entry);
01370       }
01371    }
01372    ast_mutex_unlock(&f->lock);
01373 
01374    /* PREDIAL: Preprocess any callee gosub arguments. */
01375    if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_PREDIAL_CALLEE)
01376       && !ast_strlen_zero(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE])) {
01377       ast_replace_subargument_delimiter(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE]);
01378       targs->predial_callee =
01379          ast_app_expand_sub_args(chan, opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE]);
01380    }
01381 
01382    /* PREDIAL: Run gosub on the caller's channel */
01383    if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_PREDIAL_CALLER)
01384       && !ast_strlen_zero(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER])) {
01385       ast_replace_subargument_delimiter(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER]);
01386       ast_app_exec_sub(NULL, chan, opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER], 0);
01387    }
01388 
01389    /* Forget the 'N' option if the call is already up. */
01390    if (ast_channel_state(chan) == AST_STATE_UP) {
01391       ast_clear_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER);
01392    }
01393 
01394    if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER)) {
01395       ast_indicate(chan, AST_CONTROL_RINGING);
01396    } else {
01397       /* Answer the call */
01398       if (ast_channel_state(chan) != AST_STATE_UP) {
01399          ast_answer(chan);
01400       }
01401 
01402       if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_STATUSMSG)) {
01403          ast_stream_and_wait(chan, targs->statusprompt, "");
01404       }
01405 
01406       if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_RECORDNAME)) {
01407          int duration = 5;
01408 
01409          snprintf(targs->namerecloc, sizeof(targs->namerecloc), "%s/followme.%s",
01410             ast_config_AST_SPOOL_DIR, ast_channel_uniqueid(chan));
01411          if (ast_play_and_record(chan, "vm-rec-name", targs->namerecloc, 5, "sln", &duration,
01412             NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) {
01413             goto outrun;
01414          }
01415          if (!ast_fileexists(targs->namerecloc, NULL, ast_channel_language(chan))) {
01416             targs->namerecloc[0] = '\0';
01417          }
01418       }
01419 
01420       if (!ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) {
01421          if (ast_streamfile(chan, targs->plsholdprompt, ast_channel_language(chan))) {
01422             goto outrun;
01423          }
01424          if (ast_waitstream(chan, "") < 0)
01425             goto outrun;
01426       }
01427       ast_moh_start(chan, targs->mohclass, NULL);
01428    }
01429 
01430    ast_channel_lock(chan);
01431    ast_connected_line_copy_from_caller(&targs->connected_in, ast_channel_caller(chan));
01432    ast_channel_unlock(chan);
01433 
01434    outbound = findmeexec(targs, chan);
01435    if (!outbound) {
01436       if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER)) {
01437          if (ast_channel_state(chan) != AST_STATE_UP) {
01438             ast_answer(chan);
01439          }
01440       } else {
01441          ast_moh_stop(chan);
01442       }
01443 
01444       if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) {
01445          ast_stream_and_wait(chan, targs->sorryprompt, "");
01446       }
01447       res = 0;
01448    } else {
01449       caller = chan;
01450       /* Bridge the two channels. */
01451 
01452       memset(&config, 0, sizeof(config));
01453       ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01454       ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01455       ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01456       config.end_bridge_callback = end_bridge_callback;
01457       config.end_bridge_callback_data = chan;
01458       config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
01459 
01460       /* Update connected line to caller if available. */
01461       if (targs->pending_out_connected_update) {
01462          if (ast_channel_connected_line_sub(outbound, caller, &targs->connected_out, 0) &&
01463             ast_channel_connected_line_macro(outbound, caller, &targs->connected_out, 1, 0)) {
01464             ast_channel_update_connected_line(caller, &targs->connected_out, NULL);
01465          }
01466       }
01467 
01468       if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER)) {
01469          if (ast_channel_state(caller) != AST_STATE_UP) {
01470             ast_answer(caller);
01471          }
01472       } else {
01473          ast_moh_stop(caller);
01474       }
01475 
01476       /* Be sure no generators are left on it */
01477       ast_deactivate_generator(caller);
01478       /* Make sure channels are compatible */
01479       res = ast_channel_make_compatible(caller, outbound);
01480       if (res < 0) {
01481          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));
01482          ast_autoservice_chan_hangup_peer(caller, outbound);
01483          goto outrun;
01484       }
01485 
01486       /* Update connected line to winner if changed. */
01487       if (targs->pending_in_connected_update) {
01488          if (ast_channel_connected_line_sub(caller, outbound, &targs->connected_in, 0) &&
01489             ast_channel_connected_line_macro(caller, outbound, &targs->connected_in, 0, 0)) {
01490             ast_channel_update_connected_line(outbound, &targs->connected_in, NULL);
01491          }
01492       }
01493 
01494       /* Put winner on hold if caller requested. */
01495       if (targs->pending_hold) {
01496          if (ast_strlen_zero(targs->suggested_moh)) {
01497             ast_indicate_data(outbound, AST_CONTROL_HOLD, NULL, 0);
01498          } else {
01499             ast_indicate_data(outbound, AST_CONTROL_HOLD,
01500                targs->suggested_moh, strlen(targs->suggested_moh) + 1);
01501          }
01502       }
01503 
01504       res = ast_bridge_call(caller, outbound, &config);
01505    }
01506 
01507 outrun:
01508    while ((nm = AST_LIST_REMOVE_HEAD(&targs->cnumbers, entry))) {
01509       ast_free(nm);
01510    }
01511    if (!ast_strlen_zero(targs->namerecloc)) {
01512       unlink(targs->namerecloc);
01513    }
01514    ast_free((char *) targs->predial_callee);
01515    ast_party_connected_line_free(&targs->connected_in);
01516    ast_party_connected_line_free(&targs->connected_out);
01517    ast_free(targs);
01518 
01519    if (f->realtime) {
01520       /* Not in list */
01521       free_numbers(f);
01522       ast_free(f);
01523    }
01524 
01525    return res;
01526 }
01527 
01528 static int unload_module(void)
01529 {
01530    struct call_followme *f;
01531 
01532    ast_unregister_application(app);
01533 
01534    /* Free Memory. Yeah! I'm free! */
01535    AST_RWLIST_WRLOCK(&followmes);
01536    while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
01537       free_numbers(f);
01538       ast_free(f);
01539    }
01540 
01541    AST_RWLIST_UNLOCK(&followmes);
01542 
01543    return 0;
01544 }
01545 
01546 /*!
01547  * \brief Load the module
01548  *
01549  * Module loading including tests for configuration or dependencies.
01550  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
01551  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
01552  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
01553  * configuration file or other non-critical problem return 
01554  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
01555  */
01556 static int load_module(void)
01557 {
01558    if(!reload_followme(0))
01559       return AST_MODULE_LOAD_DECLINE;
01560 
01561    return ast_register_application_xml(app, app_exec);
01562 }
01563 
01564 static int reload(void)
01565 {
01566    reload_followme(1);
01567 
01568    return 0;
01569 }
01570 
01571 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
01572       .support_level = AST_MODULE_SUPPORT_CORE,
01573       .load = load_module,
01574       .unload = unload_module,
01575       .reload = reload,
01576           );

Generated on Thu Apr 16 06:27:09 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6