Sat Apr 26 2014 22:01:26

Asterisk developer's documentation


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