Thu Sep 7 01:02:50 2017

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: 372390 $")
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="d">
00075                   <para>Disable the 'Please hold while we try to connect your call' announcement.</para>
00076                </option>
00077                <option name="I">
00078                   <para>Asterisk will ignore any connected line update requests
00079                   it may receive on this dial attempt.</para>
00080                </option>
00081                <option name="n">
00082                   <para>Playback the unreachable status message if we've run out
00083                   of steps or the callee has elected not to be reachable.</para>
00084                </option>
00085                <option name="s">
00086                   <para>Playback the incoming status message prior to starting
00087                   the follow-me step(s)</para>
00088                </option>
00089             </optionlist>
00090          </parameter>
00091       </syntax>
00092       <description>
00093          <para>This application performs Find-Me/Follow-Me functionality for the caller
00094          as defined in the profile matching the <replaceable>followmeid</replaceable> parameter in
00095          <filename>followme.conf</filename>. If the specified <replaceable>followmeid</replaceable>
00096          profile doesn't exist in <filename>followme.conf</filename>, execution will be returned
00097          to the dialplan and call execution will continue at the next priority.</para>
00098          <para>Returns -1 on hangup.</para>
00099       </description>
00100    </application>
00101  ***/
00102 
00103 static char *app = "FollowMe";
00104 
00105 /*! Maximum accept/decline DTMF string plus terminator. */
00106 #define MAX_YN_STRING      20
00107 
00108 /*! \brief Number structure */
00109 struct number {
00110    char number[512]; /*!< Phone Number(s) and/or Extension(s) */
00111    long timeout;     /*!< Dial Timeout, if used. */
00112    int order;     /*!< The order to dial in */
00113    AST_LIST_ENTRY(number) entry; /*!< Next Number record */
00114 };
00115 
00116 /*! \brief Data structure for followme scripts */
00117 struct call_followme {
00118    ast_mutex_t lock;
00119    char name[AST_MAX_EXTENSION]; /*!< Name - FollowMeID */
00120    char moh[MAX_MUSICCLASS];  /*!< Music On Hold Class to be used */
00121    char context[AST_MAX_CONTEXT];  /*!< Context to dial from */
00122    unsigned int active;    /*!< Profile is active (1), or disabled (0). */
00123    int realtime;           /*!< Cached from realtime */
00124    char takecall[MAX_YN_STRING]; /*!< Digit mapping to take a call */
00125    char nextindp[MAX_YN_STRING]; /*!< Digit mapping to decline a call */
00126    char callfromprompt[PATH_MAX];   /*!< Sound prompt name and path */
00127    char norecordingprompt[PATH_MAX];   /*!< Sound prompt name and path */
00128    char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
00129    char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
00130    char statusprompt[PATH_MAX];  /*!< Sound prompt name and path */
00131    char sorryprompt[PATH_MAX];   /*!< Sound prompt name and path */
00132 
00133    AST_LIST_HEAD_NOLOCK(numbers, number) numbers;     /*!< Head of the list of follow-me numbers */
00134    AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers; /*!< Head of the list of black-listed numbers */
00135    AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers; /*!< Head of the list of white-listed numbers */
00136    AST_LIST_ENTRY(call_followme) entry;           /*!< Next Follow-Me record */
00137 };
00138 
00139 struct fm_args {
00140    /*! Inbound (caller) channel */
00141    struct ast_channel *chan;
00142    char *mohclass;
00143    AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
00144    /*! Winning outbound (callee) channel */
00145    struct ast_channel *outbound;
00146    /*! Accumulated connected line information from inbound call. */
00147    struct ast_party_connected_line connected_in;
00148    /*! Accumulated connected line information from outbound call. */
00149    struct ast_party_connected_line connected_out;
00150    /*! TRUE if connected line information from inbound call changed. */
00151    unsigned int pending_in_connected_update:1;
00152    /*! TRUE if connected line information from outbound call is available. */
00153    unsigned int pending_out_connected_update:1;
00154    int status;
00155    char context[AST_MAX_CONTEXT];
00156    char namerecloc[PATH_MAX];
00157    char takecall[MAX_YN_STRING]; /*!< Digit mapping to take a call */
00158    char nextindp[MAX_YN_STRING]; /*!< Digit mapping to decline a call */
00159    char callfromprompt[PATH_MAX];   /*!< Sound prompt name and path */
00160    char norecordingprompt[PATH_MAX];   /*!< Sound prompt name and path */
00161    char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
00162    char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
00163    char statusprompt[PATH_MAX];  /*!< Sound prompt name and path */
00164    char sorryprompt[PATH_MAX];   /*!< Sound prompt name and path */
00165    struct ast_flags followmeflags;
00166 };
00167 
00168 struct findme_user {
00169    struct ast_channel *ochan;
00170    /*! Accumulated connected line information from outgoing call. */
00171    struct ast_party_connected_line connected;
00172    long digts;
00173    int ynidx;
00174    int state;
00175    char dialarg[256];
00176    /*! Collected digits to accept/decline the call. */
00177    char yn[MAX_YN_STRING];
00178    /*! TRUE if call cleared. */
00179    unsigned int cleared:1;
00180    /*! TRUE if connected line information is available. */
00181    unsigned int pending_connected_update:1;
00182    AST_LIST_ENTRY(findme_user) entry;
00183 };
00184 
00185 enum {
00186    FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00187    FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00188    FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
00189    FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
00190    FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 4),
00191 };
00192 
00193 AST_APP_OPTIONS(followme_opts, {
00194    AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME),
00195    AST_APP_OPTION('d', FOLLOWMEFLAG_DISABLEHOLDPROMPT),
00196    AST_APP_OPTION('I', FOLLOWMEFLAG_IGNORE_CONNECTEDLINE),
00197    AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG),
00198    AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG),
00199 });
00200 
00201 static const char *featuredigittostr;
00202 static int featuredigittimeout = 5000;    /*!< Feature Digit Timeout */
00203 static const char *defaultmoh = "default";      /*!< Default Music-On-Hold Class */
00204 
00205 static char takecall[MAX_YN_STRING] = "1";
00206 static char nextindp[MAX_YN_STRING] = "2";
00207 static char callfromprompt[PATH_MAX] = "followme/call-from";
00208 static char norecordingprompt[PATH_MAX] = "followme/no-recording";
00209 static char optionsprompt[PATH_MAX] = "followme/options";
00210 static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
00211 static char statusprompt[PATH_MAX] = "followme/status";
00212 static char sorryprompt[PATH_MAX] = "followme/sorry";
00213 
00214 
00215 static AST_RWLIST_HEAD_STATIC(followmes, call_followme);
00216 AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
00217 
00218 static void free_numbers(struct call_followme *f)
00219 {
00220    /* Free numbers attached to the profile */
00221    struct number *prev;
00222 
00223    while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
00224       /* Free the number */
00225       ast_free(prev);
00226    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00227 
00228    while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
00229       /* Free the blacklisted number */
00230       ast_free(prev);
00231    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00232 
00233    while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
00234       /* Free the whitelisted number */
00235       ast_free(prev);
00236    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00237 }
00238 
00239 
00240 /*! \brief Allocate and initialize followme profile */
00241 static struct call_followme *alloc_profile(const char *fmname)
00242 {
00243    struct call_followme *f;
00244 
00245    if (!(f = ast_calloc(1, sizeof(*f))))
00246       return NULL;
00247 
00248    ast_mutex_init(&f->lock);
00249    ast_copy_string(f->name, fmname, sizeof(f->name));
00250    f->moh[0] = '\0';
00251    f->context[0] = '\0';
00252    ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
00253    ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
00254    ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
00255    ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
00256    ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
00257    ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
00258    ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
00259    ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
00260    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00261    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00262    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00263    return f;
00264 }
00265 
00266 static void init_profile(struct call_followme *f)
00267 {
00268    f->active = 1;
00269    ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00270 }
00271 
00272    
00273    
00274 /*! \brief Set parameter in profile from configuration file */
00275 static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
00276 {
00277 
00278    if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music")) 
00279       ast_copy_string(f->moh, val, sizeof(f->moh));
00280    else if (!strcasecmp(param, "context")) 
00281       ast_copy_string(f->context, val, sizeof(f->context));
00282    else if (!strcasecmp(param, "takecall"))
00283       ast_copy_string(f->takecall, val, sizeof(f->takecall));
00284    else if (!strcasecmp(param, "declinecall"))
00285       ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
00286    else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
00287       ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
00288    else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt")) 
00289       ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
00290    else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt")) 
00291       ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
00292    else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
00293       ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
00294    else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt")) 
00295       ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
00296    else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt")) 
00297       ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
00298    else if (failunknown) {
00299       if (linenum >= 0)
00300          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
00301       else
00302          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
00303    }
00304 }
00305 
00306 /*! \brief Add a new number */
00307 static struct number *create_followme_number(const char *number, int timeout, int numorder)
00308 {
00309    struct number *cur;
00310    char *buf = ast_strdupa(number);
00311    char *tmp;
00312 
00313    if (!(cur = ast_calloc(1, sizeof(*cur))))
00314       return NULL;
00315 
00316    cur->timeout = timeout;
00317    if ((tmp = strchr(buf, ',')))
00318       *tmp = '\0';
00319    ast_copy_string(cur->number, buf, sizeof(cur->number));
00320    cur->order = numorder;
00321    ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
00322 
00323    return cur;
00324 }
00325 
00326 /*! \brief Reload followme application module */
00327 static int reload_followme(int reload)
00328 {
00329    struct call_followme *f;
00330    struct ast_config *cfg;
00331    char *cat = NULL, *tmp;
00332    struct ast_variable *var;
00333    struct number *cur, *nm;
00334    char *numberstr;
00335    int timeout;
00336    int numorder;
00337    const char *takecallstr;
00338    const char *declinecallstr;
00339    const char *tmpstr;
00340    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00341 
00342    if (!(cfg = ast_config_load("followme.conf", config_flags))) {
00343       ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
00344       return 0;
00345    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00346       return 0;
00347    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00348       ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format.  Aborting.\n");
00349       return 0;
00350    }
00351 
00352    AST_RWLIST_WRLOCK(&followmes);
00353 
00354    /* Reset Global Var Values */
00355    featuredigittimeout = 5000;
00356 
00357    /* Mark all profiles as inactive for the moment */
00358    AST_RWLIST_TRAVERSE(&followmes, f, entry) {
00359       f->active = 0;
00360    }
00361 
00362    featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
00363 
00364    if (!ast_strlen_zero(featuredigittostr)) {
00365       if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
00366          featuredigittimeout = 5000;
00367    }
00368 
00369    if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
00370       ast_copy_string(takecall, takecallstr, sizeof(takecall));
00371    }
00372 
00373    if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
00374       ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
00375    }
00376 
00377    if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
00378       ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00379    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
00380       ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00381    }
00382 
00383    if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
00384       ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00385    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
00386       ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00387    }
00388 
00389 
00390    if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
00391       ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00392    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
00393       ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00394    }
00395 
00396    if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
00397       ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00398    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
00399       ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00400    }
00401 
00402    if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
00403       ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00404    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
00405       ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00406    }
00407 
00408    if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
00409       ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00410    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
00411       ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00412    }
00413 
00414    /* Chug through config file */
00415    while ((cat = ast_category_browse(cfg, cat))) {
00416       int new = 0;
00417 
00418       if (!strcasecmp(cat, "general"))
00419          continue;
00420 
00421       /* Look for an existing one */
00422       AST_LIST_TRAVERSE(&followmes, f, entry) {
00423          if (!strcasecmp(f->name, cat))
00424             break;
00425       }
00426 
00427       ast_debug(1, "New profile %s.\n", cat);
00428 
00429       if (!f) {
00430          /* Make one then */
00431          f = alloc_profile(cat);
00432          new = 1;
00433       }
00434 
00435       /* Totally fail if we fail to find/create an entry */
00436       if (!f)
00437          continue;
00438 
00439       if (!new)
00440          ast_mutex_lock(&f->lock);
00441       /* Re-initialize the profile */
00442       init_profile(f);
00443       free_numbers(f);
00444       var = ast_variable_browse(cfg, cat);
00445       while (var) {
00446          if (!strcasecmp(var->name, "number")) {
00447             int idx = 0;
00448 
00449             /* Add a new number */
00450             numberstr = ast_strdupa(var->value);
00451             if ((tmp = strchr(numberstr, ','))) {
00452                *tmp++ = '\0';
00453                timeout = atoi(tmp);
00454                if (timeout < 0) {
00455                   timeout = 25;
00456                }
00457                if ((tmp = strchr(tmp, ','))) {
00458                   *tmp++ = '\0';
00459                   numorder = atoi(tmp);
00460                   if (numorder < 0)
00461                      numorder = 0;
00462                } else 
00463                   numorder = 0;
00464             } else {
00465                timeout = 25;
00466                numorder = 0;
00467             }
00468 
00469             if (!numorder) {
00470                idx = 1;
00471                AST_LIST_TRAVERSE(&f->numbers, nm, entry) 
00472                   idx++;
00473                numorder = idx;
00474             }
00475             cur = create_followme_number(numberstr, timeout, numorder);
00476             if (cur) {
00477                AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
00478             }
00479          } else {
00480             profile_set_param(f, var->name, var->value, var->lineno, 1);
00481             ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
00482          }
00483          var = var->next;
00484       } /* End while(var) loop */
00485 
00486       if (!new) 
00487          ast_mutex_unlock(&f->lock);
00488       else
00489          AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
00490    }
00491 
00492    ast_config_destroy(cfg);
00493 
00494    AST_RWLIST_UNLOCK(&followmes);
00495 
00496    return 1;
00497 }
00498 
00499 static void clear_caller(struct findme_user *tmpuser)
00500 {
00501    struct ast_channel *outbound;
00502 
00503    if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
00504       outbound = tmpuser->ochan;
00505       ast_channel_lock(outbound);
00506       if (!outbound->cdr) {
00507          outbound->cdr = ast_cdr_alloc();
00508          if (outbound->cdr) {
00509             ast_cdr_init(outbound->cdr, outbound);
00510          }
00511       }
00512       if (outbound->cdr) {
00513          char tmp[256];
00514 
00515          snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
00516          ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00517          ast_cdr_update(outbound);
00518          ast_cdr_start(outbound->cdr);
00519          ast_cdr_end(outbound->cdr);
00520          /* If the cause wasn't handled properly */
00521          if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
00522             ast_cdr_failed(outbound->cdr);
00523          }
00524       } else {
00525          ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
00526       }
00527       ast_channel_unlock(outbound);
00528       ast_hangup(outbound);
00529       tmpuser->ochan = NULL;
00530    }
00531 }
00532 
00533 static void clear_calling_tree(struct findme_user_listptr *findme_user_list) 
00534 {
00535    struct findme_user *tmpuser;
00536 
00537    AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00538       clear_caller(tmpuser);
00539       tmpuser->cleared = 1;
00540    }
00541 }
00542 
00543 static void destroy_calling_tree(struct findme_user_listptr *findme_user_list)
00544 {
00545    struct findme_user *fmuser;
00546 
00547    while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
00548       if (!fmuser->cleared) {
00549          clear_caller(fmuser);
00550       }
00551       ast_party_connected_line_free(&fmuser->connected);
00552       ast_free(fmuser);
00553    }
00554    ast_free(findme_user_list);
00555 }
00556 
00557 static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, struct fm_args *tpargs)
00558 {
00559    struct ast_party_connected_line connected;
00560    struct ast_channel *watchers[256];
00561    int pos;
00562    struct ast_channel *winner;
00563    struct ast_frame *f;
00564    int ctstatus = 0;
00565    int dg;
00566    struct findme_user *tmpuser;
00567    int to = 0;
00568    int livechannels = 0;
00569    int tmpto;
00570    long totalwait = 0, wtd = 0, towas = 0;
00571    char *callfromname;
00572    char *pressbuttonname;
00573 
00574    /* ------------ wait_for_winner_channel start --------------- */ 
00575 
00576    callfromname = ast_strdupa(tpargs->callfromprompt);
00577    pressbuttonname = ast_strdupa(tpargs->optionsprompt);
00578 
00579    if (AST_LIST_EMPTY(findme_user_list)) {
00580       ast_verb(3, "couldn't reach at this number.\n");
00581       return NULL;
00582    }
00583 
00584    if (!caller) {
00585       ast_verb(3, "Original caller hungup. Cleanup.\n");
00586       clear_calling_tree(findme_user_list);
00587       return NULL;
00588    }
00589 
00590    totalwait = nm->timeout * 1000;
00591 
00592    while (!ctstatus) {
00593       to = 1000;
00594       pos = 1; 
00595       livechannels = 0;
00596       watchers[0] = caller;
00597 
00598       dg = 0;
00599       winner = NULL;
00600       AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00601          if (tmpuser->state >= 0 && tmpuser->ochan) {
00602             if (tmpuser->state == 3) 
00603                tmpuser->digts += (towas - wtd);
00604             if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
00605                ast_verb(3, "We've been waiting for digits longer than we should have.\n");
00606                if (!ast_strlen_zero(namerecloc)) {
00607                   tmpuser->state = 1;
00608                   tmpuser->digts = 0;
00609                   if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
00610                      ast_sched_runq(tmpuser->ochan->sched);
00611                   } else {
00612                      ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00613                      return NULL;
00614                   }
00615                } else {
00616                   tmpuser->state = 2;
00617                   tmpuser->digts = 0;
00618                   if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00619                      ast_sched_runq(tmpuser->ochan->sched);
00620                   else {
00621                      ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00622                      return NULL;
00623                   }
00624                }
00625             }
00626             if (tmpuser->ochan->stream) {
00627                ast_sched_runq(tmpuser->ochan->sched);
00628                tmpto = ast_sched_wait(tmpuser->ochan->sched);
00629                if (tmpto > 0 && tmpto < to)
00630                   to = tmpto;
00631                else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
00632                   ast_stopstream(tmpuser->ochan);
00633                   if (tmpuser->state == 1) {
00634                      ast_verb(3, "Playback of the call-from file appears to be done.\n");
00635                      if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
00636                         tmpuser->state = 2;
00637                      } else {
00638                         ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
00639                         memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00640                         tmpuser->ynidx = 0;
00641                         if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
00642                            tmpuser->state = 3;
00643                         else {
00644                            ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
00645                            return NULL;
00646                         } 
00647                      }
00648                   } else if (tmpuser->state == 2) {
00649                      ast_verb(3, "Playback of name file appears to be done.\n");
00650                      memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00651                      tmpuser->ynidx = 0;
00652                      if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
00653                         tmpuser->state = 3;
00654                      } else {
00655                         return NULL;
00656                      } 
00657                   } else if (tmpuser->state == 3) {
00658                      ast_verb(3, "Playback of the next step file appears to be done.\n");
00659                      tmpuser->digts = 0;
00660                   }
00661                }
00662             }
00663             watchers[pos++] = tmpuser->ochan;
00664             livechannels++;
00665          }
00666       }
00667 
00668       tmpto = to;
00669       if (to < 0) {
00670          to = 1000;
00671          tmpto = 1000;
00672       }
00673       towas = to;
00674       winner = ast_waitfor_n(watchers, pos, &to);
00675       tmpto -= to;
00676       totalwait -= tmpto;
00677       wtd = to;
00678       if (totalwait <= 0) {
00679          ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
00680          clear_calling_tree(findme_user_list);
00681          return NULL;
00682       }
00683       if (winner) {
00684          /* Need to find out which channel this is */
00685          for (dg = 0; dg < ARRAY_LEN(watchers); ++dg) {
00686             if (winner == watchers[dg]) {
00687                break;
00688             }
00689          }
00690          if (dg) {
00691             /* The winner is an outgoing channel. */
00692             AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00693                if (tmpuser->ochan == winner) {
00694                   break;
00695                }
00696             }
00697          } else {
00698             tmpuser = NULL;
00699          }
00700          f = ast_read(winner);
00701          if (f) {
00702             if (f->frametype == AST_FRAME_CONTROL) {
00703                switch (f->subclass.integer) {
00704                case AST_CONTROL_HANGUP:
00705                   ast_verb(3, "%s received a hangup frame.\n", winner->name);
00706                   if (f->data.uint32) {
00707                      winner->hangupcause = f->data.uint32;
00708                   }
00709                   if (dg == 0) {
00710                      ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
00711                      clear_calling_tree(findme_user_list);
00712                      ctstatus = -1;
00713                   }
00714                   break;
00715                case AST_CONTROL_ANSWER:
00716                   ast_verb(3, "%s answered %s\n", winner->name, caller->name);
00717                   /* If call has been answered, then the eventual hangup is likely to be normal hangup */ 
00718                   winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00719                   caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00720                   ast_verb(3, "Starting playback of %s\n", callfromname);
00721                   if (dg > 0) {
00722                      if (!ast_strlen_zero(namerecloc)) {
00723                         if (!ast_streamfile(winner, callfromname, winner->language)) {
00724                            ast_sched_runq(winner->sched);
00725                            tmpuser->state = 1;
00726                         } else {
00727                            ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00728                            ast_frfree(f);
00729                            return NULL;
00730                         }
00731                      } else {
00732                         tmpuser->state = 2;
00733                         if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00734                            ast_sched_runq(tmpuser->ochan->sched);
00735                         else {
00736                            ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00737                            ast_frfree(f);
00738                            return NULL;
00739                         }
00740                      }
00741                   }
00742                   break;
00743                case AST_CONTROL_BUSY:
00744                   ast_verb(3, "%s is busy\n", winner->name);
00745                   break;
00746                case AST_CONTROL_CONGESTION:
00747                   ast_verb(3, "%s is circuit-busy\n", winner->name);
00748                   break;
00749                case AST_CONTROL_RINGING:
00750                   ast_verb(3, "%s is ringing\n", winner->name);
00751                   break;
00752                case AST_CONTROL_PROGRESS:
00753                   ast_verb(3, "%s is making progress\n", winner->name);
00754                   break;
00755                case AST_CONTROL_VIDUPDATE:
00756                   ast_verb(3, "%s requested a video update\n", winner->name);
00757                   break;
00758                case AST_CONTROL_SRCUPDATE:
00759                   ast_verb(3, "%s requested a source update\n", winner->name);
00760                   break;
00761                case AST_CONTROL_PROCEEDING:
00762                   ast_verb(3, "%s is proceeding\n", winner->name);
00763                   break;
00764                case AST_CONTROL_HOLD:
00765                   ast_verb(3, "%s placed call on hold\n", winner->name);
00766                   break;
00767                case AST_CONTROL_UNHOLD:
00768                   ast_verb(3, "%s removed call from hold\n", winner->name);
00769                   break;
00770                case AST_CONTROL_OFFHOOK:
00771                case AST_CONTROL_FLASH:
00772                   /* Ignore going off hook and flash */
00773                   break;
00774                case AST_CONTROL_CONNECTED_LINE:
00775                   if (!tmpuser) {
00776                      /*
00777                       * Hold connected line update from caller until we have a
00778                       * winner.
00779                       */
00780                      ast_verb(3,
00781                         "%s connected line has changed. Saving it until we have a winner.\n",
00782                         winner->name);
00783                      ast_party_connected_line_set_init(&connected, &tpargs->connected_in);
00784                      if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
00785                         ast_party_connected_line_set(&tpargs->connected_in,
00786                            &connected, NULL);
00787                         tpargs->pending_in_connected_update = 1;
00788                      }
00789                      ast_party_connected_line_free(&connected);
00790                      break;
00791                   }
00792                   if (ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE)) {
00793                      ast_verb(3, "Connected line update from %s prevented.\n",
00794                         winner->name);
00795                   } else {
00796                      ast_verb(3,
00797                         "%s connected line has changed. Saving it until answer.\n",
00798                         winner->name);
00799                      ast_party_connected_line_set_init(&connected, &tmpuser->connected);
00800                      if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
00801                         ast_party_connected_line_set(&tmpuser->connected,
00802                            &connected, NULL);
00803                         tmpuser->pending_connected_update = 1;
00804                      }
00805                      ast_party_connected_line_free(&connected);
00806                   }
00807                   break;
00808                case AST_CONTROL_REDIRECTING:
00809                   /*
00810                    * Always ignore because the caller is already answered
00811                    * and is likely listening to MOH.
00812                    */
00813                   break;
00814                case -1:
00815                   ast_verb(3, "%s stopped sounds\n", winner->name);
00816                   break;
00817                default:
00818                   ast_debug(1, "Dunno what to do with control type %d from %s\n",
00819                      f->subclass.integer, winner->name);
00820                   break;
00821                }
00822             } 
00823             if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
00824                if (winner->stream)
00825                   ast_stopstream(winner);
00826                tmpuser->digts = 0;
00827                ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
00828                if (tmpuser->ynidx < ARRAY_LEN(tmpuser->yn) - 1) {
00829                   tmpuser->yn[tmpuser->ynidx++] = (char) f->subclass.integer;
00830                }
00831                ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
00832                if (!strcmp(tmpuser->yn, tpargs->takecall)) {
00833                   ast_debug(1, "Match to take the call!\n");
00834                   ast_frfree(f);
00835                   return tmpuser->ochan;
00836                }
00837                if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
00838                   ast_debug(1, "Next in dial plan step requested.\n");
00839                   ast_frfree(f);
00840                   return NULL;
00841                }
00842             }
00843 
00844             ast_frfree(f);
00845          } else {
00846             ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n", dg);
00847             if (!dg) {
00848                /* Caller hung up. */
00849                clear_calling_tree(findme_user_list);
00850                return NULL;
00851             } else {
00852                /* Outgoing channel hung up. */
00853                tmpuser->state = -1;
00854                tmpuser->ochan = NULL;
00855                ast_hangup(winner);
00856                --livechannels;
00857                ast_debug(1, "live channels left %d\n", livechannels);
00858                if (!livechannels) {
00859                   ast_verb(3, "no live channels left. exiting.\n");
00860                   return NULL;
00861                }
00862             }
00863          }
00864       } else {
00865          ast_debug(1, "timed out waiting for action\n");
00866       }
00867    }
00868 
00869    /* --- WAIT FOR WINNER NUMBER END! -----------*/
00870    return NULL;
00871 }
00872 
00873 static void findmeexec(struct fm_args *tpargs)
00874 {
00875    struct number *nm;
00876    struct ast_channel *outbound;
00877    struct ast_channel *caller;
00878    struct ast_channel *winner = NULL;
00879    char dialarg[512];
00880    char num[512];
00881    int dg, idx;
00882    char *rest, *number;
00883    struct findme_user *tmpuser;
00884    struct findme_user *fmuser;
00885    struct findme_user_listptr *findme_user_list;
00886 
00887    findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
00888    if (!findme_user_list) {
00889       ast_log(LOG_WARNING, "Failed to allocate memory for findme_user_list\n");
00890       return;
00891    }
00892    AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
00893 
00894    caller = tpargs->chan;
00895    for (idx = 1; !ast_check_hangup(caller); ++idx) {
00896       /* Find next followme numbers to dial. */
00897       AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
00898          if (nm->order == idx) {
00899             break;
00900          }
00901       }
00902       if (!nm) {
00903          break;
00904       }
00905 
00906       ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);
00907 
00908       ast_copy_string(num, nm->number, sizeof(num));
00909       for (number = num; number; number = rest) {
00910          rest = strchr(number, '&');
00911          if (rest) {
00912             *rest++ = 0;
00913          }
00914 
00915          /* We check if the extension exists, before creating the ast_channel struct */
00916          if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL))) {
00917             ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
00918             continue;
00919          }
00920 
00921          if (!strcmp(tpargs->context, "")) {
00922             snprintf(dialarg, sizeof(dialarg), "%s", number);
00923          } else {
00924             snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context);
00925          }
00926 
00927          tmpuser = ast_calloc(1, sizeof(*tmpuser));
00928          if (!tmpuser) {
00929             continue;
00930          }
00931 
00932          outbound = ast_request("Local", ast_best_codec(caller->nativeformats), caller, dialarg, &dg);
00933          if (outbound) {
00934             ast_channel_lock_both(caller, outbound);
00935             ast_connected_line_copy_from_caller(&outbound->connected, &caller->caller);
00936             ast_channel_inherit_variables(caller, outbound);
00937             ast_channel_datastore_inherit(caller, outbound);
00938             ast_string_field_set(outbound, language, caller->language);
00939             ast_string_field_set(outbound, accountcode, caller->accountcode);
00940             ast_string_field_set(outbound, musicclass, caller->musicclass);
00941             ast_channel_unlock(outbound);
00942             ast_channel_unlock(caller);
00943             ast_verb(3, "calling Local/%s\n", dialarg);
00944             if (!ast_call(outbound, dialarg, 0)) {
00945                tmpuser->ochan = outbound;
00946                tmpuser->state = 0;
00947                tmpuser->cleared = 0;
00948                ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
00949                AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
00950             } else {
00951                ast_verb(3, "couldn't reach at this number.\n");
00952                ast_channel_lock(outbound);
00953                if (!outbound->cdr) {
00954                   outbound->cdr = ast_cdr_alloc();
00955                }
00956                if (outbound->cdr) {
00957                   char tmp[256];
00958 
00959                   ast_cdr_init(outbound->cdr, outbound);
00960                   snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
00961                   ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00962                   ast_cdr_update(outbound);
00963                   ast_cdr_start(outbound->cdr);
00964                   ast_cdr_end(outbound->cdr);
00965                   /* If the cause wasn't handled properly */
00966                   if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
00967                      ast_cdr_failed(outbound->cdr);
00968                   }
00969                } else {
00970                   ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
00971                }
00972                ast_channel_unlock(outbound);
00973                ast_hangup(outbound);
00974                ast_free(tmpuser);
00975             }
00976          } else {
00977             ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
00978             ast_free(tmpuser);
00979          }
00980       }
00981 
00982       if (AST_LIST_EMPTY(findme_user_list)) {
00983          continue;
00984       }
00985 
00986       winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, tpargs);
00987       if (!winner) {
00988          continue;
00989       }
00990 
00991       /* Destroy losing calls up to the winner.  The rest will be destroyed later. */
00992       while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
00993          if (fmuser->ochan == winner) {
00994             /* Pass any connected line info up. */
00995             tpargs->connected_out = fmuser->connected;
00996             tpargs->pending_out_connected_update = fmuser->pending_connected_update;
00997             ast_free(fmuser);
00998             break;
00999          } else {
01000             /* Destroy losing call. */
01001             if (!fmuser->cleared) {
01002                clear_caller(fmuser);
01003             }
01004             ast_party_connected_line_free(&fmuser->connected);
01005             ast_free(fmuser);
01006          }
01007       }
01008       break;
01009    }
01010    destroy_calling_tree(findme_user_list);
01011    if (!winner) {
01012       tpargs->status = 1;
01013    } else {
01014       tpargs->status = 100;
01015       tpargs->outbound = winner;
01016    }
01017 }
01018 
01019 static struct call_followme *find_realtime(const char *name)
01020 {
01021    struct ast_variable *var;
01022    struct ast_variable *v;
01023    struct ast_config *cfg;
01024    const char *catg;
01025    struct call_followme *new_follower;
01026    struct ast_str *str;
01027 
01028    str = ast_str_create(16);
01029    if (!str) {
01030       return NULL;
01031    }
01032 
01033    var = ast_load_realtime("followme", "name", name, SENTINEL);
01034    if (!var) {
01035       ast_free(str);
01036       return NULL;
01037    }
01038 
01039    if (!(new_follower = alloc_profile(name))) {
01040       ast_variables_destroy(var);
01041       ast_free(str);
01042       return NULL;
01043    }
01044 
01045    for (v = var; v; v = v->next) {
01046       if (!strcasecmp(v->name, "active")) {
01047          if (ast_false(v->value)) {
01048             ast_mutex_destroy(&new_follower->lock);
01049             ast_free(new_follower);
01050             ast_variables_destroy(var);
01051             ast_free(str);
01052             return NULL;
01053          }
01054       } else {
01055          profile_set_param(new_follower, v->name, v->value, 0, 0);
01056       }
01057    }
01058 
01059    ast_variables_destroy(var);
01060    new_follower->realtime = 1;
01061 
01062    /* Load numbers */
01063    cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
01064       name, SENTINEL);
01065    if (!cfg) {
01066       ast_mutex_destroy(&new_follower->lock);
01067       ast_free(new_follower);
01068       ast_free(str);
01069       return NULL;
01070    }
01071 
01072    for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
01073       const char *numstr;
01074       const char *timeoutstr;
01075       const char *ordstr;
01076       int timeout;
01077       struct number *cur;
01078 
01079       if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
01080          continue;
01081       }
01082       if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
01083          || sscanf(timeoutstr, "%30d", &timeout) != 1
01084          || timeout < 1) {
01085          timeout = 25;
01086       }
01087       /* This one has to exist; it was part of the query */
01088       ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
01089       ast_str_set(&str, 0, "%s", numstr);
01090       if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
01091          AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
01092       }
01093    }
01094    ast_config_destroy(cfg);
01095 
01096    ast_free(str);
01097    return new_follower;
01098 }
01099 
01100 static void end_bridge_callback(void *data)
01101 {
01102    char buf[80];
01103    time_t end;
01104    struct ast_channel *chan = data;
01105 
01106    time(&end);
01107 
01108    ast_channel_lock(chan);
01109    if (chan->cdr->answer.tv_sec) {
01110       snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
01111       pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
01112    }
01113 
01114    if (chan->cdr->start.tv_sec) {
01115       snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
01116       pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
01117    }
01118    ast_channel_unlock(chan);
01119 }
01120 
01121 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
01122 {
01123    bconfig->end_bridge_callback_data = originator;
01124 }
01125 
01126 static int app_exec(struct ast_channel *chan, const char *data)
01127 {
01128    struct fm_args targs = { 0, };
01129    struct ast_bridge_config config;
01130    struct call_followme *f;
01131    struct number *nm, *newnm;
01132    int res = 0;
01133    char *argstr;
01134    struct ast_channel *caller;
01135    struct ast_channel *outbound;
01136    AST_DECLARE_APP_ARGS(args,
01137       AST_APP_ARG(followmeid);
01138       AST_APP_ARG(options);
01139    );
01140 
01141    if (ast_strlen_zero(data)) {
01142       ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01143       return -1;
01144    }
01145 
01146    argstr = ast_strdupa((char *) data);
01147 
01148    AST_STANDARD_APP_ARGS(args, argstr);
01149 
01150    if (ast_strlen_zero(args.followmeid)) {
01151       ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01152       return -1;
01153    }
01154 
01155    AST_RWLIST_RDLOCK(&followmes);
01156    AST_RWLIST_TRAVERSE(&followmes, f, entry) {
01157       if (!strcasecmp(f->name, args.followmeid) && (f->active))
01158          break;
01159    }
01160    AST_RWLIST_UNLOCK(&followmes);
01161 
01162    ast_debug(1, "New profile %s.\n", args.followmeid);
01163 
01164    if (!f) {
01165       f = find_realtime(args.followmeid);
01166    }
01167 
01168    if (!f) {
01169       ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
01170       return 0;
01171    }
01172 
01173    /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
01174    if (args.options) 
01175       ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
01176 
01177    /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
01178    ast_mutex_lock(&f->lock);
01179    targs.mohclass = ast_strdupa(f->moh);
01180    ast_copy_string(targs.context, f->context, sizeof(targs.context));
01181    ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
01182    ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
01183    ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
01184    ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
01185    ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
01186    ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
01187    ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
01188    ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
01189    /* Copy the numbers we're going to use into another list in case the master list should get modified 
01190       (and locked) while we're trying to do a follow-me */
01191    AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
01192    AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
01193       newnm = create_followme_number(nm->number, nm->timeout, nm->order);
01194       if (newnm) {
01195          AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
01196       }
01197    }
01198    ast_mutex_unlock(&f->lock);
01199 
01200    /* Answer the call */
01201    if (chan->_state != AST_STATE_UP) {
01202       ast_answer(chan);
01203    }
01204 
01205    if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG)) 
01206       ast_stream_and_wait(chan, targs.statusprompt, "");
01207 
01208    if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME)) {
01209       int duration = 5;
01210 
01211       snprintf(targs.namerecloc, sizeof(targs.namerecloc), "%s/followme.%s",
01212          ast_config_AST_SPOOL_DIR, chan->uniqueid);
01213 
01214       if (ast_play_and_record(chan, "vm-rec-name", targs.namerecloc, 5, "sln", &duration,
01215          NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) {
01216          goto outrun;
01217       }
01218 
01219       if (!ast_fileexists(targs.namerecloc, NULL, chan->language)) {
01220          targs.namerecloc[0] = '\0';
01221       }
01222    }
01223 
01224    if (!ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) {
01225       if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
01226          goto outrun;
01227       if (ast_waitstream(chan, "") < 0)
01228          goto outrun;
01229    }
01230    ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
01231 
01232    targs.status = 0;
01233    targs.chan = chan;
01234    ast_channel_lock(chan);
01235    ast_connected_line_copy_from_caller(&targs.connected_in, &chan->caller);
01236    ast_channel_unlock(chan);
01237 
01238    findmeexec(&targs);
01239    if (targs.status != 100) {
01240       ast_moh_stop(chan);
01241       if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) 
01242          ast_stream_and_wait(chan, targs.sorryprompt, "");
01243       res = 0;
01244    } else {
01245       caller = chan;
01246       outbound = targs.outbound;
01247       /* Bridge the two channels. */
01248 
01249       memset(&config, 0, sizeof(config));
01250       ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01251       ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01252       ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01253       config.end_bridge_callback = end_bridge_callback;
01254       config.end_bridge_callback_data = chan;
01255       config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
01256 
01257       ast_moh_stop(caller);
01258       /* Be sure no generators are left on it */
01259       ast_deactivate_generator(caller);
01260       /* Make sure channels are compatible */
01261       res = ast_channel_make_compatible(caller, outbound);
01262       if (res < 0) {
01263          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
01264          ast_hangup(outbound);
01265          goto outrun;
01266       }
01267 
01268       /* Update connected line to caller if available. */
01269       if (targs.pending_out_connected_update) {
01270          if (ast_channel_connected_line_macro(outbound, caller, &targs.connected_out, 1, 0)) {
01271             ast_channel_update_connected_line(caller, &targs.connected_out, NULL);
01272          }
01273       }
01274 
01275       /* Update connected line to winner if changed. */
01276       if (targs.pending_in_connected_update) {
01277          if (ast_channel_connected_line_macro(caller, outbound, &targs.connected_in, 0, 0)) {
01278             ast_channel_update_connected_line(outbound, &targs.connected_in, NULL);
01279          }
01280       }
01281 
01282       res = ast_bridge_call(caller, outbound, &config);
01283       ast_hangup(outbound);
01284    }
01285 
01286 outrun:
01287    while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry))) {
01288       ast_free(nm);
01289    }
01290    if (!ast_strlen_zero(targs.namerecloc)) {
01291       unlink(targs.namerecloc);
01292    }
01293    ast_party_connected_line_free(&targs.connected_in);
01294    ast_party_connected_line_free(&targs.connected_out);
01295 
01296    if (f->realtime) {
01297       /* Not in list */
01298       free_numbers(f);
01299       ast_free(f);
01300    }
01301 
01302    return res;
01303 }
01304 
01305 static int unload_module(void)
01306 {
01307    struct call_followme *f;
01308 
01309    ast_unregister_application(app);
01310 
01311    /* Free Memory. Yeah! I'm free! */
01312    AST_RWLIST_WRLOCK(&followmes);
01313    while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
01314       free_numbers(f);
01315       ast_free(f);
01316    }
01317 
01318    AST_RWLIST_UNLOCK(&followmes);
01319 
01320    return 0;
01321 }
01322 
01323 static int load_module(void)
01324 {
01325    if(!reload_followme(0))
01326       return AST_MODULE_LOAD_DECLINE;
01327 
01328    return ast_register_application_xml(app, app_exec);
01329 }
01330 
01331 static int reload(void)
01332 {
01333    reload_followme(1);
01334 
01335    return 0;
01336 }
01337 
01338 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
01339       .load = load_module,
01340       .unload = unload_module,
01341       .reload = reload,
01342           );

Generated on 7 Sep 2017 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1