Sat Feb 11 06:33:01 2012

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

Generated on Sat Feb 11 06:33:01 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6