Fri Feb 19 17:12:46 2010

Asterisk developer's documentation


res_features.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Routines implementing call features as call pickup, parking and transfer
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  */
00025 
00026 /*** MODULEINFO
00027         <depend>chan_local</depend>
00028  ***/
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 235821 $")
00033 
00034 #include <pthread.h>
00035 #include <stdlib.h>
00036 #include <errno.h>
00037 #include <unistd.h>
00038 #include <string.h>
00039 #include <stdlib.h>
00040 #include <stdio.h>
00041 #include <sys/time.h>
00042 #include <sys/signal.h>
00043 #include <netinet/in.h>
00044 
00045 #include "asterisk/lock.h"
00046 #include "asterisk/file.h"
00047 #include "asterisk/logger.h"
00048 #include "asterisk/channel.h"
00049 #include "asterisk/pbx.h"
00050 #include "asterisk/options.h"
00051 #include "asterisk/causes.h"
00052 #include "asterisk/module.h"
00053 #include "asterisk/translate.h"
00054 #include "asterisk/app.h"
00055 #include "asterisk/say.h"
00056 #include "asterisk/features.h"
00057 #include "asterisk/musiconhold.h"
00058 #include "asterisk/config.h"
00059 #include "asterisk/cli.h"
00060 #include "asterisk/manager.h"
00061 #include "asterisk/utils.h"
00062 #include "asterisk/adsi.h"
00063 #include "asterisk/devicestate.h"
00064 #include "asterisk/monitor.h"
00065 #include "asterisk/global_datastores.h"
00066 
00067 #define DEFAULT_PARK_TIME 45000
00068 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
00069 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 1000
00070 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
00071 
00072 #define AST_MAX_WATCHERS 256
00073 #define MAX_DIAL_FEATURE_OPTIONS 30
00074 
00075 #define FEATURE_RETURN_HANGUP                  -1
00076 #define FEATURE_RETURN_SUCCESSBREAK             0
00077 #define FEATURE_RETURN_PASSDIGITS               21
00078 #define FEATURE_RETURN_STOREDIGITS              22
00079 #define FEATURE_RETURN_SUCCESS                  23
00080 #define FEATURE_RETURN_KEEPTRYING               24
00081 #define FEATURE_RETURN_PARKFAILED               25
00082 
00083 enum {
00084    AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0),
00085    AST_FEATURE_FLAG_ONPEER =    (1 << 1),
00086    AST_FEATURE_FLAG_ONSELF =    (1 << 2),
00087    AST_FEATURE_FLAG_BYCALLEE =  (1 << 3),
00088    AST_FEATURE_FLAG_BYCALLER =  (1 << 4),
00089    AST_FEATURE_FLAG_BYBOTH  =   (3 << 3),
00090 };
00091 
00092 static char *parkedcall = "ParkedCall";
00093 
00094 static int parkaddhints = 0;                               /*!< Add parking hints automatically */
00095 static int parkingtime = DEFAULT_PARK_TIME;                /*!< No more than 45 seconds parked before you do something with them */
00096 static char parking_con[AST_MAX_EXTENSION];                /*!< Context for which parking is made accessible */
00097 static char parking_con_dial[AST_MAX_EXTENSION];           /*!< Context for dialback for parking (KLUDGE) */
00098 static char parking_ext[AST_MAX_EXTENSION];                /*!< Extension you type to park the call */
00099 static char pickup_ext[AST_MAX_EXTENSION];                 /*!< Call pickup extension */
00100 static char parkmohclass[MAX_MUSICCLASS];                  /*!< Music class used for parking */
00101 static int parking_start;                                  /*!< First available extension for parking */
00102 static int parking_stop;                                   /*!< Last available extension for parking */
00103 
00104 static int parkedcalltransfers;                            /*!< Who can REDIRECT after picking up a parked a call */
00105 static int parkedcallreparking;                            /*!< Who can PARKCALL after picking up a parked call */
00106 static int parkedcallhangup;                               /*!< Who can DISCONNECT after picking up a parked call */
00107 static int parkedcallrecording;                            /*!< Who can AUTOMON after picking up a parked call */
00108 
00109 static char courtesytone[256];                             /*!< Courtesy tone */
00110 static int parkedplay = 0;                                 /*!< Who to play the courtesy tone to */
00111 static char xfersound[256];                                /*!< Call transfer sound */
00112 static char xferfailsound[256];                            /*!< Call transfer failure sound */
00113 
00114 static int parking_offset;
00115 static int parkfindnext;
00116 
00117 static int adsipark;
00118 
00119 static int transferdigittimeout;
00120 static int featuredigittimeout;
00121 
00122 static int atxfernoanswertimeout;
00123 
00124 static char *registrar = "res_features";        /*!< Registrar for operations */
00125 
00126 /* module and CLI command definitions */
00127 static char *synopsis = "Answer a parked call";
00128 
00129 static char *descrip = "ParkedCall(exten):"
00130 "Used to connect to a parked call.  This application is always\n"
00131 "registered internally and does not need to be explicitly added\n"
00132 "into the dialplan, although you should include the 'parkedcalls'\n"
00133 "context.\n";
00134 
00135 static char *parkcall = PARK_APP_NAME;
00136 
00137 static char *synopsis2 = "Park yourself";
00138 
00139 static char *descrip2 = "Park():"
00140 "Used to park yourself (typically in combination with a supervised\n"
00141 "transfer to know the parking space). This application is always\n"
00142 "registered internally and does not need to be explicitly added\n"
00143 "into the dialplan, although you should include the 'parkedcalls'\n"
00144 "context (or the context specified in features.conf).\n\n"
00145 "If you set the PARKINGEXTEN variable to an extension in your\n"
00146 "parking context, park() will park the call on that extension, unless\n"
00147 "it already exists. In that case, execution will continue at next\n"
00148 "priority.\n" ;
00149 
00150 static struct ast_app *monitor_app = NULL;
00151 static int monitor_ok = 1;
00152 
00153 struct parkeduser {
00154    struct ast_channel *chan;                   /*!< Parking channel */
00155    struct timeval start;                       /*!< Time the parking started */
00156    int parkingnum;                             /*!< Parking lot */
00157    char parkingexten[AST_MAX_EXTENSION];       /*!< If set beforehand, parking extension used for this call */
00158    char context[AST_MAX_CONTEXT];              /*!< Where to go if our parking time expires */
00159    char exten[AST_MAX_EXTENSION];
00160    int priority;
00161    int parkingtime;                            /*!< Maximum length in parking lot before return */
00162    int notquiteyet;
00163    char peername[1024];
00164    unsigned char moh_trys;
00165    struct parkeduser *next;
00166 };
00167 
00168 static struct parkeduser *parkinglot;
00169 
00170 AST_MUTEX_DEFINE_STATIC(parking_lock); /*!< protects all static variables above */
00171 
00172 static pthread_t parking_thread;
00173 
00174 struct ast_dial_features {
00175    struct ast_flags features_caller;
00176    struct ast_flags features_callee;
00177    int is_caller;
00178 };
00179 
00180 static void *dial_features_duplicate(void *data)
00181 {
00182    struct ast_dial_features *df = data, *df_copy;
00183 
00184    if (!(df_copy = ast_calloc(1, sizeof(*df)))) {
00185       return NULL;
00186    }
00187 
00188    memcpy(df_copy, df, sizeof(*df));
00189 
00190    return df_copy;
00191 }
00192 
00193 static void dial_features_destroy(void *data)
00194 {
00195    struct ast_dial_features *df = data;
00196    if (df) {
00197       ast_free(df);
00198    }
00199 }
00200 
00201 const struct ast_datastore_info dial_features_info = {
00202    .type = "dial-features",
00203    .destroy = dial_features_destroy,
00204    .duplicate = dial_features_duplicate,
00205 };
00206 
00207 char *ast_parking_ext(void)
00208 {
00209    return parking_ext;
00210 }
00211 
00212 char *ast_pickup_ext(void)
00213 {
00214    return pickup_ext;
00215 }
00216 
00217 struct ast_bridge_thread_obj 
00218 {
00219    struct ast_bridge_config bconfig;
00220    struct ast_channel *chan;
00221    struct ast_channel *peer;
00222 };
00223 
00224 /*! \brief store context, priority and extension */
00225 static void set_c_e_p(struct ast_channel *chan, const char *context, const char *ext, int pri)
00226 {
00227    ast_copy_string(chan->context, context, sizeof(chan->context));
00228    ast_copy_string(chan->exten, ext, sizeof(chan->exten));
00229    chan->priority = pri;
00230 }
00231 
00232 static void check_goto_on_transfer(struct ast_channel *chan) 
00233 {
00234    struct ast_channel *xferchan;
00235    const char *val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR");
00236    char *x, *goto_on_transfer;
00237    struct ast_frame *f;
00238 
00239    if (ast_strlen_zero(val))
00240       return;
00241 
00242    goto_on_transfer = ast_strdupa(val);
00243 
00244    if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "%s", chan->name)))
00245       return;
00246 
00247    for (x = goto_on_transfer; x && *x; x++) {
00248       if (*x == '^')
00249          *x = '|';
00250    }
00251    /* Make formats okay */
00252    xferchan->readformat = chan->readformat;
00253    xferchan->writeformat = chan->writeformat;
00254    ast_channel_masquerade(xferchan, chan);
00255    ast_parseable_goto(xferchan, goto_on_transfer);
00256    xferchan->_state = AST_STATE_UP;
00257    ast_clear_flag(xferchan, AST_FLAGS_ALL);  
00258    xferchan->_softhangup = 0;
00259    if ((f = ast_read(xferchan))) {
00260       ast_frfree(f);
00261       f = NULL;
00262       ast_pbx_start(xferchan);
00263    } else {
00264       ast_hangup(xferchan);
00265    }
00266 }
00267 
00268 static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *caller, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, const char *language);
00269 
00270 static void *ast_bridge_call_thread(void *data)
00271 {
00272    struct ast_bridge_thread_obj *tobj = data;
00273 
00274    tobj->chan->appl = "Transferred Call";
00275    tobj->chan->data = tobj->peer->name;
00276    tobj->peer->appl = "Transferred Call";
00277    tobj->peer->data = tobj->chan->name;
00278 
00279    ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
00280    ast_hangup(tobj->chan);
00281    ast_hangup(tobj->peer);
00282    bzero(tobj, sizeof(*tobj)); /*! \todo XXX for safety */
00283    free(tobj);
00284    return NULL;
00285 }
00286 
00287 static void ast_bridge_call_thread_launch(void *data) 
00288 {
00289    pthread_t thread;
00290    pthread_attr_t attr;
00291    struct sched_param sched;
00292 
00293    pthread_attr_init(&attr);
00294    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00295    ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data);
00296    pthread_attr_destroy(&attr);
00297    memset(&sched, 0, sizeof(sched));
00298    pthread_setschedparam(thread, SCHED_RR, &sched);
00299 }
00300 
00301 static int adsi_announce_park(struct ast_channel *chan, char *parkingexten)
00302 {
00303    int res;
00304    int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
00305    char tmp[256];
00306    char *message[5] = {NULL, NULL, NULL, NULL, NULL};
00307 
00308    snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten);
00309    message[0] = tmp;
00310    res = ast_adsi_load_session(chan, NULL, 0, 1);
00311    if (res == -1)
00312       return res;
00313    return ast_adsi_print(chan, message, justify, 1);
00314 }
00315 
00316 /*! \brief Notify metermaids that we've changed an extension */
00317 static void notify_metermaids(char *exten, char *context)
00318 {
00319    if (option_debug > 3)
00320       ast_log(LOG_DEBUG, "Notification of state change to metermaids %s@%s\n", exten, context);
00321 
00322    /* Send notification to devicestate subsystem */
00323    ast_device_state_changed("park:%s@%s", exten, context);
00324    return;
00325 }
00326 
00327 /*! \brief metermaids callback from devicestate.c */
00328 static int metermaidstate(const char *data)
00329 {
00330    int res = AST_DEVICE_INVALID;
00331    char *context = ast_strdupa(data);
00332    char *exten;
00333 
00334    exten = strsep(&context, "@");
00335    if (!context)
00336       return res;
00337    
00338    if (option_debug > 3)
00339       ast_log(LOG_DEBUG, "Checking state of exten %s in context %s\n", exten, context);
00340 
00341    res = ast_exists_extension(NULL, context, exten, 1, NULL);
00342 
00343    if (!res)
00344       return AST_DEVICE_NOT_INUSE;
00345    else
00346       return AST_DEVICE_INUSE;
00347 }
00348 
00349 static struct parkeduser *park_space_reserve(struct ast_channel *chan)
00350 {
00351    struct parkeduser *pu, *cur;
00352    int i, parking_space = -1, parking_range;
00353    const char *parkingexten;
00354 
00355    /* Allocate memory for parking data */
00356    if (!(pu = ast_calloc(1, sizeof(*pu)))) 
00357       return NULL;
00358 
00359    /* Lock parking lot */
00360    ast_mutex_lock(&parking_lock);
00361    /* Check for channel variable PARKINGEXTEN */
00362    parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN");
00363    if (!ast_strlen_zero(parkingexten)) {
00364       /*!\note The API forces us to specify a numeric parking slot, even
00365        * though the architecture would tend to support non-numeric extensions
00366        * (as are possible with SIP, for example).  Hence, we enforce that
00367        * limitation here.  If extout was not numeric, we could permit
00368        * arbitrary non-numeric extensions.
00369        */
00370       if (sscanf(parkingexten, "%30d", &parking_space) != 1 || parking_space < 0) {
00371          ast_log(LOG_WARNING, "PARKINGEXTEN does not indicate a valid parking slot: '%s'.\n", parkingexten);
00372          ast_mutex_unlock(&parking_lock);
00373          free(pu);
00374          return NULL;
00375       }
00376       snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", parking_space);
00377 
00378       if (ast_exists_extension(NULL, parking_con, pu->parkingexten, 1, NULL)) {
00379          ast_mutex_unlock(&parking_lock);
00380          ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con);
00381          free(pu);
00382          return NULL;
00383       }
00384    } else {
00385       /* Select parking space within range */
00386       parking_range = parking_stop - parking_start+1;
00387       for (i = 0; i < parking_range; i++) {
00388          parking_space = (i + parking_offset) % parking_range + parking_start;
00389          cur = parkinglot;
00390          while(cur) {
00391             if (cur->parkingnum == parking_space) 
00392                break;
00393             cur = cur->next;
00394          }
00395          if (!cur)
00396             break;
00397       }
00398 
00399       if (!(i < parking_range)) {
00400          ast_log(LOG_WARNING, "No more parking spaces\n");
00401          ast_mutex_unlock(&parking_lock);
00402          free(pu);
00403          return NULL;
00404       }
00405       /* Set pointer for next parking */
00406       if (parkfindnext) 
00407          parking_offset = parking_space - parking_start + 1;
00408       snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", parking_space);
00409    }
00410    
00411    pu->notquiteyet = 1;
00412    pu->parkingnum = parking_space;
00413    pu->next = parkinglot;
00414    parkinglot = pu;
00415    ast_mutex_unlock(&parking_lock);
00416 
00417    return pu;
00418 }
00419 
00420 static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout, const char *orig_chan_name, struct parkeduser *pu)
00421 {
00422    struct ast_context *con;
00423    int parkingnum_copy;
00424    const char *event_from;
00425 
00426    /* Get a valid space if not already done */
00427    if (pu == NULL)
00428       pu = park_space_reserve(chan);
00429    if (pu == NULL)
00430       return 1; /* Continue execution if possible */
00431 
00432    snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", pu->parkingnum);
00433    
00434    chan->appl = "Parked Call";
00435    chan->data = NULL; 
00436 
00437    pu->chan = chan;
00438    
00439    /* Put the parked channel on hold if we have two different channels */
00440    if (chan != peer) {
00441       ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
00442          S_OR(parkmohclass, NULL),
00443          !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
00444    }
00445    
00446    pu->start = ast_tvnow();
00447    pu->parkingtime = (timeout > 0) ? timeout : parkingtime;
00448    if (extout)
00449       *extout = pu->parkingnum;
00450 
00451    if (peer) { 
00452       /* This is so ugly that it hurts, but implementing get_base_channel() on local channels
00453          could have ugly side effects.  We could have transferer<->local,1<->local,2<->parking
00454          and we need the callback name to be that of transferer.  Since local,1/2 have the same
00455          name we can be tricky and just grab the bridged channel from the other side of the local
00456       */
00457       if (!strcasecmp(peer->tech->type, "Local")) {
00458          struct ast_channel *tmpchan, *base_peer;
00459          char other_side[AST_CHANNEL_NAME];
00460          char *c;
00461          ast_copy_string(other_side, S_OR(orig_chan_name, peer->name), sizeof(other_side));
00462          if ((c = strrchr(other_side, ','))) {
00463             *++c = '1';
00464          }
00465          if ((tmpchan = ast_get_channel_by_name_locked(other_side))) {
00466             if ((base_peer = ast_bridged_channel(tmpchan))) {
00467                ast_copy_string(pu->peername, base_peer->name, sizeof(pu->peername));
00468             }
00469             ast_channel_unlock(tmpchan);
00470          }
00471       } else {
00472          ast_copy_string(pu->peername, S_OR(orig_chan_name, peer->name), sizeof(pu->peername));
00473       }
00474    }
00475 
00476    /* Remember what had been dialed, so that if the parking
00477       expires, we try to come back to the same place */
00478    ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context));
00479    ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten));
00480    pu->priority = chan->macropriority ? chan->macropriority : chan->priority;
00481    parkingnum_copy = pu->parkingnum;
00482 
00483    /* If parking a channel directly (peer == chan), don't quite yet get parking running on it.
00484      * All parking lot entires are put into the parking lot with notquiteyet on. */
00485    if (peer != chan) 
00486       pu->notquiteyet = 0;
00487 
00488    if (option_verbose > 1) 
00489       ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d@%s. Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, parking_con, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000));
00490 
00491    if (peer) {
00492       event_from = peer->name;
00493    } else {
00494       event_from = pbx_builtin_getvar_helper(chan, "BLINDTRANSFER");
00495    }
00496 
00497    manager_event(EVENT_FLAG_CALL, "ParkedCall",
00498       "Exten: %s\r\n"
00499       "Channel: %s\r\n"
00500       "From: %s\r\n"
00501       "Timeout: %ld\r\n"
00502       "CallerID: %s\r\n"
00503       "CallerIDName: %s\r\n",
00504       pu->parkingexten, pu->chan->name, event_from ? event_from : "",
00505       (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
00506       S_OR(pu->chan->cid.cid_num, "<unknown>"),
00507       S_OR(pu->chan->cid.cid_name, "<unknown>")
00508       );
00509 
00510    if (peer && adsipark && ast_adsi_available(peer)) {
00511       adsi_announce_park(peer, pu->parkingexten);  /* Only supports parking numbers */
00512       ast_adsi_unload_session(peer);
00513    }
00514 
00515    con = ast_context_find(parking_con);
00516    if (!con) 
00517       con = ast_context_create(NULL, parking_con, registrar);
00518    if (!con)   /* Still no context? Bad */
00519       ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
00520    if (con) {
00521       if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, strdup(pu->parkingexten), ast_free_ptr, registrar)) {
00522          notify_metermaids(pu->parkingexten, parking_con);
00523       }
00524    }
00525 
00526    /* Wake up the (presumably select()ing) thread */
00527    pthread_kill(parking_thread, SIGURG);
00528 
00529    /* Only say number if it's a number and the channel hasn't been masqueraded away */
00530    if (peer && (ast_strlen_zero(orig_chan_name) || !strcasecmp(peer->name, orig_chan_name))) {
00531       /* Make sure we don't start saying digits to the channel being parked */
00532       ast_set_flag(peer, AST_FLAG_MASQ_NOSTREAM);
00533       /* Tell the peer channel the number of the parking space */
00534       ast_say_digits(peer, parkingnum_copy, "", peer->language);
00535       ast_clear_flag(peer, AST_FLAG_MASQ_NOSTREAM);
00536    }
00537 
00538    if (peer == chan) { /* pu->notquiteyet = 1 */
00539       /* Wake up parking thread if we're really done */
00540       ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
00541          S_OR(parkmohclass, NULL),
00542          !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
00543       pu->notquiteyet = 0;
00544       pthread_kill(parking_thread, SIGURG);
00545    }
00546    return 0;
00547 }
00548 
00549 /*! \brief Park a call 
00550    \note We put the user in the parking list, then wake up the parking thread to be sure it looks
00551    after these channels too */
00552 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
00553 {
00554    return park_call_full(chan, peer, timeout, extout, NULL, NULL);
00555 }
00556 
00557 static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout, int play_announcement, const char *orig_chan_name)
00558 {
00559    struct ast_channel *chan;
00560    struct ast_frame *f;
00561    struct parkeduser *pu;
00562    int park_status;
00563 
00564    if ((pu = park_space_reserve(rchan)) == NULL) {
00565       if (peer)
00566          ast_stream_and_wait(peer, "beeperr", peer->language, "");
00567       return FEATURE_RETURN_PARKFAILED;
00568    }
00569 
00570    /* Make a new, fake channel that we'll use to masquerade in the real one */
00571    if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->amaflags, "Parked/%s",rchan->name))) {
00572       ast_log(LOG_WARNING, "Unable to create parked channel\n");
00573       return -1;
00574    }
00575 
00576    /* Make formats okay */
00577    chan->readformat = rchan->readformat;
00578    chan->writeformat = rchan->writeformat;
00579    ast_channel_masquerade(chan, rchan);
00580 
00581    /* Setup the extensions and such */
00582    set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority);
00583 
00584    /* Make the masq execute */
00585    if ((f = ast_read(chan))) {
00586       ast_frfree(f);
00587    }
00588 
00589    if (peer == rchan) {
00590       peer = chan;
00591    }
00592 
00593    if (!play_announcement || !orig_chan_name) {
00594       /* chan is the channel being parked, peer is the effective park-er */
00595       orig_chan_name = ast_strdupa(peer->name);
00596    }
00597 
00598    park_status = park_call_full(chan, peer, timeout, extout, orig_chan_name, pu);
00599    if (park_status == 1) {
00600       /* would be nice to play: "invalid parking extension" */
00601       ast_hangup(chan);
00602       return -1;
00603    }
00604 
00605    return 0;
00606 }
00607 
00608 int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
00609 {
00610    return masq_park_call(rchan, peer, timeout, extout, 0, NULL);
00611 }
00612 
00613 static int masq_park_call_announce(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout, const char *orig_chan_name)
00614 {
00615    return masq_park_call(rchan, peer, timeout, extout, 1, orig_chan_name);
00616 }
00617 
00618 /*! \brief
00619  * set caller and callee according to the direction
00620  */
00621 static void set_peers(struct ast_channel **caller, struct ast_channel **callee,
00622    struct ast_channel *peer, struct ast_channel *chan, int sense)
00623 {
00624    if (sense == FEATURE_SENSE_PEER) {
00625       *caller = peer;
00626       *callee = chan;
00627    } else {
00628       *callee = peer;
00629       *caller = chan;
00630    }
00631 }
00632 
00633 /*! \brief support routing for one touch call parking */
00634 static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
00635 {
00636    struct ast_channel *parker;
00637    struct ast_channel *parkee;
00638    int res = 0;
00639    struct ast_module_user *u;
00640    const char *orig_chan_name;
00641 
00642    u = ast_module_user_add(chan);
00643 
00644    set_peers(&parker, &parkee, peer, chan, sense);
00645    orig_chan_name = ast_strdupa(parker->name);
00646    /* we used to set chan's exten and priority to "s" and 1
00647       here, but this generates (in some cases) an invalid
00648       extension, and if "s" exists, could errantly
00649       cause execution of extensions you don't expect It
00650       makes more sense to let nature take its course
00651       when chan finishes, and let the pbx do its thing
00652       and hang up when the park is over.
00653    */
00654    if (chan->_state != AST_STATE_UP)
00655       res = ast_answer(chan);
00656    if (!res)
00657       res = ast_safe_sleep(chan, 1000);
00658 
00659    if (!res) { /* one direction used to call park_call.... */
00660       res = masq_park_call_announce(parkee, parker, 0, NULL, orig_chan_name);
00661       /* PBX should hangup zombie channel if a masquerade actually occurred (res=0) */
00662    }
00663 
00664    ast_module_user_remove(u);
00665    return res;
00666 }
00667 
00668 static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
00669 {
00670    char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
00671    int x = 0;
00672    size_t len;
00673    struct ast_channel *caller_chan, *callee_chan;
00674 
00675    if (!monitor_ok) {
00676       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
00677       return -1;
00678    }
00679 
00680    if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) {
00681       monitor_ok = 0;
00682       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
00683       return -1;
00684    }
00685 
00686    set_peers(&caller_chan, &callee_chan, peer, chan, sense);
00687 
00688    if (!ast_strlen_zero(courtesytone)) {
00689       if (ast_autoservice_start(callee_chan))
00690          return -1;
00691       if (ast_stream_and_wait(caller_chan, courtesytone, caller_chan->language, "")) {
00692          ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
00693          ast_autoservice_stop(callee_chan);
00694          return -1;
00695       }
00696       if (ast_autoservice_stop(callee_chan))
00697          return -1;
00698    }
00699    
00700    if (callee_chan->monitor) {
00701       if (option_verbose > 3)
00702          ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code);
00703       ast_monitor_stop(callee_chan, 1);
00704       return FEATURE_RETURN_SUCCESS;
00705    }
00706 
00707    if (caller_chan && callee_chan) {
00708       const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT");
00709       const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
00710 
00711       if (!touch_format)
00712          touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT");
00713 
00714       if (!touch_monitor)
00715          touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
00716    
00717       if (touch_monitor) {
00718          len = strlen(touch_monitor) + 50;
00719          args = alloca(len);
00720          touch_filename = alloca(len);
00721          snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
00722          snprintf(args, len, "%s|%s|m", (touch_format) ? touch_format : "wav", touch_filename);
00723       } else {
00724          caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name));
00725          callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name));
00726          len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
00727          args = alloca(len);
00728          touch_filename = alloca(len);
00729          snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
00730          snprintf(args, len, "%s|%s|m", S_OR(touch_format, "wav"), touch_filename);
00731       }
00732 
00733       for( x = 0; x < strlen(args); x++) {
00734          if (args[x] == '/')
00735             args[x] = '-';
00736       }
00737       
00738       if (option_verbose > 3)
00739          ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, args);
00740 
00741       pbx_exec(callee_chan, monitor_app, args);
00742       pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
00743       pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
00744    
00745       return FEATURE_RETURN_SUCCESS;
00746    }
00747    
00748    ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");  
00749    return -1;
00750 }
00751 
00752 static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
00753 {
00754    if (option_verbose > 3)
00755       ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code);
00756    return FEATURE_RETURN_HANGUP;
00757 }
00758 
00759 static int finishup(struct ast_channel *chan)
00760 {
00761         ast_indicate(chan, AST_CONTROL_UNHOLD);
00762   
00763         return ast_autoservice_stop(chan);
00764 }
00765 
00766 /*! \brief Find the context for the transfer */
00767 static const char *real_ctx(struct ast_channel *transferer, struct ast_channel *transferee)
00768 {
00769         const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
00770         if (ast_strlen_zero(s))
00771                 s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
00772         if (ast_strlen_zero(s)) /* Use the non-macro context to transfer the call XXX ? */
00773                 s = transferer->macrocontext;
00774         if (ast_strlen_zero(s))
00775                 s = transferer->context;
00776         return s;  
00777 }
00778 
00779 static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
00780 {
00781    struct ast_channel *transferer;
00782    struct ast_channel *transferee;
00783    const char *transferer_real_context;
00784    char xferto[256];
00785    int res;
00786    const char *orig_chan_name;
00787    int parkstatus = 0;
00788 
00789    set_peers(&transferer, &transferee, peer, chan, sense);
00790    orig_chan_name = ast_strdupa(transferer->name);
00791    transferer_real_context = real_ctx(transferer, transferee);
00792    /* Start autoservice on chan while we talk to the originator */
00793    ast_autoservice_start(transferee);
00794    ast_indicate(transferee, AST_CONTROL_HOLD);
00795 
00796    memset(xferto, 0, sizeof(xferto));
00797 
00798    /* Transfer */
00799    res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
00800    if (res < 0) {
00801       finishup(transferee);
00802       return -1; /* error ? */
00803    }
00804    if (res > 0)   /* If they've typed a digit already, handle it */
00805       xferto[0] = (char) res;
00806 
00807    ast_stopstream(transferer);
00808    res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
00809    if (res < 0) {  /* hangup, would be 0 for invalid and 1 for valid */
00810       finishup(transferee);
00811       return res;
00812    }
00813    if (!strcmp(xferto, ast_parking_ext())) {
00814       res = finishup(transferee);
00815       if (res)
00816          res = -1;
00817       else if (!(parkstatus = masq_park_call_announce(transferee, transferer, 0, NULL, orig_chan_name))) {  /* success */
00818          /* We return non-zero, but tell the PBX not to hang the channel when
00819             the thread dies -- We have to be careful now though.  We are responsible for 
00820             hanging up the channel, else it will never be hung up! */
00821          return 0;
00822       } else {
00823          ast_log(LOG_WARNING, "Unable to park call %s, parkstatus=%d\n", transferee->name, parkstatus);
00824       }
00825       /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */
00826    } else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
00827       pbx_builtin_setvar_helper(transferer, "BLINDTRANSFER", transferee->name);
00828       pbx_builtin_setvar_helper(transferee, "BLINDTRANSFER", transferer->name);
00829       res=finishup(transferee);
00830       if (!transferer->cdr) { /* this code should never get called (in a perfect world) */
00831          transferer->cdr=ast_cdr_alloc();
00832          if (transferer->cdr) {
00833             ast_cdr_init(transferer->cdr, transferer); /* initilize our channel's cdr */
00834             ast_cdr_start(transferer->cdr);
00835          }
00836       }
00837       if (transferer->cdr) {
00838          struct ast_cdr *swap = transferer->cdr;
00839          /* swap cdrs-- it will save us some time & work */
00840          transferer->cdr = transferee->cdr;
00841          transferee->cdr = swap;
00842       }
00843       if (!transferee->pbx) {
00844          /* Doh!  Use our handy async_goto functions */
00845          if (option_verbose > 2) 
00846             ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
00847                         ,transferee->name, xferto, transferer_real_context);
00848          if (ast_async_goto(transferee, transferer_real_context, xferto, 1))
00849             ast_log(LOG_WARNING, "Async goto failed :-(\n");
00850          res = -1;
00851       } else {
00852          /* Set the channel's new extension, since it exists, using transferer context */
00853          ast_set_flag(transferee, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
00854          set_c_e_p(transferee, transferer_real_context, xferto, 0);
00855       }
00856       check_goto_on_transfer(transferer);
00857       return res;
00858    } else {
00859       if (option_verbose > 2) 
00860          ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context);
00861    }
00862    if (parkstatus != FEATURE_RETURN_PARKFAILED && ast_stream_and_wait(transferer, xferfailsound, transferer->language, AST_DIGIT_ANY) < 0 ) {
00863       finishup(transferee);
00864       return -1;
00865    }
00866    ast_stopstream(transferer);
00867    res = finishup(transferee);
00868    if (res) {
00869       if (option_verbose > 1)
00870          ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
00871       return res;
00872    }
00873    return FEATURE_RETURN_SUCCESS;
00874 }
00875 
00876 static int check_compat(struct ast_channel *c, struct ast_channel *newchan)
00877 {
00878    if (ast_channel_make_compatible(c, newchan) < 0) {
00879       ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
00880          c->name, newchan->name);
00881       ast_hangup(newchan);
00882       return -1;
00883    }
00884    return 0;
00885 }
00886 
00887 /*!
00888  * \brief Attended transfer implementation
00889  * \param chan transfered user
00890  * \param peer person transfering call
00891  * \param config
00892  * \param sense feature options
00893  * 
00894  * \param toExt
00895  * \param toCont
00896  * This is the actual implementation of attended transfer, it can be activated as a regular feature or through the AMI.
00897  * "toExt" is the extension to transfer to (default: ask for it on the transferer channel)
00898  * "toCont" is the context to transfer to (default: the one in which the transferer is)
00899  *
00900  * \return -1 on failure
00901 */
00902 static int do_atxfer(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config,int sense,const char *toExt,const char *toCont)
00903 {
00904    struct ast_channel *transferer;
00905    struct ast_channel *transferee;
00906    const char *transferer_real_context;
00907    const char *transfer_context;
00908    char xferto[256] = "";
00909    int res;
00910    int outstate=0;
00911    struct ast_channel *newchan;
00912    struct ast_channel *xferchan;
00913    struct ast_bridge_thread_obj *tobj;
00914    struct ast_bridge_config bconfig;
00915    struct ast_frame *f;
00916    int l;
00917    struct ast_datastore *features_datastore;
00918    struct ast_dial_features *dialfeatures = NULL;
00919 
00920    if (option_debug)
00921       ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense);
00922    set_peers(&transferer, &transferee, peer, chan, sense);
00923    transferer_real_context = real_ctx(transferer, transferee);
00924    transfer_context = S_OR(toCont, transferer_real_context);
00925 
00926    /* Start autoservice on chan while we talk to the originator */
00927    ast_autoservice_start(transferee);
00928    ast_indicate(transferee, AST_CONTROL_HOLD);
00929 
00930    if (!ast_strlen_zero(toExt)) {
00931       ast_copy_string(xferto, toExt, sizeof(xferto));
00932    } else {
00933       /* Ask for extension to transfer to on the transferer channel */
00934       res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
00935       if (res < 0) {
00936          finishup(transferee);
00937          return res;
00938       }
00939       if (res > 0) /* If they've typed a digit already, handle it */
00940          xferto[0] = (char) res;
00941 
00942       /* this is specific of atxfer */
00943       res = ast_app_dtget(transferer, transfer_context, xferto, sizeof(xferto), 100, transferdigittimeout);
00944       if (res < 0) {  /* hangup, would be 0 for invalid and 1 for valid */
00945          finishup(transferee);
00946          return res;
00947       }
00948       if (res == 0) {
00949          ast_log(LOG_WARNING, "Did not read data.\n");
00950          finishup(transferee);
00951          if (ast_stream_and_wait(transferer, "beeperr", transferer->language, ""))
00952             return -1;
00953          return FEATURE_RETURN_SUCCESS;
00954       }
00955    }
00956 
00957    /* valid extension, res == 1 */
00958    if (!ast_exists_extension(transferer, transfer_context, xferto, 1, transferer->cid.cid_num)) {
00959       ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transfer_context);
00960       finishup(transferee);
00961       if (ast_stream_and_wait(transferer, "beeperr", transferer->language, ""))
00962          return -1;
00963       return FEATURE_RETURN_SUCCESS;
00964    }
00965 
00966    /* If we are attended transfering to parking, just use builtin_parkcall instead of trying to track all of
00967     * the different variables for handling this properly with a builtin_atxfer */
00968    if (!strcmp(xferto, ast_parking_ext())) {
00969       finishup(transferee);
00970       return builtin_parkcall(chan, peer, config, NULL, sense, NULL);
00971    }
00972 
00973    l = strlen(xferto);
00974    snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transfer_context); /* append context */
00975    newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats),
00976       xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name, transferer->language);
00977    ast_indicate(transferer, -1);
00978    if (!newchan) {
00979       finishup(transferee);
00980       /* any reason besides user requested cancel and busy triggers the failed sound */
00981       if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY &&
00982             ast_stream_and_wait(transferer, xferfailsound, transferer->language, ""))
00983          return -1;
00984       return FEATURE_RETURN_SUCCESS;
00985    }
00986 
00987    if (!ast_check_hangup(transferer)) {
00988       if (check_compat(transferer, newchan)) {
00989          /* we do mean transferee here, NOT transferer */
00990          finishup(transferee);
00991          return -1;
00992       }
00993       memset(&bconfig,0,sizeof(struct ast_bridge_config));
00994       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
00995       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
00996       res = ast_bridge_call(transferer, newchan, &bconfig);
00997       if (newchan->_softhangup || !transferer->_softhangup) {
00998          ast_hangup(newchan);
00999          if (ast_stream_and_wait(transferer, xfersound, transferer->language, ""))
01000             ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
01001          finishup(transferee);
01002          transferer->_softhangup = 0;
01003          return FEATURE_RETURN_SUCCESS;
01004       }
01005    } else {
01006       ast_log(LOG_DEBUG, "transferer hangup; outstate = %d\n", outstate);
01007       switch (outstate) {
01008       case AST_CONTROL_RINGING:
01009       {
01010          int connected = 0;
01011          while (!connected && (ast_waitfor(newchan, -1) >= 0)) {
01012             if ((f = ast_read(newchan)) == NULL) {
01013                break;
01014             }
01015             if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_ANSWER) {
01016                connected = 1;
01017             }
01018             ast_frfree(f);
01019          }
01020          if (!connected) {
01021             ast_hangup(newchan);
01022             finishup(transferee);
01023             return -1;
01024          }
01025          /* fall through */
01026       }
01027       case AST_CONTROL_ANSWER:
01028          ast_log(LOG_DEBUG, "transferer hangup; callee answered\n");
01029          break;
01030 
01031       default:
01032          ast_hangup(newchan);
01033          finishup(transferee);
01034          return FEATURE_RETURN_SUCCESS;
01035       }
01036    }
01037 
01038    if (check_compat(transferee, newchan)) {
01039       finishup(transferee);
01040       return -1;
01041    }
01042 
01043    ast_indicate(transferee, AST_CONTROL_UNHOLD);
01044 
01045    if ((ast_autoservice_stop(transferee) < 0)
01046       || (ast_waitfordigit(transferee, 100) < 0)
01047       || (ast_waitfordigit(newchan, 100) < 0) 
01048       || ast_check_hangup(transferee) 
01049       || ast_check_hangup(newchan)) {
01050       ast_hangup(newchan);
01051       return -1;
01052    }
01053 
01054    xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
01055    if (!xferchan) {
01056       ast_hangup(newchan);
01057       return -1;
01058    }
01059    /* Make formats okay */
01060    xferchan->visible_indication = transferer->visible_indication;
01061    xferchan->readformat = transferee->readformat;
01062    xferchan->writeformat = transferee->writeformat;
01063    ast_channel_masquerade(xferchan, transferee);
01064    ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
01065    xferchan->_state = AST_STATE_UP;
01066    ast_clear_flag(xferchan, AST_FLAGS_ALL);  
01067    xferchan->_softhangup = 0;
01068 
01069    if ((f = ast_read(xferchan)))
01070       ast_frfree(f);
01071 
01072    newchan->_state = AST_STATE_UP;
01073    ast_clear_flag(newchan, AST_FLAGS_ALL);   
01074    newchan->_softhangup = 0;
01075 
01076    tobj = ast_calloc(1, sizeof(struct ast_bridge_thread_obj));
01077    if (!tobj) {
01078       ast_hangup(xferchan);
01079       ast_hangup(newchan);
01080       return -1;
01081    }
01082 
01083    ast_channel_lock(newchan);
01084    if ((features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL))) {
01085       dialfeatures = features_datastore->data;
01086    }
01087    ast_channel_unlock(newchan);
01088 
01089    if (dialfeatures) {
01090       /* newchan should always be the callee and shows up as callee in dialfeatures, but for some reason
01091          I don't currently understand, the abilities of newchan seem to be stored on the caller side */
01092       ast_copy_flags(&(config->features_callee), &(dialfeatures->features_caller), AST_FLAGS_ALL);
01093    }
01094 
01095    ast_channel_lock(xferchan);
01096    if ((features_datastore = ast_channel_datastore_find(xferchan, &dial_features_info, NULL))) {
01097       dialfeatures = features_datastore->data;
01098    }
01099    ast_channel_unlock(xferchan);
01100 
01101    if (dialfeatures) {
01102       ast_copy_flags(&(config->features_caller), &(dialfeatures->features_caller), AST_FLAGS_ALL);
01103    }
01104 
01105    tobj->chan = newchan;
01106    tobj->peer = xferchan;
01107    tobj->bconfig = *config;
01108 
01109    if (tobj->bconfig.end_bridge_callback_data_fixup) {
01110       tobj->bconfig.end_bridge_callback_data_fixup(&tobj->bconfig, tobj->peer, tobj->chan);
01111    }
01112 
01113    if (ast_stream_and_wait(newchan, xfersound, newchan->language, ""))
01114       ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
01115    ast_bridge_call_thread_launch(tobj);
01116    return -1;  /* XXX meaning the channel is bridged ? */
01117 }
01118 
01119 
01120 /*!
01121  * \brief Attended transfer ()
01122  * \param chan
01123  * \param peer
01124  * \param config
01125  * \param code
01126  * \param sense
01127  * \param data
01128  * Get extension to transfer to, if you cannot generate channel (or find extension)
01129  * return to host channel. After called channel answered wait for hangup of transferer,
01130  * bridge call between transfer peer (taking them off hold) to attended transfer channel.
01131  * \return -1 means what failure/success both?
01132 */
01133 static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
01134 {
01135    return do_atxfer(chan, peer, config, sense, NULL, NULL);
01136 }
01137 
01138 /* add atxfer and automon as undefined so you can only use em if you configure them */
01139 #define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
01140 
01141 AST_RWLOCK_DEFINE_STATIC(features_lock);
01142 
01143 static struct ast_call_feature builtin_features[] = 
01144  {
01145    { AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF, "" },
01146    { AST_FEATURE_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF, "" },
01147    { AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF, "" },
01148    { AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF, "" },
01149    { AST_FEATURE_PARKCALL, "Park Call", "parkcall", "", "", builtin_parkcall, AST_FEATURE_FLAG_NEEDSDTMF, "" },
01150 };
01151 
01152 
01153 static AST_RWLIST_HEAD_STATIC(feature_list, ast_call_feature);
01154 
01155 /*! \brief register new feature into feature_list*/
01156 void ast_register_feature(struct ast_call_feature *feature)
01157 {
01158    if (!feature) {
01159       ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
01160          return;
01161    }
01162   
01163    AST_RWLIST_WRLOCK(&feature_list);
01164    AST_RWLIST_INSERT_HEAD(&feature_list, feature, feature_entry);
01165    AST_RWLIST_UNLOCK(&feature_list);
01166 
01167    if (option_verbose >= 2) {
01168       ast_verbose(VERBOSE_PREFIX_2 "Registered Feature '%s'\n",feature->sname);
01169    }
01170 }
01171 
01172 /*! \brief unregister feature from feature_list */
01173 void ast_unregister_feature(struct ast_call_feature *feature)
01174 {
01175    if (!feature)
01176       return;
01177 
01178    AST_RWLIST_WRLOCK(&feature_list);
01179    AST_RWLIST_REMOVE(&feature_list, feature, feature_entry);
01180    AST_RWLIST_UNLOCK(&feature_list);
01181    
01182    free(feature);
01183 }
01184 
01185 /*! \brief Remove all features in the list */
01186 static void ast_unregister_features(void)
01187 {
01188    struct ast_call_feature *feature;
01189 
01190    AST_RWLIST_WRLOCK(&feature_list);
01191    while ((feature = AST_LIST_REMOVE_HEAD(&feature_list, feature_entry))) {
01192       free(feature);
01193    }
01194    AST_RWLIST_UNLOCK(&feature_list);
01195 }
01196 
01197 /*! \brief find a feature by name */
01198 static struct ast_call_feature *find_dynamic_feature(const char *name)
01199 {
01200    struct ast_call_feature *tmp;
01201 
01202    AST_RWLIST_TRAVERSE(&feature_list, tmp, feature_entry) {
01203       if (!strcasecmp(tmp->sname, name)) {
01204          break;
01205       }
01206    }
01207 
01208    return tmp;
01209 }
01210 
01211 /*! \brief exec an app by feature */
01212 static int feature_exec_app(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
01213 {
01214    struct ast_app *app;
01215    struct ast_call_feature *feature = data;
01216    struct ast_channel *work, *idle;
01217    int res;
01218 
01219    if (!feature) { /* shouldn't ever happen! */
01220       ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
01221       return -1; 
01222    }
01223 
01224    if (sense == FEATURE_SENSE_CHAN) {
01225       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
01226          return FEATURE_RETURN_KEEPTRYING;
01227       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
01228          work = chan;
01229          idle = peer;
01230       } else {
01231          work = peer;
01232          idle = chan;
01233       }
01234    } else {
01235       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
01236          return FEATURE_RETURN_KEEPTRYING;
01237       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
01238          work = peer;
01239          idle = chan;
01240       } else {
01241          work = chan;
01242          idle = peer;
01243       }
01244    }
01245 
01246    if (!(app = pbx_findapp(feature->app))) {
01247       ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
01248       return -2;
01249    }
01250 
01251    ast_autoservice_start(idle);
01252    
01253    if (!ast_strlen_zero(feature->moh_class))
01254       ast_moh_start(idle, feature->moh_class, NULL);
01255 
01256    res = pbx_exec(work, app, feature->app_args);
01257 
01258    if (!ast_strlen_zero(feature->moh_class))
01259       ast_moh_stop(idle);
01260 
01261    ast_autoservice_stop(idle);
01262 
01263    if (res)
01264       return FEATURE_RETURN_SUCCESSBREAK;
01265    
01266    return FEATURE_RETURN_SUCCESS;   /*! \todo XXX should probably return res */
01267 }
01268 
01269 static void unmap_features(void)
01270 {
01271    int x;
01272 
01273    ast_rwlock_wrlock(&features_lock);
01274    for (x = 0; x < FEATURES_COUNT; x++)
01275       strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
01276    ast_rwlock_unlock(&features_lock);
01277 }
01278 
01279 static int remap_feature(const char *name, const char *value)
01280 {
01281    int x, res = -1;
01282 
01283    ast_rwlock_wrlock(&features_lock);
01284    for (x = 0; x < FEATURES_COUNT; x++) {
01285       if (strcasecmp(builtin_features[x].sname, name))
01286          continue;
01287 
01288       ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten));
01289       res = 0;
01290       break;
01291    }
01292    ast_rwlock_unlock(&features_lock);
01293 
01294    return res;
01295 }
01296 
01297 /*!
01298  * \brief Helper function for feature_interpret and ast_feature_detect
01299  * \param chan,peer,config,code,sense,dynamic_features char buf,feature flags,operation,feature
01300  *
01301  * Lock features list, browse for code, unlock list
01302  * If a feature is found and the operation variable is set, that feature's
01303  * operation is executed.  The first feature found is copied to the feature parameter.
01304  * \retval res on success.
01305  * \retval -1 on failure.
01306 */
01307 static int feature_interpret_helper(struct ast_channel *chan, struct ast_channel *peer,
01308    struct ast_bridge_config *config, char *code, int sense, char *dynamic_features_buf,
01309    struct ast_flags *features, int operation, struct ast_call_feature *feature)
01310 {
01311    int x;
01312    struct ast_call_feature *tmpfeature;
01313    char *tmp, *tok;
01314    int res = FEATURE_RETURN_PASSDIGITS;
01315    int feature_detected = 0;
01316 
01317    if (!(peer && chan && config) && operation) {
01318       return -1; /* can not run feature operation */
01319    }
01320 
01321    ast_rwlock_rdlock(&features_lock);
01322    for (x = 0; x < FEATURES_COUNT; x++) {
01323       if ((ast_test_flag(features, builtin_features[x].feature_mask)) &&
01324           !ast_strlen_zero(builtin_features[x].exten)) {
01325          /* Feature is up for consideration */
01326          if (!strcmp(builtin_features[x].exten, code)) {
01327             if (option_debug > 2) {
01328                ast_log(LOG_DEBUG, "Feature detected: fname=%s sname=%s exten=%s\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten);
01329             }
01330             if (operation) {
01331                res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
01332             }
01333             memcpy(feature, &builtin_features[x], sizeof(feature));
01334             feature_detected = 1;
01335             break;
01336          } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
01337             if (res == FEATURE_RETURN_PASSDIGITS)
01338                res = FEATURE_RETURN_STOREDIGITS;
01339          }
01340       }
01341    }
01342    ast_rwlock_unlock(&features_lock);
01343 
01344    if (ast_strlen_zero(dynamic_features_buf) || feature_detected) {
01345       return res;
01346    }
01347 
01348    tmp = dynamic_features_buf;
01349 
01350    while ((tok = strsep(&tmp, "#"))) {
01351       AST_RWLIST_RDLOCK(&feature_list);
01352       if (!(tmpfeature = find_dynamic_feature(tok))) {
01353          AST_RWLIST_UNLOCK(&feature_list);
01354          continue;
01355       }
01356 
01357       /* Feature is up for consideration */
01358       if (!strcmp(tmpfeature->exten, code)) {
01359          if (option_debug > 2) {
01360             ast_log(LOG_NOTICE, " Feature Found: %s exten: %s\n",tmpfeature->sname, tok);
01361          }
01362          if (operation) {
01363             res = tmpfeature->operation(chan, peer, config, code, sense, tmpfeature);
01364          }
01365          memcpy(feature, tmpfeature, sizeof(feature));
01366          if (res != FEATURE_RETURN_KEEPTRYING) {
01367             AST_RWLIST_UNLOCK(&feature_list);
01368             break;
01369          }
01370          res = FEATURE_RETURN_PASSDIGITS;
01371       } else if (!strncmp(tmpfeature->exten, code, strlen(code)))
01372          res = FEATURE_RETURN_STOREDIGITS;
01373 
01374       AST_RWLIST_UNLOCK(&feature_list);
01375    }
01376 
01377    return res;
01378 }
01379 
01380 /*!
01381  * \brief Check the dynamic features
01382  * \param chan,peer,config,code,sense
01383  *
01384  * \retval res on success.
01385  * \retval -1 on failure.
01386 */
01387 
01388 static int feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense) {
01389 
01390    char dynamic_features_buf[128];
01391    const char *peer_dynamic_features, *chan_dynamic_features;
01392    struct ast_flags features;
01393    struct ast_call_feature feature;
01394    if (sense == FEATURE_SENSE_CHAN) {
01395       ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
01396    }
01397    else {
01398       ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);
01399    }
01400 
01401    ast_channel_lock(peer);
01402    peer_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES"),""));
01403    ast_channel_unlock(peer);
01404 
01405    ast_channel_lock(chan);
01406    chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
01407    ast_channel_unlock(chan);
01408 
01409    snprintf(dynamic_features_buf, sizeof(dynamic_features_buf), "%s%s%s", S_OR(chan_dynamic_features, ""), chan_dynamic_features && peer_dynamic_features ? "#" : "", S_OR(peer_dynamic_features,""));
01410 
01411    if (option_debug > 2) {
01412       ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d, dynamic=%s\n", chan->name, peer->name, code, sense, features.flags, dynamic_features_buf);
01413    }
01414 
01415    return feature_interpret_helper(chan, peer, config, code, sense, dynamic_features_buf, &features, 1, &feature);
01416 }
01417 
01418 
01419 int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, char *code, struct ast_call_feature *feature) {
01420 
01421    return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, 0, feature);
01422 }
01423 
01424 static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
01425 {
01426    int x;
01427 
01428    ast_clear_flag(config, AST_FLAGS_ALL);
01429 
01430    ast_rwlock_rdlock(&features_lock);
01431    for (x = 0; x < FEATURES_COUNT; x++) {
01432       if (!ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
01433          continue;
01434 
01435       if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask))
01436          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
01437 
01438       if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask))
01439          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
01440    }
01441    ast_rwlock_unlock(&features_lock);
01442 
01443    if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) {
01444       const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
01445 
01446       if (dynamic_features) {
01447          char *tmp = ast_strdupa(dynamic_features);
01448          char *tok;
01449          struct ast_call_feature *feature;
01450 
01451          /* while we have a feature */
01452          while ((tok = strsep(&tmp, "#"))) {
01453             AST_RWLIST_RDLOCK(&feature_list);
01454             if ((feature = find_dynamic_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
01455                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
01456                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
01457                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
01458                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
01459             }
01460             AST_RWLIST_UNLOCK(&feature_list);
01461          }
01462       }
01463    }
01464 }
01465 
01466 /*! \todo XXX Check - this is very similar to the code in channel.c */
01467 static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *caller, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, const char *language)
01468 {
01469    int state = 0;
01470    int cause = 0;
01471    int to;
01472    struct ast_channel *chan;
01473    struct ast_channel *monitor_chans[2];
01474    struct ast_channel *active_channel;
01475    int res = 0, ready = 0;
01476    
01477    if ((chan = ast_request(type, format, data, &cause))) {
01478       ast_set_callerid(chan, cid_num, cid_name, cid_num);
01479       ast_string_field_set(chan, language, language);
01480       ast_channel_inherit_variables(caller, chan); 
01481       pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", caller->name);
01482          
01483       if (!ast_call(chan, data, timeout)) {
01484          struct timeval started;
01485          int x, len = 0;
01486          char *disconnect_code = NULL, *dialed_code = NULL;
01487 
01488          ast_indicate(caller, AST_CONTROL_RINGING);
01489          /* support dialing of the featuremap disconnect code while performing an attended tranfer */
01490          ast_rwlock_rdlock(&features_lock);
01491          for (x = 0; x < FEATURES_COUNT; x++) {
01492             if (strcasecmp(builtin_features[x].sname, "disconnect"))
01493                continue;
01494 
01495             disconnect_code = builtin_features[x].exten;
01496             len = strlen(disconnect_code) + 1;
01497             dialed_code = alloca(len);
01498             memset(dialed_code, 0, len);
01499             break;
01500          }
01501          ast_rwlock_unlock(&features_lock);
01502          x = 0;
01503          started = ast_tvnow();
01504          to = timeout;
01505          while (!ast_check_hangup(caller) && timeout && (chan->_state != AST_STATE_UP)) {
01506             struct ast_frame *f = NULL;
01507 
01508             monitor_chans[0] = caller;
01509             monitor_chans[1] = chan;
01510             active_channel = ast_waitfor_n(monitor_chans, 2, &to);
01511 
01512             /* see if the timeout has been violated */
01513             if(ast_tvdiff_ms(ast_tvnow(), started) > timeout) {
01514                state = AST_CONTROL_UNHOLD;
01515                ast_log(LOG_NOTICE, "We exceeded our AT-timeout\n");
01516                break; /*doh! timeout*/
01517             }
01518 
01519             if (!active_channel)
01520                continue;
01521 
01522             if (chan && (chan == active_channel)) {
01523                if (!ast_strlen_zero(chan->call_forward)) {
01524                   if (!(chan = ast_call_forward(caller, chan, &to, format, NULL, outstate))) {
01525                      return NULL;
01526                   }
01527                   continue;
01528                }
01529                f = ast_read(chan);
01530                if (f == NULL) { /*doh! where'd he go?*/
01531                   state = AST_CONTROL_HANGUP;
01532                   res = 0;
01533                   break;
01534                }
01535                
01536                if (f->frametype == AST_FRAME_CONTROL || f->frametype == AST_FRAME_DTMF || f->frametype == AST_FRAME_TEXT) {
01537                   if (f->subclass == AST_CONTROL_RINGING) {
01538                      state = f->subclass;
01539                      if (option_verbose > 2)
01540                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", chan->name);
01541                      ast_indicate(caller, AST_CONTROL_RINGING);
01542                   } else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) {
01543                      state = f->subclass;
01544                      if (option_verbose > 2)
01545                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", chan->name);
01546                      ast_indicate(caller, AST_CONTROL_BUSY);
01547                      ast_frfree(f);
01548                      f = NULL;
01549                      break;
01550                   } else if (f->subclass == AST_CONTROL_ANSWER) {
01551                      /* This is what we are hoping for */
01552                      state = f->subclass;
01553                      ast_frfree(f);
01554                      f = NULL;
01555                      ready=1;
01556                      break;
01557                   } else if (f->subclass != -1 && f->subclass != AST_CONTROL_PROGRESS) {
01558                      ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass);
01559                   }
01560                   /* else who cares */
01561                } else if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
01562                   ast_write(caller, f);
01563                }
01564 
01565             } else if (caller && (active_channel == caller)) {
01566                f = ast_read(caller);
01567                if (f == NULL) { /*doh! where'd he go?*/
01568                   if (caller->_softhangup && !chan->_softhangup) {
01569                      /* make this a blind transfer */
01570                      ready = 1;
01571                      break;
01572                   }
01573                   state = AST_CONTROL_HANGUP;
01574                   res = 0;
01575                   break;
01576                }
01577                
01578                if (f->frametype == AST_FRAME_DTMF) {
01579                   dialed_code[x++] = f->subclass;
01580                   dialed_code[x] = '\0';
01581                   if (strlen(dialed_code) == len) {
01582                      x = 0;
01583                   } else if (x && strncmp(dialed_code, disconnect_code, x)) {
01584                      x = 0;
01585                      dialed_code[x] = '\0';
01586                   }
01587                   if (*dialed_code && !strcmp(dialed_code, disconnect_code)) {
01588                      /* Caller Canceled the call */
01589                      state = AST_CONTROL_UNHOLD;
01590                      ast_frfree(f);
01591                      f = NULL;
01592                      break;
01593                   }
01594                } else if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
01595                   ast_write(chan, f);
01596                }
01597             }
01598             if (f)
01599                ast_frfree(f);
01600          } /* end while */
01601       } else
01602          ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
01603    } else {
01604       ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
01605       switch(cause) {
01606       case AST_CAUSE_BUSY:
01607          state = AST_CONTROL_BUSY;
01608          break;
01609       case AST_CAUSE_CONGESTION:
01610          state = AST_CONTROL_CONGESTION;
01611          break;
01612       }
01613    }
01614    
01615    ast_indicate(caller, -1);
01616    if (chan && ready) {
01617       if (chan->_state == AST_STATE_UP) 
01618          state = AST_CONTROL_ANSWER;
01619       res = 0;
01620    } else if(chan) {
01621       res = -1;
01622       ast_hangup(chan);
01623       chan = NULL;
01624    } else {
01625       res = -1;
01626    }
01627    
01628    if (outstate)
01629       *outstate = state;
01630 
01631    return chan;
01632 }
01633 
01634 static struct ast_cdr *pick_unlocked_cdr(struct ast_cdr *cdr)
01635 {
01636    struct ast_cdr *cdr_orig = cdr;
01637    while (cdr) {
01638       if (!ast_test_flag(cdr,AST_CDR_FLAG_LOCKED))
01639          return cdr;
01640       cdr = cdr->next;
01641    }
01642    return cdr_orig; /* everybody LOCKED or some other weirdness, like a NULL */
01643 }
01644 
01645 static void set_bridge_features_on_config(struct ast_bridge_config *config, const char *features)
01646 {
01647    const char *feature;
01648 
01649    if (ast_strlen_zero(features)) {
01650       return;
01651    }
01652 
01653    for (feature = features; *feature; feature++) {
01654       switch (*feature) {
01655       case 'T' :
01656       case 't' :
01657          ast_set_flag(&(config->features_caller), AST_FEATURE_REDIRECT);
01658          break;
01659       case 'K' :
01660       case 'k' :
01661          ast_set_flag(&(config->features_caller), AST_FEATURE_PARKCALL);
01662          break;
01663       case 'H' :
01664       case 'h' :
01665          ast_set_flag(&(config->features_caller), AST_FEATURE_DISCONNECT);
01666          break;
01667       case 'W' :
01668       case 'w' :
01669          ast_set_flag(&(config->features_caller), AST_FEATURE_AUTOMON);
01670          break;
01671       default :
01672          ast_log(LOG_WARNING, "Skipping unknown feature code '%c'\n", *feature);
01673       }
01674    }
01675 }
01676 
01677 static void add_features_datastores(struct ast_channel *caller, struct ast_channel *callee, struct ast_bridge_config *config)
01678 {
01679    struct ast_datastore *ds_callee_features = NULL, *ds_caller_features = NULL;
01680    struct ast_dial_features *callee_features = NULL, *caller_features = NULL;
01681 
01682    ast_channel_lock(caller);
01683    ds_caller_features = ast_channel_datastore_find(caller, &dial_features_info, NULL);
01684    ast_channel_unlock(caller);
01685    if (!ds_caller_features) {
01686       if (!(ds_caller_features = ast_channel_datastore_alloc(&dial_features_info, NULL))) {
01687          ast_log(LOG_WARNING, "Unable to create channel datastore for caller features. Aborting!\n");
01688          return;
01689       }
01690       if (!(caller_features = ast_calloc(1, sizeof(*caller_features)))) {
01691          ast_log(LOG_WARNING, "Unable to allocate memory for callee feature flags. Aborting!\n");
01692          ast_channel_datastore_free(ds_caller_features);
01693          return;
01694       }
01695       ds_caller_features->inheritance = DATASTORE_INHERIT_FOREVER;
01696       caller_features->is_caller = 1;
01697       ast_copy_flags(&(caller_features->features_callee), &(config->features_callee), AST_FLAGS_ALL);
01698       ast_copy_flags(&(caller_features->features_caller), &(config->features_caller), AST_FLAGS_ALL);
01699       ds_caller_features->data = caller_features;
01700       ast_channel_lock(caller);
01701       ast_channel_datastore_add(caller, ds_caller_features);
01702       ast_channel_unlock(caller);
01703    } else {
01704       /* If we don't return here, then when we do a builtin_atxfer we will copy the disconnect
01705        * flags over from the atxfer to the caller */
01706       return;
01707    }
01708 
01709    ast_channel_lock(callee);
01710    ds_callee_features = ast_channel_datastore_find(callee, &dial_features_info, NULL);
01711    ast_channel_unlock(callee);
01712    if (!ds_callee_features) {
01713       if (!(ds_callee_features = ast_channel_datastore_alloc(&dial_features_info, NULL))) {
01714          ast_log(LOG_WARNING, "Unable to create channel datastore for callee features. Aborting!\n");
01715          return;
01716       }
01717       if (!(callee_features = ast_calloc(1, sizeof(*callee_features)))) {
01718          ast_log(LOG_WARNING, "Unable to allocate memory for callee feature flags. Aborting!\n");
01719          ast_channel_datastore_free(ds_callee_features);
01720          return;
01721       }
01722       ds_callee_features->inheritance = DATASTORE_INHERIT_FOREVER;
01723       callee_features->is_caller = 0;
01724       ast_copy_flags(&(callee_features->features_callee), &(config->features_caller), AST_FLAGS_ALL);
01725       ast_copy_flags(&(callee_features->features_caller), &(config->features_callee), AST_FLAGS_ALL);
01726       ds_callee_features->data = callee_features;
01727       ast_channel_lock(callee);
01728       ast_channel_datastore_add(callee, ds_callee_features);
01729       ast_channel_unlock(callee);
01730    }
01731 
01732    return;
01733 }
01734 
01735 static void cmd_atxfer(struct ast_channel *a, struct ast_channel *b, struct ast_bridge_config *conf, struct ast_channel *who, char *xferto)
01736 {
01737    int sense = (a == who) ? FEATURE_SENSE_CHAN : FEATURE_SENSE_PEER;
01738    char *context = strchr(xferto, '@');;
01739    if (context)
01740       *context++ = '\0';
01741    do_atxfer(a, b, conf, sense, xferto, context);
01742 }
01743 
01744 int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
01745 {
01746    /* Copy voice back and forth between the two channels.  Give the peer
01747       the ability to transfer calls with '#<extension' syntax. */
01748    struct ast_frame *f;
01749    struct ast_channel *who;
01750    char chan_featurecode[FEATURE_MAX_LEN + 1]="";
01751    char peer_featurecode[FEATURE_MAX_LEN + 1]="";
01752    char orig_channame[AST_MAX_EXTENSION];
01753    char orig_peername[AST_MAX_EXTENSION];
01754 
01755    int res;
01756    int diff;
01757    int hasfeatures=0;
01758    int hadfeatures=0;
01759    int autoloopflag;
01760    struct ast_option_header *aoh;
01761    struct ast_bridge_config backup_config;
01762    struct ast_cdr *bridge_cdr = NULL;
01763    struct ast_cdr *orig_peer_cdr = NULL;
01764    struct ast_cdr *chan_cdr = chan->cdr; /* the proper chan cdr, if there are forked cdrs */
01765    struct ast_cdr *peer_cdr = peer->cdr; /* the proper chan cdr, if there are forked cdrs */
01766    struct ast_cdr *new_chan_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
01767    struct ast_cdr *new_peer_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
01768 
01769    memset(&backup_config, 0, sizeof(backup_config));
01770 
01771    config->start_time = ast_tvnow();
01772 
01773    if (chan && peer) {
01774       pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
01775       pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
01776    } else if (chan) {
01777       pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
01778    }
01779 
01780    set_bridge_features_on_config(config, pbx_builtin_getvar_helper(chan, "BRIDGE_FEATURES"));
01781    add_features_datastores(chan, peer, config);
01782 
01783    /* This is an interesting case.  One example is if a ringing channel gets redirected to
01784     * an extension that picks up a parked call.  This will make sure that the call taken
01785     * out of parking gets told that the channel it just got bridged to is still ringing. */
01786    if (chan->_state == AST_STATE_RINGING && peer->visible_indication != AST_CONTROL_RINGING) {
01787       ast_indicate(peer, AST_CONTROL_RINGING);
01788    }
01789 
01790    if (monitor_ok) {
01791       const char *monitor_exec;
01792       struct ast_channel *src = NULL;
01793       if (!monitor_app) { 
01794          if (!(monitor_app = pbx_findapp("Monitor")))
01795             monitor_ok=0;
01796       }
01797       if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR"))) 
01798          src = chan;
01799       else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
01800          src = peer;
01801       if (monitor_app && src) {
01802          char *tmp = ast_strdupa(monitor_exec);
01803          pbx_exec(src, monitor_app, tmp);
01804       }
01805    }
01806 
01807    set_config_flags(chan, peer, config);
01808    config->firstpass = 1;
01809 
01810    /* Answer if need be */
01811    if (ast_answer(chan))
01812       return -1;
01813 
01814    ast_copy_string(orig_channame,chan->name,sizeof(orig_channame));
01815    ast_copy_string(orig_peername,peer->name,sizeof(orig_peername));
01816    orig_peer_cdr = peer_cdr;
01817    
01818    if (!chan_cdr || (chan_cdr && !ast_test_flag(chan_cdr, AST_CDR_FLAG_POST_DISABLED))) {
01819          
01820       if (chan_cdr) {
01821          ast_set_flag(chan_cdr, AST_CDR_FLAG_MAIN);
01822          ast_cdr_update(chan);
01823          bridge_cdr = ast_cdr_dup(chan_cdr);
01824          /* rip any forked CDR's off of the chan_cdr and attach
01825           * them to the bridge_cdr instead */
01826          bridge_cdr->next = chan_cdr->next;
01827          chan_cdr->next = NULL;
01828          ast_copy_string(bridge_cdr->lastapp, S_OR(chan->appl, ""), sizeof(bridge_cdr->lastapp));
01829          ast_copy_string(bridge_cdr->lastdata, S_OR(chan->data, ""), sizeof(bridge_cdr->lastdata));
01830          if (peer_cdr && !ast_strlen_zero(peer_cdr->userfield)) {
01831             ast_copy_string(bridge_cdr->userfield, peer_cdr->userfield, sizeof(bridge_cdr->userfield));
01832          }
01833       } else {
01834          /* better yet, in a xfer situation, find out why the chan cdr got zapped (pun unintentional) */
01835          bridge_cdr = ast_cdr_alloc(); /* this should be really, really rare/impossible? */
01836          ast_copy_string(bridge_cdr->channel, chan->name, sizeof(bridge_cdr->channel));
01837          ast_copy_string(bridge_cdr->dstchannel, peer->name, sizeof(bridge_cdr->dstchannel));
01838          ast_copy_string(bridge_cdr->uniqueid, chan->uniqueid, sizeof(bridge_cdr->uniqueid));
01839          ast_copy_string(bridge_cdr->lastapp, S_OR(chan->appl, ""), sizeof(bridge_cdr->lastapp));
01840          ast_copy_string(bridge_cdr->lastdata, S_OR(chan->data, ""), sizeof(bridge_cdr->lastdata));
01841          ast_cdr_setcid(bridge_cdr, chan);
01842          bridge_cdr->disposition = (chan->_state == AST_STATE_UP) ?  AST_CDR_ANSWERED : AST_CDR_NOANSWER;
01843          bridge_cdr->amaflags = chan->amaflags ? chan->amaflags :  ast_default_amaflags;
01844          ast_copy_string(bridge_cdr->accountcode, chan->accountcode, sizeof(bridge_cdr->accountcode));
01845          /* Destination information */
01846          ast_copy_string(bridge_cdr->dst, chan->exten, sizeof(bridge_cdr->dst));
01847          ast_copy_string(bridge_cdr->dcontext, chan->context, sizeof(bridge_cdr->dcontext));
01848          if (peer_cdr) {
01849             bridge_cdr->start = peer_cdr->start;
01850             ast_copy_string(bridge_cdr->userfield, peer_cdr->userfield, sizeof(bridge_cdr->userfield));
01851          } else {
01852             ast_cdr_start(bridge_cdr);
01853          }
01854       }
01855       /* peer_cdr->answer will be set when a macro runs on the peer;
01856          in that case, the bridge answer will be delayed while the
01857          macro plays on the peer channel. The peer answered the call
01858          before the macro started playing. To the phone system,
01859          this is billable time for the call, even tho the caller
01860          hears nothing but ringing while the macro does its thing. */
01861 
01862       /* Another case where the peer cdr's time will be set, is when
01863          A self-parks by pickup up phone and dialing 700, then B
01864          picks up A by dialing its parking slot; there may be more 
01865          practical paths that get the same result, tho... in which
01866          case you get the previous answer time from the Park... which
01867          is before the bridge's start time, so I added in the 
01868          tvcmp check to the if below */
01869 
01870       if (peer_cdr && !ast_tvzero(peer_cdr->answer) && ast_tvcmp(peer_cdr->answer, bridge_cdr->start) >= 0) {
01871          ast_cdr_setanswer(bridge_cdr, peer_cdr->answer);
01872          ast_cdr_setdisposition(bridge_cdr, peer_cdr->disposition);
01873          if (chan_cdr) {
01874             ast_cdr_setanswer(chan_cdr, peer_cdr->answer);
01875             ast_cdr_setdisposition(chan_cdr, peer_cdr->disposition);
01876          }
01877       } else {
01878          ast_cdr_answer(bridge_cdr);
01879          if (chan_cdr) {
01880             ast_cdr_answer(chan_cdr); /* for the sake of cli status checks */
01881          }
01882       }
01883       if (ast_test_flag(chan,AST_FLAG_BRIDGE_HANGUP_DONT) && (chan_cdr || peer_cdr)) {
01884          if (chan_cdr) {
01885             ast_set_flag(chan_cdr, AST_CDR_FLAG_BRIDGED);
01886          }
01887          if (peer_cdr) {
01888             ast_set_flag(peer_cdr, AST_CDR_FLAG_BRIDGED);
01889          }
01890       }
01891    }
01892 
01893    for (;;) {
01894       struct ast_channel *other; /* used later */
01895 
01896       res = ast_channel_bridge(chan, peer, config, &f, &who);
01897       
01898       /* When frame is not set, we are probably involved in a situation
01899          where we've timed out.
01900          When frame is set, we'll come thru this code twice; once for DTMF_BEGIN
01901          and also for DTMF_END. If we flow into the following 'if' for both, then 
01902          our wait times are cut in half, as both will subtract from the
01903          feature_timer. Not good!
01904       */
01905       if (config->feature_timer && (!f || f->frametype == AST_FRAME_DTMF_END)) {
01906          /* Update time limit for next pass */
01907          diff = ast_tvdiff_ms(ast_tvnow(), config->start_time);
01908          if (res == AST_BRIDGE_RETRY) {
01909             /* The feature fully timed out but has not been updated. Skip
01910              * the potential round error from the diff calculation and
01911              * explicitly set to expired. */
01912             config->feature_timer = -1;
01913          } else {
01914             config->feature_timer -= diff;
01915          }
01916 
01917          if (hasfeatures) {
01918             /* Running on backup config, meaning a feature might be being
01919                activated, but that's no excuse to keep things going 
01920                indefinitely! */
01921             if (backup_config.feature_timer && ((backup_config.feature_timer -= diff) <= 0)) {
01922                if (option_debug)
01923                   ast_log(LOG_DEBUG, "Timed out, realtime this time!\n");
01924                config->feature_timer = 0;
01925                who = chan;
01926                if (f)
01927                   ast_frfree(f);
01928                f = NULL;
01929                res = 0;
01930             } else if (config->feature_timer <= 0) {
01931                /* Not *really* out of time, just out of time for
01932                   digits to come in for features. */
01933                if (option_debug)
01934                   ast_log(LOG_DEBUG, "Timed out for feature!\n");
01935                if (!ast_strlen_zero(peer_featurecode)) {
01936                   ast_dtmf_stream(chan, peer, peer_featurecode, 0);
01937                   memset(peer_featurecode, 0, sizeof(peer_featurecode));
01938                }
01939                if (!ast_strlen_zero(chan_featurecode)) {
01940                   ast_dtmf_stream(peer, chan, chan_featurecode, 0);
01941                   memset(chan_featurecode, 0, sizeof(chan_featurecode));
01942                }
01943                if (f)
01944                   ast_frfree(f);
01945                hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
01946                if (!hasfeatures) {
01947                   /* Restore original (possibly time modified) bridge config */
01948                   memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
01949                   memset(&backup_config, 0, sizeof(backup_config));
01950                }
01951                hadfeatures = hasfeatures;
01952                /* Continue as we were */
01953                continue;
01954             } else if (!f) {
01955                /* The bridge returned without a frame and there is a feature in progress.
01956                 * However, we don't think the feature has quite yet timed out, so just
01957                 * go back into the bridge. */
01958                continue;
01959             }
01960          } else {
01961             if (config->feature_timer <=0) {
01962                /* We ran out of time */
01963                config->feature_timer = 0;
01964                who = chan;
01965                if (f)
01966                   ast_frfree(f);
01967                f = NULL;
01968                res = 0;
01969             }
01970          }
01971       }
01972       if (res < 0) {
01973          if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_test_flag(peer, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer))
01974             ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
01975          goto before_you_go;
01976       }
01977       
01978       if (!f || (f->frametype == AST_FRAME_CONTROL &&
01979             (f->subclass == AST_CONTROL_HANGUP || f->subclass == AST_CONTROL_BUSY || 
01980                f->subclass == AST_CONTROL_CONGESTION ) ) ) {
01981          res = -1;
01982          break;
01983       }
01984       /* many things should be sent to the 'other' channel */
01985       other = (who == chan) ? peer : chan;
01986       if (f->frametype == AST_FRAME_CONTROL) {
01987          switch (f->subclass) {
01988          case AST_CONTROL_RINGING:
01989          case AST_CONTROL_FLASH:
01990          case -1:
01991             ast_indicate(other, f->subclass);
01992             break;
01993          case AST_CONTROL_HOLD:
01994          case AST_CONTROL_UNHOLD:
01995             ast_indicate_data(other, f->subclass, f->data, f->datalen);
01996             break;
01997          case AST_CONTROL_OPTION:
01998             aoh = f->data;
01999             /* Forward option Requests */
02000             if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
02001                ast_channel_setoption(other, ntohs(aoh->option), aoh->data, 
02002                   f->datalen - sizeof(struct ast_option_header), 0);
02003             }
02004             break;
02005          case AST_CONTROL_ATXFERCMD:
02006             cmd_atxfer(chan, peer, config, who, f->data);
02007             break;
02008          }
02009       } else if (f->frametype == AST_FRAME_DTMF_BEGIN) {
02010          /* eat it */
02011       } else if (f->frametype == AST_FRAME_DTMF) {
02012          char *featurecode;
02013          int sense;
02014 
02015          hadfeatures = hasfeatures;
02016          /* This cannot overrun because the longest feature is one shorter than our buffer */
02017          if (who == chan) {
02018             sense = FEATURE_SENSE_CHAN;
02019             featurecode = chan_featurecode;
02020          } else  {
02021             sense = FEATURE_SENSE_PEER;
02022             featurecode = peer_featurecode;
02023          }
02024          /*! append the event to featurecode. we rely on the string being zero-filled, and
02025           * not overflowing it. 
02026           * \todo XXX how do we guarantee the latter ?
02027           */
02028          featurecode[strlen(featurecode)] = f->subclass;
02029          /* Get rid of the frame before we start doing "stuff" with the channels */
02030          ast_frfree(f);
02031          f = NULL;
02032          config->feature_timer = backup_config.feature_timer;
02033          res = feature_interpret(chan, peer, config, featurecode, sense);
02034          switch(res) {
02035          case FEATURE_RETURN_PASSDIGITS:
02036             ast_dtmf_stream(other, who, featurecode, 0);
02037             /* Fall through */
02038          case FEATURE_RETURN_SUCCESS:
02039             memset(featurecode, 0, sizeof(chan_featurecode));
02040             break;
02041          }
02042          if (res >= FEATURE_RETURN_PASSDIGITS) {
02043             res = 0;
02044          } else 
02045             break;
02046          hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
02047          if (hadfeatures && !hasfeatures) {
02048             /* Restore backup */
02049             memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
02050             memset(&backup_config, 0, sizeof(struct ast_bridge_config));
02051          } else if (hasfeatures) {
02052             if (!hadfeatures) {
02053                /* Backup configuration */
02054                memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
02055                /* Setup temporary config options */
02056                config->play_warning = 0;
02057                ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
02058                ast_clear_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
02059                config->warning_freq = 0;
02060                config->warning_sound = NULL;
02061                config->end_sound = NULL;
02062                config->start_sound = NULL;
02063                config->firstpass = 0;
02064             }
02065             config->start_time = ast_tvnow();
02066             config->feature_timer = featuredigittimeout;
02067             if (option_debug)
02068                ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->feature_timer);
02069          }
02070       }
02071       if (f)
02072          ast_frfree(f);
02073 
02074    }
02075   before_you_go:
02076 
02077    if (ast_test_flag(chan,AST_FLAG_BRIDGE_HANGUP_DONT)) {
02078       ast_clear_flag(chan,AST_FLAG_BRIDGE_HANGUP_DONT); /* its job is done */
02079       if (bridge_cdr) {
02080          ast_cdr_discard(bridge_cdr);
02081          /* QUESTION: should we copy bridge_cdr fields to the peer before we throw it away? */
02082       }
02083       return res; /* if we shouldn't do the h-exten, we shouldn't do the bridge cdr, either! */
02084    }
02085 
02086    if (config->end_bridge_callback) {
02087       config->end_bridge_callback(config->end_bridge_callback_data);
02088    }
02089 
02090    if (!ast_test_flag(&(config->features_caller),AST_FEATURE_NO_H_EXTEN) && 
02091        ast_exists_extension(chan, chan->context, "h", 1, chan->cid.cid_num)) {
02092       struct ast_cdr *swapper = NULL;
02093       char savelastapp[AST_MAX_EXTENSION];
02094       char savelastdata[AST_MAX_EXTENSION];
02095       char save_exten[AST_MAX_EXTENSION];
02096       int  save_prio, spawn_error = 0;
02097       
02098       autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
02099       ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
02100       if (bridge_cdr && ast_opt_end_cdr_before_h_exten) {
02101          ast_cdr_end(bridge_cdr);
02102       }
02103       /* swap the bridge cdr and the chan cdr for a moment, and let the endbridge
02104          dialplan code operate on it */
02105       ast_channel_lock(chan);
02106       if (bridge_cdr) {
02107          swapper = chan->cdr;
02108          ast_copy_string(savelastapp, bridge_cdr->lastapp, sizeof(bridge_cdr->lastapp));
02109          ast_copy_string(savelastdata, bridge_cdr->lastdata, sizeof(bridge_cdr->lastdata));
02110          chan->cdr = bridge_cdr;
02111       }
02112       ast_copy_string(save_exten, chan->exten, sizeof(save_exten));
02113       ast_copy_string(chan->exten, "h", sizeof(chan->exten));
02114       save_prio = chan->priority;
02115       chan->priority = 1;
02116       ast_channel_unlock(chan);
02117       while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
02118          if ((spawn_error = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
02119             /* Something bad happened, or a hangup has been requested. */
02120             if (option_debug)
02121                ast_log(LOG_DEBUG, "Spawn h extension (%s,%s,%d) exited non-zero on '%s'\n", chan->context, chan->exten, chan->priority, chan->name);
02122             if (option_verbose > 1)
02123                ast_verbose( VERBOSE_PREFIX_2 "Spawn h extension (%s, %s, %d) exited non-zero on '%s'\n", chan->context, chan->exten, chan->priority, chan->name);
02124             break;
02125          }
02126          chan->priority++;
02127       }
02128       /* swap it back */
02129       ast_channel_lock(chan);
02130       ast_copy_string(chan->exten, save_exten, sizeof(chan->exten));
02131       chan->priority = save_prio;
02132       if (bridge_cdr) {
02133          if (chan->cdr == bridge_cdr) {
02134             chan->cdr = swapper;
02135          } else {
02136             bridge_cdr = NULL;
02137          }
02138       }
02139       if (chan->priority != 1 || !spawn_error) {
02140          ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_RUN);
02141       }
02142       ast_channel_unlock(chan);
02143       /* protect the lastapp/lastdata against the effects of the hangup/dialplan code */
02144       if (bridge_cdr) {
02145          ast_copy_string(bridge_cdr->lastapp, savelastapp, sizeof(bridge_cdr->lastapp));
02146          ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata));
02147       }
02148       ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
02149    }
02150    
02151    /* obey the NoCDR() wishes. -- move the DISABLED flag to the bridge CDR if it was set on the channel during the bridge... */
02152    new_chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
02153    if (bridge_cdr && new_chan_cdr && ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED)) {
02154       ast_set_flag(bridge_cdr, AST_CDR_FLAG_POST_DISABLED);
02155    }
02156 
02157    /* we can post the bridge CDR at this point */
02158    if (bridge_cdr) {
02159       ast_cdr_end(bridge_cdr);
02160       ast_cdr_detach(bridge_cdr);
02161    }
02162    
02163    /* do a specialized reset on the beginning channel
02164       CDR's, if they still exist, so as not to mess up
02165       issues in future bridges;
02166       
02167       Here are the rules of the game:
02168       1. The chan and peer channel pointers will not change
02169          during the life of the bridge.
02170       2. But, in transfers, the channel names will change.
02171          between the time the bridge is started, and the
02172          time the channel ends. 
02173          Usually, when a channel changes names, it will
02174          also change CDR pointers.
02175       3. Usually, only one of the two channels (chan or peer)
02176          will change names.
02177       4. Usually, if a channel changes names during a bridge,
02178          it is because of a transfer. Usually, in these situations,
02179          it is normal to see 2 bridges running simultaneously, and
02180          it is not unusual to see the two channels that change
02181          swapped between bridges.
02182       5. After a bridge occurs, we have 2 or 3 channels' CDRs
02183          to attend to; if the chan or peer changed names,
02184          we have the before and after attached CDR's.
02185    */
02186    
02187    if (new_chan_cdr) {
02188       struct ast_channel *chan_ptr = NULL;
02189       
02190       if (strcasecmp(orig_channame, chan->name) != 0) { 
02191          /* old channel */
02192          chan_ptr = ast_get_channel_by_name_locked(orig_channame);
02193          if (chan_ptr) {
02194             if (!ast_bridged_channel(chan_ptr)) {
02195                struct ast_cdr *cur;
02196                for (cur = chan_ptr->cdr; cur; cur = cur->next) {
02197                   if (cur == chan_cdr) {
02198                      break;
02199                   }
02200                }
02201                if (cur)
02202                   ast_cdr_specialized_reset(chan_cdr,0);
02203             }
02204             ast_channel_unlock(chan_ptr);
02205          }
02206          /* new channel */
02207          ast_cdr_specialized_reset(new_chan_cdr,0);
02208       } else {
02209          ast_cdr_specialized_reset(chan_cdr,0); /* nothing changed, reset the chan_cdr  */
02210       }
02211    }
02212 
02213    {
02214       struct ast_channel *chan_ptr = NULL;
02215       new_peer_cdr = pick_unlocked_cdr(peer->cdr); /* the proper chan cdr, if there are forked cdrs */
02216       if (new_chan_cdr && ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED) && new_peer_cdr && !ast_test_flag(new_peer_cdr, AST_CDR_FLAG_POST_DISABLED))
02217          ast_set_flag(new_peer_cdr, AST_CDR_FLAG_POST_DISABLED); /* DISABLED is viral-- it will propagate across a bridge */
02218       if (strcasecmp(orig_peername, peer->name) != 0) { 
02219          /* old channel */
02220          chan_ptr = ast_get_channel_by_name_locked(orig_peername);
02221          if (chan_ptr) {
02222             if (!ast_bridged_channel(chan_ptr)) {
02223                struct ast_cdr *cur;
02224                for (cur = chan_ptr->cdr; cur; cur = cur->next) {
02225                   if (cur == peer_cdr) {
02226                      break;
02227                   }
02228                }
02229                if (cur)
02230                   ast_cdr_specialized_reset(peer_cdr,0);
02231             }
02232             ast_channel_unlock(chan_ptr);
02233          }
02234          /* new channel */
02235          ast_cdr_specialized_reset(new_peer_cdr,0);
02236       } else {
02237          ast_cdr_specialized_reset(peer_cdr,0); /* nothing changed, reset the peer_cdr  */
02238       }
02239    }
02240    
02241    return res;
02242 }
02243 
02244 static void post_manager_event(const char *s, char *parkingexten, struct ast_channel *chan)
02245 {
02246    manager_event(EVENT_FLAG_CALL, s,
02247       "Exten: %s\r\n"
02248       "Channel: %s\r\n"
02249       "CallerID: %s\r\n"
02250       "CallerIDName: %s\r\n\r\n",
02251       parkingexten, 
02252       chan->name,
02253       S_OR(chan->cid.cid_num, "<unknown>"),
02254       S_OR(chan->cid.cid_name, "<unknown>")
02255       );
02256 }
02257 
02258 static char *callback_dialoptions(struct ast_flags *features_callee, struct ast_flags *features_caller, char *options, size_t len)
02259 {
02260    int i = 0;
02261    enum {
02262       OPT_CALLEE_REDIRECT   = 't',
02263       OPT_CALLER_REDIRECT   = 'T',
02264       OPT_CALLEE_AUTOMON    = 'w',
02265       OPT_CALLER_AUTOMON    = 'W',
02266       OPT_CALLEE_DISCONNECT = 'h',
02267       OPT_CALLER_DISCONNECT = 'H',
02268       OPT_CALLEE_PARKCALL   = 'k',
02269       OPT_CALLER_PARKCALL   = 'K',
02270    };
02271 
02272    memset(options, 0, len);
02273    if (ast_test_flag(features_caller, AST_FEATURE_REDIRECT) && i < len) {
02274       options[i++] = OPT_CALLER_REDIRECT;
02275    }
02276    if (ast_test_flag(features_caller, AST_FEATURE_AUTOMON) && i < len) {
02277       options[i++] = OPT_CALLER_AUTOMON;
02278    }
02279    if (ast_test_flag(features_caller, AST_FEATURE_DISCONNECT) && i < len) {
02280       options[i++] = OPT_CALLER_DISCONNECT;
02281    }
02282    if (ast_test_flag(features_caller, AST_FEATURE_PARKCALL) && i < len) {
02283       options[i++] = OPT_CALLER_PARKCALL;
02284    }
02285 
02286    if (ast_test_flag(features_callee, AST_FEATURE_REDIRECT) && i < len) {
02287       options[i++] = OPT_CALLEE_REDIRECT;
02288    }
02289    if (ast_test_flag(features_callee, AST_FEATURE_AUTOMON) && i < len) {
02290       options[i++] = OPT_CALLEE_AUTOMON;
02291    }
02292    if (ast_test_flag(features_callee, AST_FEATURE_DISCONNECT) && i < len) {
02293       options[i++] = OPT_CALLEE_DISCONNECT;
02294    }
02295    if (ast_test_flag(features_callee, AST_FEATURE_PARKCALL) && i < len) {
02296       options[i++] = OPT_CALLEE_PARKCALL;
02297    }
02298 
02299    return options;
02300 }
02301 
02302 /*! \brief Take care of parked calls and unpark them if needed */
02303 static void *do_parking_thread(void *ignore)
02304 {
02305    fd_set rfds, efds;   /* results from previous select, to be preserved across loops. */
02306    FD_ZERO(&rfds);
02307    FD_ZERO(&efds);
02308 
02309    for (;;) {
02310       struct parkeduser *pu, *pl, *pt = NULL;
02311       int ms = -1;   /* select timeout, uninitialized */
02312       int max = -1;  /* max fd, none there yet */
02313       fd_set nrfds, nefds; /* args for the next select */
02314       FD_ZERO(&nrfds);
02315       FD_ZERO(&nefds);
02316 
02317       ast_mutex_lock(&parking_lock);
02318       pl = NULL;
02319       pu = parkinglot;
02320       /* navigate the list with prev-cur pointers to support removals */
02321       while (pu) {
02322          struct ast_channel *chan = pu->chan;   /* shorthand */
02323          int tms;        /* timeout for this item */
02324          int x;          /* fd index in channel */
02325          struct ast_context *con;
02326 
02327          if (pu->notquiteyet) { /* Pretend this one isn't here yet */
02328             pl = pu;
02329             pu = pu->next;
02330             continue;
02331          }
02332          tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
02333          if (tms > pu->parkingtime) {
02334             ast_indicate(chan, AST_CONTROL_UNHOLD);
02335             /* Get chan, exten from derived kludge */
02336             if (pu->peername[0]) {
02337                char *peername = ast_strdupa(pu->peername);
02338                char *cp = strrchr(peername, '-');
02339                if (cp) 
02340                   *cp = 0;
02341                con = ast_context_find(parking_con_dial);
02342                if (!con) {
02343                   con = ast_context_create(NULL, parking_con_dial, registrar);
02344                   if (!con)
02345                      ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
02346                }
02347                if (con) {
02348                   char returnexten[AST_MAX_EXTENSION];
02349                   struct ast_datastore *features_datastore;
02350                   struct ast_dial_features *dialfeatures = NULL;
02351 
02352                   ast_channel_lock(chan);
02353 
02354                   if ((features_datastore = ast_channel_datastore_find(chan, &dial_features_info, NULL)))
02355                      dialfeatures = features_datastore->data;
02356 
02357                   ast_channel_unlock(chan);
02358 
02359                   if (!strncmp(peername, "Parked/", 7)) {
02360                      peername += 7;
02361                   }
02362 
02363                   if (dialfeatures) {
02364                      char buf[MAX_DIAL_FEATURE_OPTIONS] = {0,};
02365                      snprintf(returnexten, sizeof(returnexten), "%s|30|%s", peername, callback_dialoptions(&(dialfeatures->features_callee), &(dialfeatures->features_caller), buf, sizeof(buf)));
02366                   } else { /* Existing default */
02367                      ast_log(LOG_WARNING, "Dialfeatures not found on %s, using default!\n", chan->name);
02368                      snprintf(returnexten, sizeof(returnexten), "%s|30|t", peername);
02369                   }
02370 
02371                   ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), ast_free_ptr, registrar);
02372                }
02373                set_c_e_p(chan, parking_con_dial, peername, 1);
02374             } else {
02375                /* They've been waiting too long, send them back to where they came.  Theoretically they
02376                   should have their original extensions and such, but we copy to be on the safe side */
02377                set_c_e_p(chan, pu->context, pu->exten, pu->priority);
02378             }
02379 
02380             post_manager_event("ParkedCallTimeOut", pu->parkingexten, chan);
02381 
02382             if (option_verbose > 1) 
02383                ast_verbose(VERBOSE_PREFIX_2 "Timeout for %s parked on %d. Returning to %s,%s,%d\n", chan->name, pu->parkingnum, chan->context, chan->exten, chan->priority);
02384             /* Start up the PBX, or hang them up */
02385             if (ast_pbx_start(chan))  {
02386                ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan->name);
02387                ast_hangup(chan);
02388             }
02389             /* And take them out of the parking lot */
02390             if (pl) 
02391                pl->next = pu->next;
02392             else
02393                parkinglot = pu->next;
02394             pt = pu;
02395             pu = pu->next;
02396             con = ast_context_find(parking_con);
02397             if (con) {
02398                if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL))
02399                   ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
02400                else
02401                   notify_metermaids(pt->parkingexten, parking_con);
02402             } else
02403                ast_log(LOG_WARNING, "Whoa, no parking context?\n");
02404             free(pt);
02405          } else { /* still within parking time, process descriptors */
02406             for (x = 0; x < AST_MAX_FDS; x++) {
02407                struct ast_frame *f;
02408 
02409                if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], &rfds) && !FD_ISSET(chan->fds[x], &efds)))
02410                   continue;   /* nothing on this descriptor */
02411 
02412                if (FD_ISSET(chan->fds[x], &efds))
02413                   ast_set_flag(chan, AST_FLAG_EXCEPTION);
02414                else
02415                   ast_clear_flag(chan, AST_FLAG_EXCEPTION);
02416                chan->fdno = x;
02417 
02418                /* See if they need servicing */
02419                f = ast_read(chan);
02420                if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass ==  AST_CONTROL_HANGUP)) {
02421                   if (f)
02422                      ast_frfree(f);
02423                   post_manager_event("ParkedCallGiveUp", pu->parkingexten, chan);
02424 
02425                   /* There's a problem, hang them up*/
02426                   if (option_verbose > 1) 
02427                      ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", chan->name);
02428                   ast_hangup(chan);
02429                   /* And take them out of the parking lot */
02430                   if (pl) 
02431                      pl->next = pu->next;
02432                   else
02433                      parkinglot = pu->next;
02434                   pt = pu;
02435                   pu = pu->next;
02436                   con = ast_context_find(parking_con);
02437                   if (con) {
02438                      if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL))
02439                         ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
02440                      else {
02441                         notify_metermaids(pt->parkingexten, parking_con);
02442                      }
02443                   } else
02444                      ast_log(LOG_WARNING, "Whoa, no parking context?\n");
02445                   free(pt);
02446                   break;
02447                } else {
02448                   /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
02449                   ast_frfree(f);
02450                   if (pu->moh_trys < 3 && !chan->generatordata) {
02451                      if (option_debug)
02452                         ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source.  Restarting.\n");
02453                      ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
02454                         S_OR(parkmohclass, NULL),
02455                         !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
02456                      pu->moh_trys++;
02457                   }
02458                   goto std;   /*! \todo XXX Ick: jumping into an else statement??? XXX */
02459                }
02460 
02461             } /* end for */
02462             if (x >= AST_MAX_FDS) {
02463 std:              for (x=0; x<AST_MAX_FDS; x++) {  /* mark fds for next round */
02464                   if (chan->fds[x] > -1) {
02465                      FD_SET(chan->fds[x], &nrfds);
02466                      FD_SET(chan->fds[x], &nefds);
02467                      if (chan->fds[x] > max)
02468                         max = chan->fds[x];
02469                   }
02470                }
02471                /* Keep track of our shortest wait */
02472                if (tms < ms || ms < 0)
02473                   ms = tms;
02474                pl = pu;
02475                pu = pu->next;
02476             }
02477          }
02478       } /* end while */
02479       ast_mutex_unlock(&parking_lock);
02480       rfds = nrfds;
02481       efds = nefds;
02482       {
02483          struct timeval tv = ast_samp2tv(ms, 1000);
02484          /* Wait for something to happen */
02485          ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
02486       }
02487       pthread_testcancel();
02488    }
02489    return NULL;   /* Never reached */
02490 }
02491 
02492 /*! \brief Park a call */
02493 static int park_call_exec(struct ast_channel *chan, void *data)
02494 {
02495    /* Cache the original channel name in case we get masqueraded in the middle
02496     * of a park--it is still theoretically possible for a transfer to happen before
02497     * we get here, but it is _really_ unlikely */
02498    char *orig_chan_name = ast_strdupa(chan->name);
02499    char orig_exten[AST_MAX_EXTENSION];
02500    int orig_priority = chan->priority;
02501 
02502    /* Data is unused at the moment but could contain a parking
02503       lot context eventually */
02504    int res = 0;
02505    struct ast_module_user *u;
02506 
02507    u = ast_module_user_add(chan);
02508 
02509    ast_copy_string(orig_exten, chan->exten, sizeof(orig_exten));
02510 
02511    /* Setup the exten/priority to be s/1 since we don't know
02512       where this call should return */
02513    strcpy(chan->exten, "s");
02514    chan->priority = 1;
02515    /* Answer if call is not up */
02516    if (chan->_state != AST_STATE_UP)
02517       res = ast_answer(chan);
02518    /* Sleep to allow VoIP streams to settle down */
02519    if (!res)
02520       res = ast_safe_sleep(chan, 1000);
02521    /* Park the call */
02522    if (!res) {
02523       res = masq_park_call_announce(chan, chan, 0, NULL, orig_chan_name);
02524       /* Continue on in the dialplan */
02525       if (res == 1) {
02526          ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten));
02527          chan->priority = orig_priority;
02528          res = 0;
02529       } else if (!res) {
02530          res = 1;
02531       }
02532    }
02533 
02534    ast_module_user_remove(u);
02535 
02536    return res;
02537 }
02538 
02539 /*! \brief Pickup parked call */
02540 static int park_exec(struct ast_channel *chan, void *data)
02541 {
02542    int res = 0;
02543    struct ast_module_user *u;
02544    struct ast_channel *peer=NULL;
02545    struct parkeduser *pu, *pl=NULL;
02546    struct ast_context *con;
02547 
02548    int park;
02549    struct ast_bridge_config config;
02550 
02551    if (!data) {
02552       ast_log(LOG_WARNING, "Parkedcall requires an argument (extension number)\n");
02553       return -1;
02554    }
02555 
02556    u = ast_module_user_add(chan);
02557 
02558    park = atoi((char *)data);
02559    ast_mutex_lock(&parking_lock);
02560    pu = parkinglot;
02561    while(pu) {
02562       if (pu->parkingnum == park) {
02563          if (pu->chan->pbx) { /* do not allow call to be picked up until the PBX thread is finished */
02564             ast_mutex_unlock(&parking_lock);
02565             ast_module_user_remove(u);
02566             return -1;
02567          }
02568          if (pl)
02569             pl->next = pu->next;
02570          else
02571             parkinglot = pu->next;
02572          break;
02573       }
02574       pl = pu;
02575       pu = pu->next;
02576    }
02577    ast_mutex_unlock(&parking_lock);
02578    if (pu) {
02579       peer = pu->chan;
02580       con = ast_context_find(parking_con);
02581       if (con) {
02582          if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
02583             ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
02584          else
02585             notify_metermaids(pu->parkingexten, parking_con);
02586       } else
02587          ast_log(LOG_WARNING, "Whoa, no parking context?\n");
02588 
02589       manager_event(EVENT_FLAG_CALL, "UnParkedCall",
02590          "Exten: %s\r\n"
02591          "Channel: %s\r\n"
02592          "From: %s\r\n"
02593          "CallerID: %s\r\n"
02594          "CallerIDName: %s\r\n",
02595          pu->parkingexten, pu->chan->name, chan->name,
02596          S_OR(pu->chan->cid.cid_num, "<unknown>"),
02597          S_OR(pu->chan->cid.cid_name, "<unknown>")
02598          );
02599 
02600       free(pu);
02601    }
02602    /* JK02: it helps to answer the channel if not already up */
02603    if (chan->_state != AST_STATE_UP)
02604       ast_answer(chan);
02605 
02606    if (peer) {
02607       struct ast_datastore *features_datastore;
02608       struct ast_dial_features *dialfeatures = NULL;
02609 
02610       /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
02611 
02612       if (!ast_strlen_zero(courtesytone)) {
02613          int error = 0;
02614          ast_indicate(peer, AST_CONTROL_UNHOLD);
02615          if (parkedplay == 0) {
02616             error = ast_stream_and_wait(chan, courtesytone, chan->language, "");
02617          } else if (parkedplay == 1) {
02618             error = ast_stream_and_wait(peer, courtesytone, chan->language, "");
02619          } else if (parkedplay == 2) {
02620             if (!ast_streamfile(chan, courtesytone, chan->language) &&
02621                   !ast_streamfile(peer, courtesytone, chan->language)) {
02622                /*! \todo XXX we would like to wait on both! */
02623                res = ast_waitstream(chan, "");
02624                if (res >= 0)
02625                   res = ast_waitstream(peer, "");
02626                if (res < 0)
02627                   error = 1;
02628             }
02629                         }
02630          if (error) {
02631             ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
02632             ast_hangup(peer);
02633             ast_module_user_remove(u);
02634             return -1;
02635          }
02636       } else
02637          ast_indicate(peer, AST_CONTROL_UNHOLD);
02638 
02639       res = ast_channel_make_compatible(chan, peer);
02640       if (res < 0) {
02641          ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
02642          ast_hangup(peer);
02643          ast_module_user_remove(u);
02644          return -1;
02645       }
02646       /* This runs sorta backwards, since we give the incoming channel control, as if it
02647          were the person called. */
02648       if (option_verbose > 2)
02649          ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
02650 
02651       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
02652       ast_cdr_setdestchan(chan->cdr, peer->name);
02653       memset(&config, 0, sizeof(struct ast_bridge_config));
02654 
02655       /* Get datastore for peer and apply it's features to the callee side of the bridge config */
02656       ast_channel_lock(peer);
02657       if ((features_datastore = ast_channel_datastore_find(peer, &dial_features_info, NULL))) {
02658          dialfeatures = features_datastore->data;
02659       }
02660       ast_channel_unlock(peer);
02661 
02662       if (dialfeatures) {
02663          ast_copy_flags(&(config.features_callee), dialfeatures->is_caller ? &(dialfeatures->features_caller) : &(dialfeatures->features_callee), AST_FLAGS_ALL);
02664       }
02665 
02666       if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
02667          ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
02668       }
02669       if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
02670          ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
02671       }
02672       if ((parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
02673          ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
02674       }
02675       if ((parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
02676          ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
02677       }
02678       if ((parkedcallhangup == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
02679          ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
02680       }
02681       if ((parkedcallhangup == AST_FEATURE_FLAG_BYCALLER) || (parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
02682          ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
02683       }
02684       if ((parkedcallrecording == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
02685          ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
02686       }
02687       if ((parkedcallrecording == AST_FEATURE_FLAG_BYCALLER) || (parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
02688          ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
02689       }
02690       res = ast_bridge_call(chan, peer, &config);
02691 
02692       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
02693       ast_cdr_setdestchan(chan->cdr, peer->name);
02694 
02695       /* Simulate the PBX hanging up */
02696       ast_hangup(peer);
02697       ast_module_user_remove(u);
02698       return -1;
02699    } else {
02700       /*! \todo XXX Play a message XXX */
02701       if (ast_stream_and_wait(chan, "pbx-invalidpark", chan->language, ""))
02702          ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
02703       if (option_verbose > 2) 
02704          ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park);
02705       res = -1;
02706    }
02707 
02708    ast_module_user_remove(u);
02709 
02710    return -1;
02711 }
02712 
02713 static int handle_showfeatures(int fd, int argc, char *argv[])
02714 {
02715    int i;
02716    struct ast_call_feature *feature;
02717    char format[] = "%-25s %-7s %-7s\n";
02718 
02719    ast_cli(fd, format, "Builtin Feature", "Default", "Current");
02720    ast_cli(fd, format, "---------------", "-------", "-------");
02721 
02722    ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext());      /* default hardcoded above, so we'll hardcode it here */
02723 
02724    ast_rwlock_rdlock(&features_lock);
02725    for (i = 0; i < FEATURES_COUNT; i++)
02726       ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
02727    ast_rwlock_unlock(&features_lock);
02728 
02729    ast_cli(fd, "\n");
02730    ast_cli(fd, format, "Dynamic Feature", "Default", "Current");
02731    ast_cli(fd, format, "---------------", "-------", "-------");
02732    if (AST_RWLIST_EMPTY(&feature_list)) {
02733       ast_cli(fd, "(none)\n");
02734    } else {
02735       AST_RWLIST_RDLOCK(&feature_list);
02736       AST_RWLIST_TRAVERSE(&feature_list, feature, feature_entry) {
02737          ast_cli(fd, format, feature->sname, "no def", feature->exten);
02738       }
02739       AST_RWLIST_UNLOCK(&feature_list);
02740    }
02741    ast_cli(fd, "\nCall parking\n");
02742    ast_cli(fd, "------------\n");
02743    ast_cli(fd,"%-20s:   %s\n", "Parking extension", parking_ext);
02744    ast_cli(fd,"%-20s:   %s\n", "Parking context", parking_con);
02745    ast_cli(fd,"%-20s:   %d-%d\n", "Parked call extensions", parking_start, parking_stop);
02746    ast_cli(fd,"\n");
02747    
02748    return RESULT_SUCCESS;
02749 }
02750 
02751 static char showfeatures_help[] =
02752 "Usage: feature list\n"
02753 "       Lists currently configured features.\n";
02754 
02755 static int handle_parkedcalls(int fd, int argc, char *argv[])
02756 {
02757    struct parkeduser *cur;
02758    int numparked = 0;
02759 
02760    ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
02761       , "Context", "Extension", "Pri", "Timeout");
02762 
02763    ast_mutex_lock(&parking_lock);
02764 
02765    for (cur = parkinglot; cur; cur = cur->next) {
02766       ast_cli(fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
02767          ,cur->parkingexten, cur->chan->name, cur->context, cur->exten
02768          ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
02769 
02770       numparked++;
02771    }
02772    ast_mutex_unlock(&parking_lock);
02773    ast_cli(fd, "%d parked call%s.\n", numparked, (numparked != 1) ? "s" : "");
02774 
02775 
02776    return RESULT_SUCCESS;
02777 }
02778 
02779 static char showparked_help[] =
02780 "Usage: show parkedcalls\n"
02781 "       Lists currently parked calls.\n";
02782 
02783 static struct ast_cli_entry cli_show_features_deprecated = {
02784    { "show", "features", NULL },
02785    handle_showfeatures, NULL,
02786    NULL };
02787 
02788 static struct ast_cli_entry cli_features[] = {
02789    { { "feature", "show", NULL },
02790    handle_showfeatures, "Lists configured features",
02791    showfeatures_help, NULL, &cli_show_features_deprecated },
02792 
02793    { { "show", "parkedcalls", NULL },
02794    handle_parkedcalls, "Lists parked calls",
02795    showparked_help },
02796 };
02797 
02798 /*! \brief Dump lot status */
02799 static int manager_parking_status( struct mansession *s, const struct message *m)
02800 {
02801    struct parkeduser *cur;
02802    const char *id = astman_get_header(m, "ActionID");
02803    char idText[256] = "";
02804 
02805    if (!ast_strlen_zero(id))
02806       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02807 
02808    astman_send_ack(s, m, "Parked calls will follow");
02809 
02810    ast_mutex_lock(&parking_lock);
02811 
02812    for (cur = parkinglot; cur; cur = cur->next) {
02813       astman_append(s, "Event: ParkedCall\r\n"
02814          "Exten: %d\r\n"
02815          "Channel: %s\r\n"
02816          "From: %s\r\n"
02817          "Timeout: %ld\r\n"
02818          "CallerID: %s\r\n"
02819          "CallerIDName: %s\r\n"
02820          "%s"
02821          "\r\n",
02822          cur->parkingnum, cur->chan->name, cur->peername,
02823          (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
02824          S_OR(cur->chan->cid.cid_num, ""),   /* XXX in other places it is <unknown> */
02825          S_OR(cur->chan->cid.cid_name, ""),
02826          idText);
02827    }
02828 
02829    astman_append(s,
02830       "Event: ParkedCallsComplete\r\n"
02831       "%s"
02832       "\r\n",idText);
02833 
02834    ast_mutex_unlock(&parking_lock);
02835 
02836    return RESULT_SUCCESS;
02837 }
02838 
02839 static char mandescr_park[] =
02840 "Description: Park a channel.\n"
02841 "Variables: (Names marked with * are required)\n"
02842 "  *Channel: Channel name to park\n"
02843 "  *Channel2: Channel to announce park info to (and return to if timeout)\n"
02844 "  Timeout: Number of milliseconds to wait before callback.\n";  
02845 
02846 static int manager_park(struct mansession *s, const struct message *m)
02847 {
02848    const char *channel = astman_get_header(m, "Channel");
02849    const char *channel2 = astman_get_header(m, "Channel2");
02850    const char *timeout = astman_get_header(m, "Timeout");
02851    char buf[BUFSIZ];
02852    int to = 0;
02853    int res = 0;
02854    int parkExt = 0;
02855    struct ast_channel *ch1, *ch2;
02856 
02857    if (ast_strlen_zero(channel)) {
02858       astman_send_error(s, m, "Channel not specified");
02859       return 0;
02860    }
02861 
02862    if (ast_strlen_zero(channel2)) {
02863       astman_send_error(s, m, "Channel2 not specified");
02864       return 0;
02865    }
02866 
02867    ch1 = ast_get_channel_by_name_locked(channel);
02868    if (!ch1) {
02869       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
02870       astman_send_error(s, m, buf);
02871       return 0;
02872    }
02873 
02874    ch2 = ast_get_channel_by_name_locked(channel2);
02875    if (!ch2) {
02876       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
02877       astman_send_error(s, m, buf);
02878       ast_channel_unlock(ch1);
02879       return 0;
02880    }
02881 
02882    if (!ast_strlen_zero(timeout)) {
02883       sscanf(timeout, "%30d", &to);
02884    }
02885 
02886    res = ast_masq_park_call(ch1, ch2, to, &parkExt);
02887    if (!res) {
02888       ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
02889       astman_send_ack(s, m, "Park successful");
02890    } else {
02891       astman_send_error(s, m, "Park failure");
02892    }
02893 
02894    ast_channel_unlock(ch1);
02895    ast_channel_unlock(ch2);
02896 
02897    return 0;
02898 }
02899 
02900 
02901 int ast_pickup_call(struct ast_channel *chan)
02902 {
02903    struct ast_channel *cur = NULL;
02904    int res = -1;
02905 
02906    while ( (cur = ast_channel_walk_locked(cur)) != NULL) {
02907       if (!cur->pbx && 
02908          (cur != chan) &&
02909          (chan->pickupgroup & cur->callgroup) &&
02910          ((cur->_state == AST_STATE_RINGING) ||
02911           (cur->_state == AST_STATE_RING))) {
02912             break;
02913       }
02914       ast_channel_unlock(cur);
02915    }
02916    if (cur) {
02917       if (option_debug)
02918          ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
02919       res = ast_answer(chan);
02920       if (res)
02921          ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
02922       res = ast_queue_control(chan, AST_CONTROL_ANSWER);
02923       if (res)
02924          ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
02925       res = ast_channel_masquerade(cur, chan);
02926       if (res)
02927          ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);     /* Done */
02928       ast_channel_unlock(cur);
02929    } else   {
02930       if (option_debug)
02931          ast_log(LOG_DEBUG, "No call pickup possible...\n");
02932    }
02933    return res;
02934 }
02935 
02936 /*! \brief Add parking hints for all defined parking lots */
02937 static void park_add_hints(char *context, int start, int stop)
02938 {
02939    int numext;
02940    char device[AST_MAX_EXTENSION];
02941    char exten[10];
02942 
02943    for (numext = start; numext <= stop; numext++) {
02944       snprintf(exten, sizeof(exten), "%d", numext);
02945       snprintf(device, sizeof(device), "park:%s@%s", exten, context);
02946       ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
02947    }
02948 }
02949 
02950 
02951 static int load_config(void) 
02952 {
02953    int start = 0, end = 0;
02954    int res;
02955    struct ast_context *con = NULL;
02956    struct ast_config *cfg = NULL;
02957    struct ast_variable *var = NULL;
02958    char old_parking_ext[AST_MAX_EXTENSION];
02959    char old_parking_con[AST_MAX_EXTENSION] = "";
02960 
02961    if (!ast_strlen_zero(parking_con)) {
02962       strcpy(old_parking_ext, parking_ext);
02963       strcpy(old_parking_con, parking_con);
02964    } 
02965 
02966    /* Reset to defaults */
02967    strcpy(parking_con, "parkedcalls");
02968    strcpy(parking_con_dial, "park-dial");
02969    strcpy(parking_ext, "700");
02970    strcpy(pickup_ext, "*8");
02971    strcpy(parkmohclass, "default");
02972    courtesytone[0] = '\0';
02973    strcpy(xfersound, "beep");
02974    strcpy(xferfailsound, "pbx-invalid");
02975    parking_start = 701;
02976    parking_stop = 750;
02977    parkfindnext = 0;
02978    adsipark = 0;
02979    parkaddhints = 0;
02980    parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
02981    parkedcallreparking = 0;
02982    parkedcallhangup = 0;
02983    parkedcallrecording = 0;
02984 
02985    transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
02986    featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
02987    atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
02988 
02989    cfg = ast_config_load("features.conf");
02990    if (!cfg) {
02991       ast_log(LOG_WARNING,"Could not load features.conf\n");
02992       return AST_MODULE_LOAD_DECLINE;
02993    }
02994    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
02995       if (!strcasecmp(var->name, "parkext")) {
02996          ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
02997       } else if (!strcasecmp(var->name, "context")) {
02998          ast_copy_string(parking_con, var->value, sizeof(parking_con));
02999       } else if (!strcasecmp(var->name, "parkingtime")) {
03000          if ((sscanf(var->value, "%30d", &parkingtime) != 1) || (parkingtime < 1)) {
03001             ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
03002             parkingtime = DEFAULT_PARK_TIME;
03003          } else
03004             parkingtime = parkingtime * 1000;
03005       } else if (!strcasecmp(var->name, "parkpos")) {
03006          if (sscanf(var->value, "%30d-%30d", &start, &end) != 2) {
03007             ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var->lineno);
03008          } else {
03009             parking_start = start;
03010             parking_stop = end;
03011          }
03012       } else if (!strcasecmp(var->name, "findslot")) {
03013          parkfindnext = (!strcasecmp(var->value, "next"));
03014       } else if (!strcasecmp(var->name, "parkinghints")) {
03015          parkaddhints = ast_true(var->value);
03016       } else if (!strcasecmp(var->name, "parkedcalltransfers")) {
03017          if (!strcasecmp(var->value, "no"))
03018             parkedcalltransfers = 0;
03019          else if (!strcasecmp(var->value, "caller"))
03020             parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
03021          else if (!strcasecmp(var->value, "callee"))
03022             parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
03023          else if (!strcasecmp(var->value, "both"))
03024             parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
03025       } else if (!strcasecmp(var->name, "parkedcallreparking")) {
03026          if (!strcasecmp(var->value, "no"))
03027             parkedcallreparking = 0;
03028          else if (!strcasecmp(var->value, "caller"))
03029             parkedcallreparking = AST_FEATURE_FLAG_BYCALLER;
03030          else if (!strcasecmp(var->value, "callee"))
03031             parkedcallreparking = AST_FEATURE_FLAG_BYCALLEE;
03032          else if (!strcasecmp(var->value, "both"))
03033             parkedcallreparking = AST_FEATURE_FLAG_BYBOTH;
03034       } else if (!strcasecmp(var->name, "parkedcallhangup")) {
03035          if (!strcasecmp(var->value, "no"))
03036             parkedcallhangup = 0;
03037          else if (!strcasecmp(var->value, "caller"))
03038             parkedcallhangup = AST_FEATURE_FLAG_BYCALLER;
03039          else if (!strcasecmp(var->value, "callee"))
03040             parkedcallhangup = AST_FEATURE_FLAG_BYCALLEE;
03041          else if (!strcasecmp(var->value, "both"))
03042             parkedcallhangup = AST_FEATURE_FLAG_BYBOTH;
03043       } else if (!strcasecmp(var->name, "parkedcallrecording")) {
03044          if (!strcasecmp(var->value, "no"))
03045             parkedcallrecording = 0;
03046          else if (!strcasecmp(var->value, "caller"))
03047             parkedcallrecording = AST_FEATURE_FLAG_BYCALLER;
03048          else if (!strcasecmp(var->value, "callee"))
03049             parkedcallrecording = AST_FEATURE_FLAG_BYCALLEE;
03050          else if (!strcasecmp(var->value, "both"))
03051             parkedcallrecording = AST_FEATURE_FLAG_BYBOTH;
03052       } else if (!strcasecmp(var->name, "adsipark")) {
03053          adsipark = ast_true(var->value);
03054       } else if (!strcasecmp(var->name, "transferdigittimeout")) {
03055          if ((sscanf(var->value, "%30d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
03056             ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
03057             transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
03058          } else
03059             transferdigittimeout = transferdigittimeout * 1000;
03060       } else if (!strcasecmp(var->name, "featuredigittimeout")) {
03061          if ((sscanf(var->value, "%30d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
03062             ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
03063             featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
03064          }
03065       } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
03066          if ((sscanf(var->value, "%30d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
03067             ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
03068             atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
03069          } else
03070             atxfernoanswertimeout = atxfernoanswertimeout * 1000;
03071       } else if (!strcasecmp(var->name, "courtesytone")) {
03072          ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
03073       }  else if (!strcasecmp(var->name, "parkedplay")) {
03074          if (!strcasecmp(var->value, "both"))
03075             parkedplay = 2;
03076          else if (!strcasecmp(var->value, "parked"))
03077             parkedplay = 1;
03078          else
03079             parkedplay = 0;
03080       } else if (!strcasecmp(var->name, "xfersound")) {
03081          ast_copy_string(xfersound, var->value, sizeof(xfersound));
03082       } else if (!strcasecmp(var->name, "xferfailsound")) {
03083          ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
03084       } else if (!strcasecmp(var->name, "pickupexten")) {
03085          ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
03086       } else if (!strcasecmp(var->name, "parkedmusicclass")) {
03087          ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass));
03088       }
03089    }
03090 
03091    unmap_features();
03092    for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
03093       if (remap_feature(var->name, var->value))
03094          ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
03095    }
03096 
03097    /* Map a key combination to an application*/
03098    ast_unregister_features();
03099    for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
03100       char *tmp_val = ast_strdupa(var->value);
03101       char *exten, *activateon, *activatedby, *app, *app_args, *moh_class; 
03102       struct ast_call_feature *feature;
03103 
03104       /* strsep() sets the argument to NULL if match not found, and it
03105        * is safe to use it with a NULL argument, so we don't check
03106        * between calls.
03107        */
03108       exten = strsep(&tmp_val,",");
03109       activatedby = strsep(&tmp_val,",");
03110       app = strsep(&tmp_val,",");
03111       app_args = strsep(&tmp_val,",");
03112       moh_class = strsep(&tmp_val,",");
03113 
03114       activateon = strsep(&activatedby, "/");   
03115 
03116       /*! \todo XXX var_name or app_args ? */
03117       if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
03118          ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
03119             app, exten, activateon, var->name);
03120          continue;
03121       }
03122 
03123       AST_RWLIST_RDLOCK(&feature_list);
03124       if ((feature = find_dynamic_feature(var->name))) {
03125          AST_RWLIST_UNLOCK(&feature_list);
03126          ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name);
03127          continue;
03128       }
03129       AST_RWLIST_UNLOCK(&feature_list);
03130             
03131       if (!(feature = ast_calloc(1, sizeof(*feature))))
03132          continue;               
03133 
03134       ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
03135       ast_copy_string(feature->app, app, FEATURE_APP_LEN);
03136       ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
03137       
03138       if (app_args) 
03139          ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
03140 
03141       if (moh_class)
03142          ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN);
03143          
03144       ast_copy_string(feature->exten, exten, sizeof(feature->exten));
03145       feature->operation = feature_exec_app;
03146       ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
03147 
03148       /* Allow caller and calle to be specified for backwards compatability */
03149       if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller"))
03150          ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
03151       else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee"))
03152          ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
03153       else {
03154          ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
03155             " must be 'self', or 'peer'\n", var->name);
03156          continue;
03157       }
03158 
03159       if (ast_strlen_zero(activatedby))
03160          ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
03161       else if (!strcasecmp(activatedby, "caller"))
03162          ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
03163       else if (!strcasecmp(activatedby, "callee"))
03164          ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
03165       else if (!strcasecmp(activatedby, "both"))
03166          ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
03167       else {
03168          ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
03169             " must be 'caller', or 'callee', or 'both'\n", var->name);
03170          continue;
03171       }
03172 
03173       ast_register_feature(feature);
03174          
03175       if (option_verbose >= 1)
03176          ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);  
03177    }   
03178    ast_config_destroy(cfg);
03179 
03180    /* Remove the old parking extension */
03181    if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) {
03182       if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar))
03183             notify_metermaids(old_parking_ext, old_parking_con);
03184       if (option_debug)
03185          ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con);
03186    }
03187    
03188    if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
03189       ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
03190       return -1;
03191    }
03192    res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar);
03193    if (parkaddhints)
03194       park_add_hints(parking_con, parking_start, parking_stop);
03195    if (!res)
03196       notify_metermaids(ast_parking_ext(), parking_con);
03197    return res;
03198 
03199 }
03200 
03201 static int reload(void)
03202 {
03203    return load_config();
03204 }
03205 
03206 static int load_module(void)
03207 {
03208    int res;
03209    
03210    memset(parking_ext, 0, sizeof(parking_ext));
03211    memset(parking_con, 0, sizeof(parking_con));
03212 
03213    if ((res = load_config()))
03214       return res;
03215    ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
03216    ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
03217    res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
03218    if (!res)
03219       res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
03220    if (!res) {
03221       ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls" );
03222       ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
03223          "Park a channel", mandescr_park); 
03224    }
03225 
03226    res |= ast_devstate_prov_add("Park", metermaidstate);
03227 
03228    return res;
03229 }
03230 
03231 
03232 static int unload_module(void)
03233 {
03234    ast_module_user_hangup_all();
03235 
03236    ast_manager_unregister("ParkedCalls");
03237    ast_manager_unregister("Park");
03238    ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
03239    ast_unregister_application(parkcall);
03240    ast_devstate_prov_del("Park");
03241    return ast_unregister_application(parkedcall);
03242 }
03243 
03244 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Features Resource",
03245       .load = load_module,
03246       .unload = unload_module,
03247       .reload = reload,
03248           );

Generated on Fri Feb 19 17:12:46 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7