Wed Aug 15 01:24:23 2007

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 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 77778 $")
00029 
00030 #include <pthread.h>
00031 #include <stdlib.h>
00032 #include <errno.h>
00033 #include <unistd.h>
00034 #include <string.h>
00035 #include <stdlib.h>
00036 #include <stdio.h>
00037 #include <sys/time.h>
00038 #include <sys/signal.h>
00039 #include <netinet/in.h>
00040 
00041 #include "asterisk/lock.h"
00042 #include "asterisk/file.h"
00043 #include "asterisk/logger.h"
00044 #include "asterisk/channel.h"
00045 #include "asterisk/pbx.h"
00046 #include "asterisk/options.h"
00047 #include "asterisk/causes.h"
00048 #include "asterisk/module.h"
00049 #include "asterisk/translate.h"
00050 #include "asterisk/app.h"
00051 #include "asterisk/say.h"
00052 #include "asterisk/features.h"
00053 #include "asterisk/musiconhold.h"
00054 #include "asterisk/config.h"
00055 #include "asterisk/cli.h"
00056 #include "asterisk/manager.h"
00057 #include "asterisk/utils.h"
00058 #include "asterisk/adsi.h"
00059 #include "asterisk/devicestate.h"
00060 #include "asterisk/monitor.h"
00061 
00062 #define DEFAULT_PARK_TIME 45000
00063 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
00064 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
00065 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
00066 
00067 #define AST_MAX_WATCHERS 256
00068 
00069 enum {
00070    AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0),
00071    AST_FEATURE_FLAG_ONPEER =    (1 << 1),
00072    AST_FEATURE_FLAG_ONSELF =    (1 << 2),
00073    AST_FEATURE_FLAG_BYCALLEE =  (1 << 3),
00074    AST_FEATURE_FLAG_BYCALLER =  (1 << 4),
00075    AST_FEATURE_FLAG_BYBOTH  =   (3 << 3),
00076 };
00077 
00078 static char *parkedcall = "ParkedCall";
00079 
00080 static int parkaddhints = 0;                               /*!< Add parking hints automatically */
00081 static int parkingtime = DEFAULT_PARK_TIME;                /*!< No more than 45 seconds parked before you do something with them */
00082 static char parking_con[AST_MAX_EXTENSION];                /*!< Context for which parking is made accessible */
00083 static char parking_con_dial[AST_MAX_EXTENSION];           /*!< Context for dialback for parking (KLUDGE) */
00084 static char parking_ext[AST_MAX_EXTENSION];                /*!< Extension you type to park the call */
00085 static char pickup_ext[AST_MAX_EXTENSION];                 /*!< Call pickup extension */
00086 static char parkmohclass[MAX_MUSICCLASS];                  /*!< Music class used for parking */
00087 static int parking_start;                                  /*!< First available extension for parking */
00088 static int parking_stop;                                   /*!< Last available extension for parking */
00089 
00090 static char courtesytone[256];                             /*!< Courtesy tone */
00091 static int parkedplay = 0;                                 /*!< Who to play the courtesy tone to */
00092 static char xfersound[256];                                /*!< Call transfer sound */
00093 static char xferfailsound[256];                            /*!< Call transfer failure sound */
00094 
00095 static int parking_offset;
00096 static int parkfindnext;
00097 
00098 static int adsipark;
00099 
00100 static int transferdigittimeout;
00101 static int featuredigittimeout;
00102 
00103 static int atxfernoanswertimeout;
00104 
00105 static char *registrar = "res_features";        /*!< Registrar for operations */
00106 
00107 /* module and CLI command definitions */
00108 static char *synopsis = "Answer a parked call";
00109 
00110 static char *descrip = "ParkedCall(exten):"
00111 "Used to connect to a parked call.  This application is always\n"
00112 "registered internally and does not need to be explicitly added\n"
00113 "into the dialplan, although you should include the 'parkedcalls'\n"
00114 "context.\n";
00115 
00116 static char *parkcall = "Park";
00117 
00118 static char *synopsis2 = "Park yourself";
00119 
00120 static char *descrip2 = "Park():"
00121 "Used to park yourself (typically in combination with a supervised\n"
00122 "transfer to know the parking space). This application is always\n"
00123 "registered internally and does not need to be explicitly added\n"
00124 "into the dialplan, although you should include the 'parkedcalls'\n"
00125 "context (or the context specified in features.conf).\n\n"
00126 "If you set the PARKINGEXTEN variable to an extension in your\n"
00127 "parking context, park() will park the call on that extension, unless\n"
00128 "it already exists. In that case, execution will continue at next\n"
00129 "priority.\n" ;
00130 
00131 static struct ast_app *monitor_app = NULL;
00132 static int monitor_ok = 1;
00133 
00134 struct parkeduser {
00135    struct ast_channel *chan;                   /*!< Parking channel */
00136    struct timeval start;                       /*!< Time the parking started */
00137    int parkingnum;                             /*!< Parking lot */
00138    char parkingexten[AST_MAX_EXTENSION];       /*!< If set beforehand, parking extension used for this call */
00139    char context[AST_MAX_CONTEXT];              /*!< Where to go if our parking time expires */
00140    char exten[AST_MAX_EXTENSION];
00141    int priority;
00142    int parkingtime;                            /*!< Maximum length in parking lot before return */
00143    int notquiteyet;
00144    char peername[1024];
00145    unsigned char moh_trys;
00146    struct parkeduser *next;
00147 };
00148 
00149 static struct parkeduser *parkinglot;
00150 
00151 AST_MUTEX_DEFINE_STATIC(parking_lock); /*!< protects all static variables above */
00152 
00153 static pthread_t parking_thread;
00154 
00155 char *ast_parking_ext(void)
00156 {
00157    return parking_ext;
00158 }
00159 
00160 char *ast_pickup_ext(void)
00161 {
00162    return pickup_ext;
00163 }
00164 
00165 struct ast_bridge_thread_obj 
00166 {
00167    struct ast_bridge_config bconfig;
00168    struct ast_channel *chan;
00169    struct ast_channel *peer;
00170 };
00171 
00172 
00173 
00174 /*! \brief store context, priority and extension */
00175 static void set_c_e_p(struct ast_channel *chan, const char *context, const char *ext, int pri)
00176 {
00177    ast_copy_string(chan->context, context, sizeof(chan->context));
00178    ast_copy_string(chan->exten, ext, sizeof(chan->exten));
00179    chan->priority = pri;
00180 }
00181 
00182 static void check_goto_on_transfer(struct ast_channel *chan) 
00183 {
00184    struct ast_channel *xferchan;
00185    const char *val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR");
00186    char *x, *goto_on_transfer;
00187    struct ast_frame *f;
00188 
00189    if (ast_strlen_zero(val))
00190       return;
00191 
00192    goto_on_transfer = ast_strdupa(val);
00193 
00194    if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, chan->name)))
00195       return;
00196 
00197    for (x = goto_on_transfer; x && *x; x++) {
00198       if (*x == '^')
00199          *x = '|';
00200    }
00201    /* Make formats okay */
00202    xferchan->readformat = chan->readformat;
00203    xferchan->writeformat = chan->writeformat;
00204    ast_channel_masquerade(xferchan, chan);
00205    ast_parseable_goto(xferchan, goto_on_transfer);
00206    xferchan->_state = AST_STATE_UP;
00207    ast_clear_flag(xferchan, AST_FLAGS_ALL);  
00208    xferchan->_softhangup = 0;
00209    if ((f = ast_read(xferchan))) {
00210       ast_frfree(f);
00211       f = NULL;
00212       ast_pbx_start(xferchan);
00213    } else {
00214       ast_hangup(xferchan);
00215    }
00216 }
00217 
00218 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);
00219 
00220 
00221 static void *ast_bridge_call_thread(void *data) 
00222 {
00223    struct ast_bridge_thread_obj *tobj = data;
00224 
00225    tobj->chan->appl = "Transferred Call";
00226    tobj->chan->data = tobj->peer->name;
00227    tobj->peer->appl = "Transferred Call";
00228    tobj->peer->data = tobj->chan->name;
00229    if (tobj->chan->cdr) {
00230       ast_cdr_reset(tobj->chan->cdr, NULL);
00231       ast_cdr_setdestchan(tobj->chan->cdr, tobj->peer->name);
00232    }
00233    if (tobj->peer->cdr) {
00234       ast_cdr_reset(tobj->peer->cdr, NULL);
00235       ast_cdr_setdestchan(tobj->peer->cdr, tobj->chan->name);
00236    }
00237 
00238    ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
00239    ast_hangup(tobj->chan);
00240    ast_hangup(tobj->peer);
00241    bzero(tobj, sizeof(*tobj)); /*! \todo XXX for safety */
00242    free(tobj);
00243    return NULL;
00244 }
00245 
00246 static void ast_bridge_call_thread_launch(void *data) 
00247 {
00248    pthread_t thread;
00249    pthread_attr_t attr;
00250    struct sched_param sched;
00251 
00252    pthread_attr_init(&attr);
00253    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00254    ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data);
00255    pthread_attr_destroy(&attr);
00256    memset(&sched, 0, sizeof(sched));
00257    pthread_setschedparam(thread, SCHED_RR, &sched);
00258 }
00259 
00260 static int adsi_announce_park(struct ast_channel *chan, char *parkingexten)
00261 {
00262    int res;
00263    int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
00264    char tmp[256];
00265    char *message[5] = {NULL, NULL, NULL, NULL, NULL};
00266 
00267    snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten);
00268    message[0] = tmp;
00269    res = ast_adsi_load_session(chan, NULL, 0, 1);
00270    if (res == -1)
00271       return res;
00272    return ast_adsi_print(chan, message, justify, 1);
00273 }
00274 
00275 /*! \brief Notify metermaids that we've changed an extension */
00276 static void notify_metermaids(char *exten, char *context)
00277 {
00278    if (option_debug > 3)
00279       ast_log(LOG_DEBUG, "Notification of state change to metermaids %s@%s\n", exten, context);
00280 
00281    /* Send notification to devicestate subsystem */
00282    ast_device_state_changed("park:%s@%s", exten, context);
00283    return;
00284 }
00285 
00286 /*! \brief metermaids callback from devicestate.c */
00287 static int metermaidstate(const char *data)
00288 {
00289    int res = AST_DEVICE_INVALID;
00290    char *context = ast_strdupa(data);
00291    char *exten;
00292 
00293    exten = strsep(&context, "@");
00294    if (!context)
00295       return res;
00296    
00297    if (option_debug > 3)
00298       ast_log(LOG_DEBUG, "Checking state of exten %s in context %s\n", exten, context);
00299 
00300    res = ast_exists_extension(NULL, context, exten, 1, NULL);
00301 
00302    if (!res)
00303       return AST_DEVICE_NOT_INUSE;
00304    else
00305       return AST_DEVICE_INUSE;
00306 }
00307 
00308 /*! \brief Park a call 
00309    \note We put the user in the parking list, then wake up the parking thread to be sure it looks
00310    after these channels too */
00311 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
00312 {
00313    struct parkeduser *pu, *cur;
00314    int i, x = -1, parking_range;
00315    struct ast_context *con;
00316    const char *parkingexten;
00317    
00318    /* Allocate memory for parking data */
00319    if (!(pu = ast_calloc(1, sizeof(*pu)))) 
00320       return -1;
00321 
00322    /* Lock parking lot */
00323    ast_mutex_lock(&parking_lock);
00324    /* Check for channel variable PARKINGEXTEN */
00325    parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN");
00326    if (!ast_strlen_zero(parkingexten)) {
00327       if (ast_exists_extension(NULL, parking_con, parkingexten, 1, NULL)) {
00328          ast_mutex_unlock(&parking_lock);
00329          free(pu);
00330          ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con);
00331          return -1; /* We failed to park this call, plain and simple so we need to error out */
00332       }
00333       ast_copy_string(pu->parkingexten, parkingexten, sizeof(pu->parkingexten));
00334       x = atoi(parkingexten);
00335    } else {
00336       /* Select parking space within range */
00337       parking_range = parking_stop - parking_start+1;
00338       for (i = 0; i < parking_range; i++) {
00339          x = (i + parking_offset) % parking_range + parking_start;
00340          cur = parkinglot;
00341          while(cur) {
00342             if (cur->parkingnum == x) 
00343                break;
00344             cur = cur->next;
00345          }
00346          if (!cur)
00347             break;
00348       }
00349 
00350       if (!(i < parking_range)) {
00351          ast_log(LOG_WARNING, "No more parking spaces\n");
00352          free(pu);
00353          ast_mutex_unlock(&parking_lock);
00354          return -1;
00355       }
00356       /* Set pointer for next parking */
00357       if (parkfindnext) 
00358          parking_offset = x - parking_start + 1;
00359    }
00360    
00361    chan->appl = "Parked Call";
00362    chan->data = NULL; 
00363 
00364    pu->chan = chan;
00365    
00366    /* Put the parked channel on hold if we have two different channels */
00367    if (chan != peer) {
00368       ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
00369          S_OR(parkmohclass, NULL),
00370          !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
00371    }
00372    
00373    pu->start = ast_tvnow();
00374    pu->parkingnum = x;
00375    pu->parkingtime = (timeout > 0) ? timeout : parkingtime;
00376    if (extout)
00377       *extout = x;
00378 
00379    if (peer) 
00380       ast_copy_string(pu->peername, peer->name, sizeof(pu->peername));
00381 
00382    /* Remember what had been dialed, so that if the parking
00383       expires, we try to come back to the same place */
00384    ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context));
00385    ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten));
00386    pu->priority = chan->macropriority ? chan->macropriority : chan->priority;
00387    pu->next = parkinglot;
00388    parkinglot = pu;
00389 
00390    /* If parking a channel directly, don't quiet yet get parking running on it */
00391    if (peer == chan) 
00392       pu->notquiteyet = 1;
00393    ast_mutex_unlock(&parking_lock);
00394    /* Wake up the (presumably select()ing) thread */
00395    pthread_kill(parking_thread, SIGURG);
00396    if (option_verbose > 1) 
00397       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));
00398 
00399    if (pu->parkingnum != -1)
00400       snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x);
00401    manager_event(EVENT_FLAG_CALL, "ParkedCall",
00402       "Exten: %s\r\n"
00403       "Channel: %s\r\n"
00404       "From: %s\r\n"
00405       "Timeout: %ld\r\n"
00406       "CallerID: %s\r\n"
00407       "CallerIDName: %s\r\n",
00408       pu->parkingexten, pu->chan->name, peer ? peer->name : "",
00409       (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
00410       S_OR(pu->chan->cid.cid_num, "<unknown>"),
00411       S_OR(pu->chan->cid.cid_name, "<unknown>")
00412       );
00413 
00414    if (peer && adsipark && ast_adsi_available(peer)) {
00415       adsi_announce_park(peer, pu->parkingexten);  /* Only supports parking numbers */
00416       ast_adsi_unload_session(peer);
00417    }
00418 
00419    con = ast_context_find(parking_con);
00420    if (!con) 
00421       con = ast_context_create(NULL, parking_con, registrar);
00422    if (!con)   /* Still no context? Bad */
00423       ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
00424    /* Tell the peer channel the number of the parking space */
00425    if (peer && pu->parkingnum != -1) /* Only say number if it's a number */
00426       ast_say_digits(peer, pu->parkingnum, "", peer->language);
00427    if (con) {
00428       if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, strdup(pu->parkingexten), ast_free, registrar))
00429          notify_metermaids(pu->parkingexten, parking_con);
00430    }
00431    if (pu->notquiteyet) {
00432       /* Wake up parking thread if we're really done */
00433       ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
00434          S_OR(parkmohclass, NULL),
00435          !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
00436       pu->notquiteyet = 0;
00437       pthread_kill(parking_thread, SIGURG);
00438    }
00439    return 0;
00440 }
00441 
00442 int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
00443 {
00444    struct ast_channel *chan;
00445    struct ast_frame *f;
00446 
00447    /* Make a new, fake channel that we'll use to masquerade in the real one */
00448    if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->amaflags, "Parked/%s",rchan->name))) {
00449       ast_log(LOG_WARNING, "Unable to create parked channel\n");
00450       return -1;
00451    }
00452 
00453    /* Make formats okay */
00454    chan->readformat = rchan->readformat;
00455    chan->writeformat = rchan->writeformat;
00456    ast_channel_masquerade(chan, rchan);
00457 
00458    /* Setup the extensions and such */
00459    set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority);
00460 
00461    /* Make the masq execute */
00462    f = ast_read(chan);
00463    if (f)
00464       ast_frfree(f);
00465 
00466    ast_park_call(chan, peer, timeout, extout);
00467    return 0;
00468 }
00469 
00470 
00471 #define FEATURE_RETURN_HANGUP    -1
00472 #define FEATURE_RETURN_SUCCESSBREAK  0
00473 #define FEATURE_RETURN_PBX_KEEPALIVE   AST_PBX_KEEPALIVE
00474 #define FEATURE_RETURN_NO_HANGUP_PEER  AST_PBX_NO_HANGUP_PEER
00475 #define FEATURE_RETURN_PASSDIGITS    21
00476 #define FEATURE_RETURN_STOREDIGITS   22
00477 #define FEATURE_RETURN_SUCCESS       23
00478 
00479 #define FEATURE_SENSE_CHAN (1 << 0)
00480 #define FEATURE_SENSE_PEER (1 << 1)
00481 
00482 /*! \brief
00483  * set caller and callee according to the direction
00484  */
00485 static void set_peers(struct ast_channel **caller, struct ast_channel **callee,
00486    struct ast_channel *peer, struct ast_channel *chan, int sense)
00487 {
00488    if (sense == FEATURE_SENSE_PEER) {
00489       *caller = peer;
00490       *callee = chan;
00491    } else {
00492       *callee = peer;
00493       *caller = chan;
00494    }
00495 }
00496 
00497 /*! \brief support routing for one touch call parking */
00498 static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
00499 {
00500    struct ast_channel *parker;
00501         struct ast_channel *parkee;
00502    int res = 0;
00503    struct ast_module_user *u;
00504 
00505    u = ast_module_user_add(chan);
00506 
00507    set_peers(&parker, &parkee, peer, chan, sense);
00508    /* Setup the exten/priority to be s/1 since we don't know
00509       where this call should return */
00510    strcpy(chan->exten, "s");
00511    chan->priority = 1;
00512    if (chan->_state != AST_STATE_UP)
00513       res = ast_answer(chan);
00514    if (!res)
00515       res = ast_safe_sleep(chan, 1000);
00516    if (!res)
00517       res = ast_park_call(parkee, parker, 0, NULL);
00518 
00519    ast_module_user_remove(u);
00520 
00521    if (!res) {
00522       if (sense == FEATURE_SENSE_CHAN)
00523          res = AST_PBX_NO_HANGUP_PEER;
00524       else
00525          res = AST_PBX_KEEPALIVE;
00526    }
00527    return res;
00528 
00529 }
00530 
00531 static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
00532 {
00533    char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
00534    int x = 0;
00535    size_t len;
00536    struct ast_channel *caller_chan, *callee_chan;
00537 
00538    if (!monitor_ok) {
00539       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
00540       return -1;
00541    }
00542 
00543    if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) {
00544       monitor_ok = 0;
00545       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
00546       return -1;
00547    }
00548 
00549    set_peers(&caller_chan, &callee_chan, peer, chan, sense);
00550 
00551    if (!ast_strlen_zero(courtesytone)) {
00552       if (ast_autoservice_start(callee_chan))
00553          return -1;
00554       if (ast_stream_and_wait(caller_chan, courtesytone, caller_chan->language, "")) {
00555          ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
00556          ast_autoservice_stop(callee_chan);
00557          return -1;
00558       }
00559       if (ast_autoservice_stop(callee_chan))
00560          return -1;
00561    }
00562    
00563    if (callee_chan->monitor) {
00564       if (option_verbose > 3)
00565          ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code);
00566       ast_monitor_stop(callee_chan, 1);
00567       return FEATURE_RETURN_SUCCESS;
00568    }
00569 
00570    if (caller_chan && callee_chan) {
00571       const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT");
00572       const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
00573 
00574       if (!touch_format)
00575          touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT");
00576 
00577       if (!touch_monitor)
00578          touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
00579    
00580       if (touch_monitor) {
00581          len = strlen(touch_monitor) + 50;
00582          args = alloca(len);
00583          touch_filename = alloca(len);
00584          snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
00585          snprintf(args, len, "%s|%s|m", (touch_format) ? touch_format : "wav", touch_filename);
00586       } else {
00587          caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name));
00588          callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name));
00589          len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
00590          args = alloca(len);
00591          touch_filename = alloca(len);
00592          snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
00593          snprintf(args, len, "%s|%s|m", S_OR(touch_format, "wav"), touch_filename);
00594       }
00595 
00596       for( x = 0; x < strlen(args); x++) {
00597          if (args[x] == '/')
00598             args[x] = '-';
00599       }
00600       
00601       if (option_verbose > 3)
00602          ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, args);
00603 
00604       pbx_exec(callee_chan, monitor_app, args);
00605       pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
00606       pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
00607    
00608       return FEATURE_RETURN_SUCCESS;
00609    }
00610    
00611    ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");  
00612    return -1;
00613 }
00614 
00615 static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
00616 {
00617    if (option_verbose > 3)
00618       ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code);
00619    return FEATURE_RETURN_HANGUP;
00620 }
00621 
00622 static int finishup(struct ast_channel *chan)
00623 {
00624         ast_indicate(chan, AST_CONTROL_UNHOLD);
00625   
00626         return ast_autoservice_stop(chan);
00627 }
00628 
00629 /*! \brief Find the context for the transfer */
00630 static const char *real_ctx(struct ast_channel *transferer, struct ast_channel *transferee)
00631 {
00632         const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
00633         if (ast_strlen_zero(s))
00634                 s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
00635         if (ast_strlen_zero(s)) /* Use the non-macro context to transfer the call XXX ? */
00636                 s = transferer->macrocontext;
00637         if (ast_strlen_zero(s))
00638                 s = transferer->context;
00639         return s;  
00640 }
00641 
00642 static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
00643 {
00644    struct ast_channel *transferer;
00645    struct ast_channel *transferee;
00646    const char *transferer_real_context;
00647    char xferto[256];
00648    int res;
00649 
00650    set_peers(&transferer, &transferee, peer, chan, sense);
00651    transferer_real_context = real_ctx(transferer, transferee);
00652    /* Start autoservice on chan while we talk to the originator */
00653    ast_autoservice_start(transferee);
00654    ast_indicate(transferee, AST_CONTROL_HOLD);
00655 
00656    memset(xferto, 0, sizeof(xferto));
00657    
00658    /* Transfer */
00659    res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
00660    if (res < 0) {
00661       finishup(transferee);
00662       return -1; /* error ? */
00663    }
00664    if (res > 0)   /* If they've typed a digit already, handle it */
00665       xferto[0] = (char) res;
00666 
00667    ast_stopstream(transferer);
00668    res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
00669    if (res < 0) {  /* hangup, would be 0 for invalid and 1 for valid */
00670       finishup(transferee);
00671       return res;
00672    }
00673    if (!strcmp(xferto, ast_parking_ext())) {
00674       res = finishup(transferee);
00675       if (res)
00676          res = -1;
00677       else if (!ast_park_call(transferee, transferer, 0, NULL)) { /* success */
00678          /* We return non-zero, but tell the PBX not to hang the channel when
00679             the thread dies -- We have to be careful now though.  We are responsible for 
00680             hanging up the channel, else it will never be hung up! */
00681 
00682          return (transferer == peer) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER;
00683       } else {
00684          ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
00685       }
00686       /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */
00687    } else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
00688       pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", transferee->name);
00689       pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
00690       res=finishup(transferee);
00691       if (!transferer->cdr) {
00692          transferer->cdr=ast_cdr_alloc();
00693          if (transferer) {
00694             ast_cdr_init(transferer->cdr, transferer); /* initilize our channel's cdr */
00695             ast_cdr_start(transferer->cdr);
00696          }
00697       }
00698       if (transferer->cdr) {
00699          ast_cdr_setdestchan(transferer->cdr, transferee->name);
00700          ast_cdr_setapp(transferer->cdr, "BLINDTRANSFER","");
00701       }
00702       if (!transferee->pbx)
00703          res = -1;
00704       
00705       if (option_verbose > 2) 
00706          ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
00707                 ,transferee->name, xferto, transferer_real_context);
00708       if (ast_async_goto(transferee, transferer_real_context, xferto, 1))
00709          ast_log(LOG_WARNING, "Async goto failed :-(\n");
00710       check_goto_on_transfer(transferer);
00711       return res;
00712    } else {
00713       if (option_verbose > 2) 
00714          ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context);
00715    }
00716    if (ast_stream_and_wait(transferer, xferfailsound, transferer->language, AST_DIGIT_ANY) < 0 ) {
00717       finishup(transferee);
00718       return -1;
00719    }
00720    ast_stopstream(transferer);
00721    res = finishup(transferee);
00722    if (res) {
00723       if (option_verbose > 1)
00724          ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
00725       return res;
00726    }
00727    return FEATURE_RETURN_SUCCESS;
00728 }
00729 
00730 static int check_compat(struct ast_channel *c, struct ast_channel *newchan)
00731 {
00732    if (ast_channel_make_compatible(c, newchan) < 0) {
00733       ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
00734          c->name, newchan->name);
00735       ast_hangup(newchan);
00736       return -1;
00737    }
00738    return 0;
00739 }
00740 
00741 static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
00742 {
00743    struct ast_channel *transferer;
00744    struct ast_channel *transferee;
00745    const char *transferer_real_context;
00746    char xferto[256] = "";
00747    int res;
00748    int outstate=0;
00749    struct ast_channel *newchan;
00750    struct ast_channel *xferchan;
00751    struct ast_bridge_thread_obj *tobj;
00752    struct ast_bridge_config bconfig;
00753    struct ast_frame *f;
00754    int l;
00755 
00756    if (option_debug)
00757       ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense);
00758    set_peers(&transferer, &transferee, peer, chan, sense);
00759         transferer_real_context = real_ctx(transferer, transferee);
00760    /* Start autoservice on chan while we talk to the originator */
00761    ast_autoservice_start(transferee);
00762    ast_indicate(transferee, AST_CONTROL_HOLD);
00763    
00764    /* Transfer */
00765    res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
00766    if (res < 0) {
00767       finishup(transferee);
00768       return res;
00769    }
00770    if (res > 0) /* If they've typed a digit already, handle it */
00771       xferto[0] = (char) res;
00772 
00773    /* this is specific of atxfer */
00774    res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
00775         if (res < 0) {  /* hangup, would be 0 for invalid and 1 for valid */
00776                 finishup(transferee);
00777                 return res;
00778         }
00779    if (res == 0) {
00780       ast_log(LOG_WARNING, "Did not read data.\n");
00781       finishup(transferee);
00782       if (ast_stream_and_wait(transferer, "beeperr", transferer->language, ""))
00783          return -1;
00784       return FEATURE_RETURN_SUCCESS;
00785    }
00786 
00787    /* valid extension, res == 1 */
00788    if (!ast_exists_extension(transferer, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
00789       ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context);
00790       finishup(transferee);
00791       if (ast_stream_and_wait(transferer, "beeperr", transferer->language, ""))
00792          return -1;
00793       return FEATURE_RETURN_SUCCESS;
00794    }
00795 
00796    l = strlen(xferto);
00797    snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context);   /* append context */
00798    newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats),
00799       xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name);
00800    ast_indicate(transferer, -1);
00801    if (!newchan) {
00802       finishup(transferee);
00803       /* any reason besides user requested cancel and busy triggers the failed sound */
00804       if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY &&
00805             ast_stream_and_wait(transferer, xferfailsound, transferer->language, ""))
00806          return -1;
00807       return FEATURE_RETURN_SUCCESS;
00808    }
00809 
00810    if (check_compat(transferer, newchan))
00811       return -1;
00812    memset(&bconfig,0,sizeof(struct ast_bridge_config));
00813    ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
00814    ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
00815    res = ast_bridge_call(transferer, newchan, &bconfig);
00816    if (newchan->_softhangup || !transferer->_softhangup) {
00817       ast_hangup(newchan);
00818       if (ast_stream_and_wait(transferer, xfersound, transferer->language, ""))
00819          ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
00820       finishup(transferee);
00821       transferer->_softhangup = 0;
00822       return FEATURE_RETURN_SUCCESS;
00823    }
00824    
00825    if (check_compat(transferee, newchan))
00826       return -1;
00827 
00828    ast_indicate(transferee, AST_CONTROL_UNHOLD);
00829    
00830    if ((ast_autoservice_stop(transferee) < 0)
00831       || (ast_waitfordigit(transferee, 100) < 0)
00832       || (ast_waitfordigit(newchan, 100) < 0) 
00833       || ast_check_hangup(transferee) 
00834       || ast_check_hangup(newchan)) {
00835       ast_hangup(newchan);
00836       return -1;
00837    }
00838 
00839    xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
00840    if (!xferchan) {
00841       ast_hangup(newchan);
00842       return -1;
00843    }
00844    /* Make formats okay */
00845    xferchan->readformat = transferee->readformat;
00846    xferchan->writeformat = transferee->writeformat;
00847    ast_channel_masquerade(xferchan, transferee);
00848    ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
00849    xferchan->_state = AST_STATE_UP;
00850    ast_clear_flag(xferchan, AST_FLAGS_ALL);  
00851    xferchan->_softhangup = 0;
00852 
00853    if ((f = ast_read(xferchan)))
00854       ast_frfree(f);
00855 
00856    newchan->_state = AST_STATE_UP;
00857    ast_clear_flag(newchan, AST_FLAGS_ALL);   
00858    newchan->_softhangup = 0;
00859 
00860    tobj = ast_calloc(1, sizeof(struct ast_bridge_thread_obj));
00861    if (!tobj) {
00862       ast_hangup(xferchan);
00863       ast_hangup(newchan);
00864       return -1;
00865    }
00866    tobj->chan = xferchan;
00867    tobj->peer = newchan;
00868    tobj->bconfig = *config;
00869 
00870    if (ast_stream_and_wait(newchan, xfersound, newchan->language, ""))
00871       ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
00872    ast_bridge_call_thread_launch(tobj);
00873    return -1;  /* XXX meaning the channel is bridged ? */
00874 }
00875 
00876 
00877 /* add atxfer and automon as undefined so you can only use em if you configure them */
00878 #define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
00879 
00880 AST_RWLOCK_DEFINE_STATIC(features_lock);
00881 
00882 static struct ast_call_feature builtin_features[] = 
00883  {
00884    { AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF, "" },
00885    { AST_FEATURE_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF, "" },
00886    { AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF, "" },
00887    { AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF, "" },
00888    { AST_FEATURE_PARKCALL, "Park Call", "parkcall", "", "", builtin_parkcall, AST_FEATURE_FLAG_NEEDSDTMF, "" },
00889 };
00890 
00891 
00892 static AST_LIST_HEAD_STATIC(feature_list,ast_call_feature);
00893 
00894 /*! \brief register new feature into feature_list*/
00895 void ast_register_feature(struct ast_call_feature *feature)
00896 {
00897    if (!feature) {
00898       ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
00899          return;
00900    }
00901   
00902    AST_LIST_LOCK(&feature_list);
00903    AST_LIST_INSERT_HEAD(&feature_list,feature,feature_entry);
00904    AST_LIST_UNLOCK(&feature_list);
00905 
00906    if (option_verbose >= 2) 
00907       ast_verbose(VERBOSE_PREFIX_2 "Registered Feature '%s'\n",feature->sname);
00908 }
00909 
00910 /*! \brief unregister feature from feature_list */
00911 void ast_unregister_feature(struct ast_call_feature *feature)
00912 {
00913    if (!feature)
00914       return;
00915 
00916    AST_LIST_LOCK(&feature_list);
00917    AST_LIST_REMOVE(&feature_list,feature,feature_entry);
00918    AST_LIST_UNLOCK(&feature_list);
00919    free(feature);
00920 }
00921 
00922 /*! \brief Remove all features in the list */
00923 static void ast_unregister_features(void)
00924 {
00925    struct ast_call_feature *feature;
00926 
00927    AST_LIST_LOCK(&feature_list);
00928    while ((feature = AST_LIST_REMOVE_HEAD(&feature_list,feature_entry)))
00929       free(feature);
00930    AST_LIST_UNLOCK(&feature_list);
00931 }
00932 
00933 /*! \brief find a feature by name */
00934 static struct ast_call_feature *find_dynamic_feature(const char *name)
00935 {
00936    struct ast_call_feature *tmp;
00937 
00938    AST_LIST_TRAVERSE(&feature_list, tmp, feature_entry) {
00939       if (!strcasecmp(tmp->sname, name))
00940          break;
00941    }
00942 
00943    return tmp;
00944 }
00945 
00946 /*! \brief exec an app by feature */
00947 static int feature_exec_app(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
00948 {
00949    struct ast_app *app;
00950    struct ast_call_feature *feature;
00951    struct ast_channel *work, *idle;
00952    int res;
00953 
00954    AST_LIST_LOCK(&feature_list);
00955    AST_LIST_TRAVERSE(&feature_list, feature, feature_entry) {
00956       if (!strcasecmp(feature->exten, code))
00957          break;
00958    }
00959    AST_LIST_UNLOCK(&feature_list);
00960 
00961    if (!feature) { /* shouldn't ever happen! */
00962       ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
00963       return -1; 
00964    }
00965 
00966    if (sense == FEATURE_SENSE_CHAN) {
00967       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
00968          return FEATURE_RETURN_PASSDIGITS;
00969       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
00970          work = chan;
00971          idle = peer;
00972       } else {
00973          work = peer;
00974          idle = chan;
00975       }
00976    } else {
00977       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
00978          return FEATURE_RETURN_PASSDIGITS;
00979       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
00980          work = peer;
00981          idle = chan;
00982       } else {
00983          work = chan;
00984          idle = peer;
00985       }
00986    }
00987 
00988    if (!(app = pbx_findapp(feature->app))) {
00989       ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
00990       return -2;
00991    }
00992 
00993    ast_autoservice_start(idle);
00994    
00995    if (!ast_strlen_zero(feature->moh_class))
00996       ast_moh_start(idle, feature->moh_class, NULL);
00997 
00998    res = pbx_exec(work, app, feature->app_args);
00999 
01000    if (!ast_strlen_zero(feature->moh_class))
01001       ast_moh_stop(idle);
01002 
01003    ast_autoservice_stop(idle);
01004 
01005    if (res == AST_PBX_KEEPALIVE)
01006       return FEATURE_RETURN_PBX_KEEPALIVE;
01007    else if (res == AST_PBX_NO_HANGUP_PEER)
01008       return FEATURE_RETURN_NO_HANGUP_PEER;
01009    else if (res)
01010       return FEATURE_RETURN_SUCCESSBREAK;
01011    
01012    return FEATURE_RETURN_SUCCESS;   /*! \todo XXX should probably return res */
01013 }
01014 
01015 static void unmap_features(void)
01016 {
01017    int x;
01018 
01019    ast_rwlock_wrlock(&features_lock);
01020    for (x = 0; x < FEATURES_COUNT; x++)
01021       strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
01022    ast_rwlock_unlock(&features_lock);
01023 }
01024 
01025 static int remap_feature(const char *name, const char *value)
01026 {
01027    int x, res = -1;
01028 
01029    ast_rwlock_wrlock(&features_lock);
01030    for (x = 0; x < FEATURES_COUNT; x++) {
01031       if (strcasecmp(builtin_features[x].sname, name))
01032          continue;
01033 
01034       ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten));
01035       res = 0;
01036       break;
01037    }
01038    ast_rwlock_unlock(&features_lock);
01039 
01040    return res;
01041 }
01042 
01043 static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
01044 {
01045    int x;
01046    struct ast_flags features;
01047    int res = FEATURE_RETURN_PASSDIGITS;
01048    struct ast_call_feature *feature;
01049    const char *dynamic_features=pbx_builtin_getvar_helper(chan,"DYNAMIC_FEATURES");
01050    char *tmp, *tok;
01051 
01052    if (sense == FEATURE_SENSE_CHAN)
01053       ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);   
01054    else
01055       ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);   
01056    if (option_debug > 2)
01057       ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, sense=%d, features=%d\n", chan->name, peer->name, sense, features.flags);
01058 
01059    ast_rwlock_rdlock(&features_lock);
01060    for (x = 0; x < FEATURES_COUNT; x++) {
01061       if ((ast_test_flag(&features, builtin_features[x].feature_mask)) &&
01062           !ast_strlen_zero(builtin_features[x].exten)) {
01063          /* Feature is up for consideration */
01064          if (!strcmp(builtin_features[x].exten, code)) {
01065             res = builtin_features[x].operation(chan, peer, config, code, sense);
01066             break;
01067          } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
01068             if (res == FEATURE_RETURN_PASSDIGITS)
01069                res = FEATURE_RETURN_STOREDIGITS;
01070          }
01071       }
01072    }
01073    ast_rwlock_unlock(&features_lock);
01074 
01075    if (ast_strlen_zero(dynamic_features))
01076       return res;
01077 
01078    tmp = ast_strdupa(dynamic_features);
01079 
01080    while ((tok = strsep(&tmp, "#"))) {
01081       AST_LIST_LOCK(&feature_list); 
01082       if (!(feature = find_dynamic_feature(tok))) {
01083          AST_LIST_UNLOCK(&feature_list);
01084          continue;
01085       }
01086          
01087       /* Feature is up for consideration */
01088       if (!strcmp(feature->exten, code)) {
01089          if (option_verbose > 2)
01090             ast_verbose(VERBOSE_PREFIX_3 " Feature Found: %s exten: %s\n",feature->sname, tok);
01091          res = feature->operation(chan, peer, config, code, sense);
01092          AST_LIST_UNLOCK(&feature_list);
01093          break;
01094       } else if (!strncmp(feature->exten, code, strlen(code)))
01095          res = FEATURE_RETURN_STOREDIGITS;
01096 
01097       AST_LIST_UNLOCK(&feature_list);
01098    }
01099    
01100    return res;
01101 }
01102 
01103 static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
01104 {
01105    int x;
01106    
01107    ast_clear_flag(config, AST_FLAGS_ALL);
01108 
01109    ast_rwlock_rdlock(&features_lock);
01110    for (x = 0; x < FEATURES_COUNT; x++) {
01111       if (!ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
01112          continue;
01113 
01114       if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask))
01115          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
01116 
01117       if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask))
01118          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
01119    }
01120    ast_rwlock_unlock(&features_lock);
01121    
01122    if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) {
01123       const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
01124 
01125       if (dynamic_features) {
01126          char *tmp = ast_strdupa(dynamic_features);
01127          char *tok;
01128          struct ast_call_feature *feature;
01129 
01130          /* while we have a feature */
01131          while ((tok = strsep(&tmp, "#"))) {
01132             AST_LIST_LOCK(&feature_list);
01133             if ((feature = find_dynamic_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
01134                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
01135                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
01136                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
01137                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
01138             }
01139             AST_LIST_UNLOCK(&feature_list);
01140          }
01141       }
01142    }
01143 }
01144 
01145 /*! \todo XXX Check - this is very similar to the code in channel.c */
01146 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)
01147 {
01148    int state = 0;
01149    int cause = 0;
01150    int to;
01151    struct ast_channel *chan;
01152    struct ast_channel *monitor_chans[2];
01153    struct ast_channel *active_channel;
01154    int res = 0, ready = 0;
01155    
01156    if ((chan = ast_request(type, format, data, &cause))) {
01157       ast_set_callerid(chan, cid_num, cid_name, cid_num);
01158       ast_channel_inherit_variables(caller, chan); 
01159       pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", caller->name);
01160       if (!chan->cdr) {
01161          chan->cdr=ast_cdr_alloc();
01162          if (chan->cdr) {
01163             ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
01164             ast_cdr_start(chan->cdr);
01165          }
01166       }
01167          
01168       if (!ast_call(chan, data, timeout)) {
01169          struct timeval started;
01170          int x, len = 0;
01171          char *disconnect_code = NULL, *dialed_code = NULL;
01172 
01173          ast_indicate(caller, AST_CONTROL_RINGING);
01174          /* support dialing of the featuremap disconnect code while performing an attended tranfer */
01175          ast_rwlock_rdlock(&features_lock);
01176          for (x = 0; x < FEATURES_COUNT; x++) {
01177             if (strcasecmp(builtin_features[x].sname, "disconnect"))
01178                continue;
01179 
01180             disconnect_code = builtin_features[x].exten;
01181             len = strlen(disconnect_code) + 1;
01182             dialed_code = alloca(len);
01183             memset(dialed_code, 0, len);
01184             break;
01185          }
01186          ast_rwlock_unlock(&features_lock);
01187          x = 0;
01188          started = ast_tvnow();
01189          to = timeout;
01190          while (!ast_check_hangup(caller) && timeout && (chan->_state != AST_STATE_UP)) {
01191             struct ast_frame *f = NULL;
01192 
01193             monitor_chans[0] = caller;
01194             monitor_chans[1] = chan;
01195             active_channel = ast_waitfor_n(monitor_chans, 2, &to);
01196 
01197             /* see if the timeout has been violated */
01198             if(ast_tvdiff_ms(ast_tvnow(), started) > timeout) {
01199                state = AST_CONTROL_UNHOLD;
01200                ast_log(LOG_NOTICE, "We exceeded our AT-timeout\n");
01201                break; /*doh! timeout*/
01202             }
01203 
01204             if (!active_channel)
01205                continue;
01206 
01207             if (chan && (chan == active_channel)){
01208                f = ast_read(chan);
01209                if (f == NULL) { /*doh! where'd he go?*/
01210                   state = AST_CONTROL_HANGUP;
01211                   res = 0;
01212                   break;
01213                }
01214                
01215                if (f->frametype == AST_FRAME_CONTROL || f->frametype == AST_FRAME_DTMF || f->frametype == AST_FRAME_TEXT) {
01216                   if (f->subclass == AST_CONTROL_RINGING) {
01217                      state = f->subclass;
01218                      if (option_verbose > 2)
01219                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", chan->name);
01220                      ast_indicate(caller, AST_CONTROL_RINGING);
01221                   } else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) {
01222                      state = f->subclass;
01223                      if (option_verbose > 2)
01224                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", chan->name);
01225                      ast_indicate(caller, AST_CONTROL_BUSY);
01226                      ast_frfree(f);
01227                      f = NULL;
01228                      break;
01229                   } else if (f->subclass == AST_CONTROL_ANSWER) {
01230                      /* This is what we are hoping for */
01231                      state = f->subclass;
01232                      ast_frfree(f);
01233                      f = NULL;
01234                      ready=1;
01235                      break;
01236                   } else {
01237                      ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass);
01238                   }
01239                   /* else who cares */
01240                }
01241 
01242             } else if (caller && (active_channel == caller)) {
01243                f = ast_read(caller);
01244                if (f == NULL) { /*doh! where'd he go?*/
01245                   if (caller->_softhangup && !chan->_softhangup) {
01246                      /* make this a blind transfer */
01247                      ready = 1;
01248                      break;
01249                   }
01250                   state = AST_CONTROL_HANGUP;
01251                   res = 0;
01252                   break;
01253                }
01254                
01255                if (f->frametype == AST_FRAME_DTMF) {
01256                   dialed_code[x++] = f->subclass;
01257                   dialed_code[x] = '\0';
01258                   if (strlen(dialed_code) == len) {
01259                      x = 0;
01260                   } else if (x && strncmp(dialed_code, disconnect_code, x)) {
01261                      x = 0;
01262                      dialed_code[x] = '\0';
01263                   }
01264                   if (*dialed_code && !strcmp(dialed_code, disconnect_code)) {
01265                      /* Caller Canceled the call */
01266                      state = AST_CONTROL_UNHOLD;
01267                      ast_frfree(f);
01268                      f = NULL;
01269                      break;
01270                   }
01271                }
01272             }
01273             if (f)
01274                ast_frfree(f);
01275          } /* end while */
01276       } else
01277          ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
01278    } else {
01279       ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
01280       switch(cause) {
01281       case AST_CAUSE_BUSY:
01282          state = AST_CONTROL_BUSY;
01283          break;
01284       case AST_CAUSE_CONGESTION:
01285          state = AST_CONTROL_CONGESTION;
01286          break;
01287       }
01288    }
01289    
01290    ast_indicate(caller, -1);
01291    if (chan && ready) {
01292       if (chan->_state == AST_STATE_UP) 
01293          state = AST_CONTROL_ANSWER;
01294       res = 0;
01295    } else if(chan) {
01296       res = -1;
01297       ast_hangup(chan);
01298       chan = NULL;
01299    } else {
01300       res = -1;
01301    }
01302    
01303    if (outstate)
01304       *outstate = state;
01305 
01306    if (chan && res <= 0) {
01307       if (chan->cdr || (chan->cdr = ast_cdr_alloc())) {
01308          char tmp[256];
01309          ast_cdr_init(chan->cdr, chan);
01310          snprintf(tmp, 256, "%s/%s", type, (char *)data);
01311          ast_cdr_setapp(chan->cdr,"Dial",tmp);
01312          ast_cdr_update(chan);
01313          ast_cdr_start(chan->cdr);
01314          ast_cdr_end(chan->cdr);
01315          /* If the cause wasn't handled properly */
01316          if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
01317             ast_cdr_failed(chan->cdr);
01318       } else {
01319          ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
01320       }
01321    }
01322    
01323    return chan;
01324 }
01325 
01326 int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
01327 {
01328    /* Copy voice back and forth between the two channels.  Give the peer
01329       the ability to transfer calls with '#<extension' syntax. */
01330    struct ast_frame *f;
01331    struct ast_channel *who;
01332    char chan_featurecode[FEATURE_MAX_LEN + 1]="";
01333    char peer_featurecode[FEATURE_MAX_LEN + 1]="";
01334    int res;
01335    int diff;
01336    int hasfeatures=0;
01337    int hadfeatures=0;
01338    struct ast_option_header *aoh;
01339    struct ast_bridge_config backup_config;
01340    struct ast_cdr *bridge_cdr;
01341 
01342    memset(&backup_config, 0, sizeof(backup_config));
01343 
01344    config->start_time = ast_tvnow();
01345 
01346    if (chan && peer) {
01347       pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
01348       pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
01349    } else if (chan)
01350       pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
01351 
01352    if (monitor_ok) {
01353       const char *monitor_exec;
01354       struct ast_channel *src = NULL;
01355       if (!monitor_app) { 
01356          if (!(monitor_app = pbx_findapp("Monitor")))
01357             monitor_ok=0;
01358       }
01359       if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR"))) 
01360          src = chan;
01361       else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
01362          src = peer;
01363       if (monitor_app && src) {
01364          char *tmp = ast_strdupa(monitor_exec);
01365          pbx_exec(src, monitor_app, tmp);
01366       }
01367    }
01368    
01369    set_config_flags(chan, peer, config);
01370    config->firstpass = 1;
01371 
01372    /* Answer if need be */
01373    if (ast_answer(chan))
01374       return -1;
01375    peer->appl = "Bridged Call";
01376    peer->data = chan->name;
01377 
01378    /* copy the userfield from the B-leg to A-leg if applicable */
01379    if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) {
01380       char tmp[256];
01381       if (!ast_strlen_zero(chan->cdr->userfield)) {
01382          snprintf(tmp, sizeof(tmp), "%s;%s", chan->cdr->userfield, peer->cdr->userfield);
01383          ast_cdr_appenduserfield(chan, tmp);
01384       } else
01385          ast_cdr_setuserfield(chan, peer->cdr->userfield);
01386       /* free the peer's cdr without ast_cdr_free complaining */
01387       free(peer->cdr);
01388       peer->cdr = NULL;
01389    }
01390 
01391    for (;;) {
01392       struct ast_channel *other; /* used later */
01393 
01394       res = ast_channel_bridge(chan, peer, config, &f, &who);
01395 
01396       if (config->feature_timer) {
01397          /* Update time limit for next pass */
01398          diff = ast_tvdiff_ms(ast_tvnow(), config->start_time);
01399          config->feature_timer -= diff;
01400          if (hasfeatures) {
01401             /* Running on backup config, meaning a feature might be being
01402                activated, but that's no excuse to keep things going 
01403                indefinitely! */
01404             if (backup_config.feature_timer && ((backup_config.feature_timer -= diff) <= 0)) {
01405                if (option_debug)
01406                   ast_log(LOG_DEBUG, "Timed out, realtime this time!\n");
01407                config->feature_timer = 0;
01408                who = chan;
01409                if (f)
01410                   ast_frfree(f);
01411                f = NULL;
01412                res = 0;
01413             } else if (config->feature_timer <= 0) {
01414                /* Not *really* out of time, just out of time for
01415                   digits to come in for features. */
01416                if (option_debug)
01417                   ast_log(LOG_DEBUG, "Timed out for feature!\n");
01418                if (!ast_strlen_zero(peer_featurecode)) {
01419                   ast_dtmf_stream(chan, peer, peer_featurecode, 0);
01420                   memset(peer_featurecode, 0, sizeof(peer_featurecode));
01421                }
01422                if (!ast_strlen_zero(chan_featurecode)) {
01423                   ast_dtmf_stream(peer, chan, chan_featurecode, 0);
01424                   memset(chan_featurecode, 0, sizeof(chan_featurecode));
01425                }
01426                if (f)
01427                   ast_frfree(f);
01428                hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
01429                if (!hasfeatures) {
01430                   /* Restore original (possibly time modified) bridge config */
01431                   memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
01432                   memset(&backup_config, 0, sizeof(backup_config));
01433                }
01434                hadfeatures = hasfeatures;
01435                /* Continue as we were */
01436                continue;
01437             } else if (!f) {
01438                /* The bridge returned without a frame and there is a feature in progress.
01439                 * However, we don't think the feature has quite yet timed out, so just
01440                 * go back into the bridge. */
01441                continue;
01442             }
01443          } else {
01444             if (config->feature_timer <=0) {
01445                /* We ran out of time */
01446                config->feature_timer = 0;
01447                who = chan;
01448                if (f)
01449                   ast_frfree(f);
01450                f = NULL;
01451                res = 0;
01452             }
01453          }
01454       }
01455       if (res < 0) {
01456          ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
01457          return -1;
01458       }
01459       
01460       if (!f || (f->frametype == AST_FRAME_CONTROL &&
01461             (f->subclass == AST_CONTROL_HANGUP || f->subclass == AST_CONTROL_BUSY || 
01462                f->subclass == AST_CONTROL_CONGESTION ) ) ) {
01463          res = -1;
01464          break;
01465       }
01466       /* many things should be sent to the 'other' channel */
01467       other = (who == chan) ? peer : chan;
01468       if (f->frametype == AST_FRAME_CONTROL) {
01469          switch (f->subclass) {
01470          case AST_CONTROL_RINGING:
01471          case AST_CONTROL_FLASH:
01472          case -1:
01473             ast_indicate(other, f->subclass);
01474             break;
01475          case AST_CONTROL_HOLD:
01476          case AST_CONTROL_UNHOLD:
01477             ast_indicate_data(other, f->subclass, f->data, f->datalen);
01478             break;
01479          case AST_CONTROL_OPTION:
01480             aoh = f->data;
01481             /* Forward option Requests */
01482             if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
01483                ast_channel_setoption(other, ntohs(aoh->option), aoh->data, 
01484                   f->datalen - sizeof(struct ast_option_header), 0);
01485             }
01486             break;
01487          }
01488       } else if (f->frametype == AST_FRAME_DTMF_BEGIN) {
01489          /* eat it */
01490       } else if (f->frametype == AST_FRAME_DTMF) {
01491          char *featurecode;
01492          int sense;
01493 
01494          hadfeatures = hasfeatures;
01495          /* This cannot overrun because the longest feature is one shorter than our buffer */
01496          if (who == chan) {
01497             sense = FEATURE_SENSE_CHAN;
01498             featurecode = chan_featurecode;
01499          } else  {
01500             sense = FEATURE_SENSE_PEER;
01501             featurecode = peer_featurecode;
01502          }
01503          /*! append the event to featurecode. we rely on the string being zero-filled, and
01504           * not overflowing it. 
01505           * \todo XXX how do we guarantee the latter ?
01506           */
01507          featurecode[strlen(featurecode)] = f->subclass;
01508          /* Get rid of the frame before we start doing "stuff" with the channels */
01509          ast_frfree(f);
01510          f = NULL;
01511          config->feature_timer = backup_config.feature_timer;
01512          res = ast_feature_interpret(chan, peer, config, featurecode, sense);
01513          switch(res) {
01514          case FEATURE_RETURN_PASSDIGITS:
01515             ast_dtmf_stream(other, who, featurecode, 0);
01516             /* Fall through */
01517          case FEATURE_RETURN_SUCCESS:
01518             memset(featurecode, 0, sizeof(chan_featurecode));
01519             break;
01520          }
01521          if (res >= FEATURE_RETURN_PASSDIGITS) {
01522             res = 0;
01523          } else 
01524             break;
01525          hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
01526          if (hadfeatures && !hasfeatures) {
01527             /* Restore backup */
01528             memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
01529             memset(&backup_config, 0, sizeof(struct ast_bridge_config));
01530          } else if (hasfeatures) {
01531             if (!hadfeatures) {
01532                /* Backup configuration */
01533                memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
01534                /* Setup temporary config options */
01535                config->play_warning = 0;
01536                ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
01537                ast_clear_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
01538                config->warning_freq = 0;
01539                config->warning_sound = NULL;
01540                config->end_sound = NULL;
01541                config->start_sound = NULL;
01542                config->firstpass = 0;
01543             }
01544             config->start_time = ast_tvnow();
01545             config->feature_timer = featuredigittimeout;
01546             if (option_debug)
01547                ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->feature_timer);
01548          }
01549       }
01550       if (f)
01551          ast_frfree(f);
01552 
01553    }
01554 
01555    /* arrange the cdrs */
01556    bridge_cdr = ast_cdr_alloc();
01557    if (bridge_cdr) {
01558       if (chan->cdr && peer->cdr) { /* both of them? merge */
01559          ast_cdr_init(bridge_cdr,chan); /* seems more logicaller to use the  destination as a base, but, really, it's random */
01560          ast_cdr_start(bridge_cdr); /* now is the time to start */
01561 
01562          /* absorb the channel cdr */
01563          ast_cdr_merge(bridge_cdr, chan->cdr);
01564          if (!ast_test_flag(chan->cdr, AST_CDR_FLAG_LOCKED))
01565             ast_cdr_discard(chan->cdr); /* if locked cdrs are in chan, they are taken over in the merge */
01566          
01567          /* absorb the peer cdr */
01568          ast_cdr_merge(bridge_cdr, peer->cdr);
01569          if (ast_test_flag(peer->cdr, AST_CDR_FLAG_LOCKED))
01570             ast_cdr_discard(peer->cdr); /* if locked cdrs are in peer, they are taken over in the merge */
01571          
01572          peer->cdr = NULL;
01573          chan->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */
01574       } else if (chan->cdr) {
01575          /* take the cdr from the channel - literally */
01576          ast_cdr_init(bridge_cdr,chan);
01577          /* absorb this data */
01578          ast_cdr_merge(bridge_cdr, chan->cdr);
01579          if (!ast_test_flag(chan->cdr, AST_CDR_FLAG_LOCKED))
01580             ast_cdr_discard(chan->cdr); /* if locked cdrs are in chan, they are taken over in the merge */
01581          chan->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */
01582       } else if (peer->cdr) {
01583          /* take the cdr from the peer - literally */
01584          ast_cdr_init(bridge_cdr,peer);
01585          /* absorb this data */
01586          ast_cdr_merge(bridge_cdr, peer->cdr);
01587          if (!ast_test_flag(peer->cdr, AST_CDR_FLAG_LOCKED))
01588             ast_cdr_discard(peer->cdr); /* if locked cdrs are in chan, they are taken over in the merge */
01589          peer->cdr = NULL;
01590          peer->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */
01591       } else {
01592          /* make up a new cdr */
01593          ast_cdr_init(bridge_cdr,chan); /* eh, just pick one of them */
01594          chan->cdr = bridge_cdr; /*  */
01595       }
01596       if (ast_strlen_zero(bridge_cdr->dstchannel)) {
01597          if (strcmp(bridge_cdr->channel, peer->name) != 0)
01598             ast_cdr_setdestchan(bridge_cdr, peer->name);
01599          else
01600             ast_cdr_setdestchan(bridge_cdr, chan->name);
01601       }
01602    }
01603    return res;
01604 }
01605 
01606 static void post_manager_event(const char *s, char *parkingexten, struct ast_channel *chan)
01607 {
01608    manager_event(EVENT_FLAG_CALL, s,
01609       "Exten: %s\r\n"
01610       "Channel: %s\r\n"
01611       "CallerID: %s\r\n"
01612       "CallerIDName: %s\r\n\r\n",
01613       parkingexten, 
01614       chan->name,
01615       S_OR(chan->cid.cid_num, "<unknown>"),
01616       S_OR(chan->cid.cid_name, "<unknown>")
01617       );
01618 }
01619 
01620 /*! \brief Take care of parked calls and unpark them if needed */
01621 static void *do_parking_thread(void *ignore)
01622 {
01623    fd_set rfds, efds;   /* results from previous select, to be preserved across loops. */
01624    FD_ZERO(&rfds);
01625    FD_ZERO(&efds);
01626 
01627    for (;;) {
01628       struct parkeduser *pu, *pl, *pt = NULL;
01629       int ms = -1;   /* select timeout, uninitialized */
01630       int max = -1;  /* max fd, none there yet */
01631       fd_set nrfds, nefds; /* args for the next select */
01632       FD_ZERO(&nrfds);
01633       FD_ZERO(&nefds);
01634 
01635       ast_mutex_lock(&parking_lock);
01636       pl = NULL;
01637       pu = parkinglot;
01638       /* navigate the list with prev-cur pointers to support removals */
01639       while (pu) {
01640          struct ast_channel *chan = pu->chan;   /* shorthand */
01641          int tms;        /* timeout for this item */
01642          int x;          /* fd index in channel */
01643          struct ast_context *con;
01644 
01645          if (pu->notquiteyet) { /* Pretend this one isn't here yet */
01646             pl = pu;
01647             pu = pu->next;
01648             continue;
01649          }
01650          tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
01651          if (tms > pu->parkingtime) {
01652             ast_indicate(chan, AST_CONTROL_UNHOLD);
01653             /* Get chan, exten from derived kludge */
01654             if (pu->peername[0]) {
01655                char *peername = ast_strdupa(pu->peername);
01656                char *cp = strrchr(peername, '-');
01657                if (cp) 
01658                   *cp = 0;
01659                con = ast_context_find(parking_con_dial);
01660                if (!con) {
01661                   con = ast_context_create(NULL, parking_con_dial, registrar);
01662                   if (!con)
01663                      ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
01664                }
01665                if (con) {
01666                   char returnexten[AST_MAX_EXTENSION];
01667                   snprintf(returnexten, sizeof(returnexten), "%s||t", peername);
01668                   ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), ast_free, registrar);
01669                }
01670                set_c_e_p(chan, parking_con_dial, peername, 1);
01671             } else {
01672                /* They've been waiting too long, send them back to where they came.  Theoretically they
01673                   should have their original extensions and such, but we copy to be on the safe side */
01674                set_c_e_p(chan, pu->context, pu->exten, pu->priority);
01675             }
01676 
01677             post_manager_event("ParkedCallTimeOut", pu->parkingexten, chan);
01678 
01679             if (option_verbose > 1) 
01680                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);
01681             /* Start up the PBX, or hang them up */
01682             if (ast_pbx_start(chan))  {
01683                ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan->name);
01684                ast_hangup(chan);
01685             }
01686             /* And take them out of the parking lot */
01687             if (pl) 
01688                pl->next = pu->next;
01689             else
01690                parkinglot = pu->next;
01691             pt = pu;
01692             pu = pu->next;
01693             con = ast_context_find(parking_con);
01694             if (con) {
01695                if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL))
01696                   ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
01697                else
01698                   notify_metermaids(pt->parkingexten, parking_con);
01699             } else
01700                ast_log(LOG_WARNING, "Whoa, no parking context?\n");
01701             free(pt);
01702          } else { /* still within parking time, process descriptors */
01703             for (x = 0; x < AST_MAX_FDS; x++) {
01704                struct ast_frame *f;
01705 
01706                if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], &rfds) && !FD_ISSET(chan->fds[x], &efds)))
01707                   continue;   /* nothing on this descriptor */
01708 
01709                if (FD_ISSET(chan->fds[x], &efds))
01710                   ast_set_flag(chan, AST_FLAG_EXCEPTION);
01711                else
01712                   ast_clear_flag(chan, AST_FLAG_EXCEPTION);
01713                chan->fdno = x;
01714 
01715                /* See if they need servicing */
01716                f = ast_read(chan);
01717                if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass ==  AST_CONTROL_HANGUP)) {
01718                   if (f)
01719                      ast_frfree(f);
01720                   post_manager_event("ParkedCallGiveUp", pu->parkingexten, chan);
01721 
01722                   /* There's a problem, hang them up*/
01723                   if (option_verbose > 1) 
01724                      ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", chan->name);
01725                   ast_hangup(chan);
01726                   /* And take them out of the parking lot */
01727                   if (pl) 
01728                      pl->next = pu->next;
01729                   else
01730                      parkinglot = pu->next;
01731                   pt = pu;
01732                   pu = pu->next;
01733                   con = ast_context_find(parking_con);
01734                   if (con) {
01735                      if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL))
01736                         ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
01737                   else
01738                      notify_metermaids(pt->parkingexten, parking_con);
01739                   } else
01740                      ast_log(LOG_WARNING, "Whoa, no parking context?\n");
01741                   free(pt);
01742                   break;
01743                } else {
01744                   /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
01745                   ast_frfree(f);
01746                   if (pu->moh_trys < 3 && !chan->generatordata) {
01747                      if (option_debug)
01748                         ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source.  Restarting.\n");
01749                      ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
01750                         S_OR(parkmohclass, NULL),
01751                         !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
01752                      pu->moh_trys++;
01753                   }
01754                   goto std;   /*! \todo XXX Ick: jumping into an else statement??? XXX */
01755                }
01756 
01757             } /* end for */
01758             if (x >= AST_MAX_FDS) {
01759 std:              for (x=0; x<AST_MAX_FDS; x++) {  /* mark fds for next round */
01760                   if (chan->fds[x] > -1) {
01761                      FD_SET(chan->fds[x], &nrfds);
01762                      FD_SET(chan->fds[x], &nefds);
01763                      if (chan->fds[x] > max)
01764                         max = chan->fds[x];
01765                   }
01766                }
01767                /* Keep track of our shortest wait */
01768                if (tms < ms || ms < 0)
01769                   ms = tms;
01770                pl = pu;
01771                pu = pu->next;
01772             }
01773          }
01774       } /* end while */
01775       ast_mutex_unlock(&parking_lock);
01776       rfds = nrfds;
01777       efds = nefds;
01778       {
01779          struct timeval tv = ast_samp2tv(ms, 1000);
01780          /* Wait for something to happen */
01781          ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
01782       }
01783       pthread_testcancel();
01784    }
01785    return NULL;   /* Never reached */
01786 }
01787 
01788 /*! \brief Park a call */
01789 static int park_call_exec(struct ast_channel *chan, void *data)
01790 {
01791    /* Data is unused at the moment but could contain a parking
01792       lot context eventually */
01793    int res = 0;
01794    struct ast_module_user *u;
01795 
01796    u = ast_module_user_add(chan);
01797 
01798    /* Setup the exten/priority to be s/1 since we don't know
01799       where this call should return */
01800    strcpy(chan->exten, "s");
01801    chan->priority = 1;
01802    /* Answer if call is not up */
01803    if (chan->_state != AST_STATE_UP)
01804       res = ast_answer(chan);
01805    /* Sleep to allow VoIP streams to settle down */
01806    if (!res)
01807       res = ast_safe_sleep(chan, 1000);
01808    /* Park the call */
01809    if (!res)
01810       res = ast_park_call(chan, chan, 0, NULL);
01811 
01812    ast_module_user_remove(u);
01813 
01814    return !res ? AST_PBX_KEEPALIVE : res;
01815 }
01816 
01817 /*! \brief Pickup parked call */
01818 static int park_exec(struct ast_channel *chan, void *data)
01819 {
01820    int res = 0;
01821    struct ast_module_user *u;
01822    struct ast_channel *peer=NULL;
01823    struct parkeduser *pu, *pl=NULL;
01824    struct ast_context *con;
01825 
01826    int park;
01827    struct ast_bridge_config config;
01828 
01829    if (!data) {
01830       ast_log(LOG_WARNING, "Parkedcall requires an argument (extension number)\n");
01831       return -1;
01832    }
01833    
01834    u = ast_module_user_add(chan);
01835 
01836    park = atoi((char *)data);
01837    ast_mutex_lock(&parking_lock);
01838    pu = parkinglot;
01839    while(pu) {
01840       if (pu->parkingnum == park) {
01841          if (pl)
01842             pl->next = pu->next;
01843          else
01844             parkinglot = pu->next;
01845          break;
01846       }
01847       pl = pu;
01848       pu = pu->next;
01849    }
01850    ast_mutex_unlock(&parking_lock);
01851    if (pu) {
01852       peer = pu->chan;
01853       con = ast_context_find(parking_con);
01854       if (con) {
01855          if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
01856             ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
01857          else
01858             notify_metermaids(pu->parkingexten, parking_con);
01859       } else
01860          ast_log(LOG_WARNING, "Whoa, no parking context?\n");
01861 
01862       manager_event(EVENT_FLAG_CALL, "UnParkedCall",
01863          "Exten: %s\r\n"
01864          "Channel: %s\r\n"
01865          "From: %s\r\n"
01866          "CallerID: %s\r\n"
01867          "CallerIDName: %s\r\n",
01868          pu->parkingexten, pu->chan->name, chan->name,
01869          S_OR(pu->chan->cid.cid_num, "<unknown>"),
01870          S_OR(pu->chan->cid.cid_name, "<unknown>")
01871          );
01872 
01873       free(pu);
01874    }
01875    /* JK02: it helps to answer the channel if not already up */
01876    if (chan->_state != AST_STATE_UP)
01877       ast_answer(chan);
01878 
01879    if (peer) {
01880       /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
01881       
01882       if (!ast_strlen_zero(courtesytone)) {
01883          int error = 0;
01884          ast_indicate(peer, AST_CONTROL_UNHOLD);
01885          if (parkedplay == 0) {
01886             error = ast_stream_and_wait(chan, courtesytone, chan->language, "");
01887          } else if (parkedplay == 1) {
01888             error = ast_stream_and_wait(peer, courtesytone, chan->language, "");
01889          } else if (parkedplay == 2) {
01890             if (!ast_streamfile(chan, courtesytone, chan->language) &&
01891                   !ast_streamfile(peer, courtesytone, chan->language)) {
01892                /*! \todo XXX we would like to wait on both! */
01893                res = ast_waitstream(chan, "");
01894                if (res >= 0)
01895                   res = ast_waitstream(peer, "");
01896                if (res < 0)
01897                   error = 1;
01898             }
01899                         }
01900          if (error) {
01901             ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
01902             ast_hangup(peer);
01903             return -1;
01904          }
01905       } else
01906          ast_indicate(peer, AST_CONTROL_UNHOLD); 
01907 
01908       res = ast_channel_make_compatible(chan, peer);
01909       if (res < 0) {
01910          ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
01911          ast_hangup(peer);
01912          return -1;
01913       }
01914       /* This runs sorta backwards, since we give the incoming channel control, as if it
01915          were the person called. */
01916       if (option_verbose > 2) 
01917          ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
01918 
01919       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
01920       ast_cdr_setdestchan(chan->cdr, peer->name);
01921       memset(&config, 0, sizeof(struct ast_bridge_config));
01922       ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01923       ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
01924       res = ast_bridge_call(chan, peer, &config);
01925 
01926       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
01927       ast_cdr_setdestchan(chan->cdr, peer->name);
01928 
01929       /* Simulate the PBX hanging up */
01930       if (res != AST_PBX_NO_HANGUP_PEER)
01931          ast_hangup(peer);
01932       return res;
01933    } else {
01934       /*! \todo XXX Play a message XXX */
01935       if (ast_stream_and_wait(chan, "pbx-invalidpark", chan->language, ""))
01936          ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
01937       if (option_verbose > 2) 
01938          ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park);
01939       res = -1;
01940    }
01941 
01942    ast_module_user_remove(u);
01943 
01944    return res;
01945 }
01946 
01947 static int handle_showfeatures(int fd, int argc, char *argv[])
01948 {
01949    int i;
01950    struct ast_call_feature *feature;
01951    char format[] = "%-25s %-7s %-7s\n";
01952 
01953    ast_cli(fd, format, "Builtin Feature", "Default", "Current");
01954    ast_cli(fd, format, "---------------", "-------", "-------");
01955 
01956    ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext());      /* default hardcoded above, so we'll hardcode it here */
01957 
01958    ast_rwlock_rdlock(&features_lock);
01959    for (i = 0; i < FEATURES_COUNT; i++)
01960       ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
01961    ast_rwlock_unlock(&features_lock);
01962 
01963    ast_cli(fd, "\n");
01964    ast_cli(fd, format, "Dynamic Feature", "Default", "Current");
01965    ast_cli(fd, format, "---------------", "-------", "-------");
01966    if (AST_LIST_EMPTY(&feature_list))
01967       ast_cli(fd, "(none)\n");
01968    else {
01969       AST_LIST_LOCK(&feature_list);
01970       AST_LIST_TRAVERSE(&feature_list, feature, feature_entry)
01971          ast_cli(fd, format, feature->sname, "no def", feature->exten); 
01972       AST_LIST_UNLOCK(&feature_list);
01973    }
01974    ast_cli(fd, "\nCall parking\n");
01975    ast_cli(fd, "------------\n");
01976    ast_cli(fd,"%-20s:   %s\n", "Parking extension", parking_ext);
01977    ast_cli(fd,"%-20s:   %s\n", "Parking context", parking_con);
01978    ast_cli(fd,"%-20s:   %d-%d\n", "Parked call extensions", parking_start, parking_stop);
01979    ast_cli(fd,"\n");
01980    
01981    return RESULT_SUCCESS;
01982 }
01983 
01984 static char showfeatures_help[] =
01985 "Usage: feature list\n"
01986 "       Lists currently configured features.\n";
01987 
01988 static int handle_parkedcalls(int fd, int argc, char *argv[])
01989 {
01990    struct parkeduser *cur;
01991    int numparked = 0;
01992 
01993    ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
01994       , "Context", "Extension", "Pri", "Timeout");
01995 
01996    ast_mutex_lock(&parking_lock);
01997 
01998    for (cur = parkinglot; cur; cur = cur->next) {
01999       ast_cli(fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
02000          ,cur->parkingexten, cur->chan->name, cur->context, cur->exten
02001          ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
02002 
02003       numparked++;
02004    }
02005    ast_mutex_unlock(&parking_lock);
02006    ast_cli(fd, "%d parked call%s.\n", numparked, (numparked != 1) ? "s" : "");
02007 
02008 
02009    return RESULT_SUCCESS;
02010 }
02011 
02012 static char showparked_help[] =
02013 "Usage: show parkedcalls\n"
02014 "       Lists currently parked calls.\n";
02015 
02016 static struct ast_cli_entry cli_show_features_deprecated = {
02017    { "show", "features", NULL },
02018    handle_showfeatures, NULL,
02019    NULL };
02020 
02021 static struct ast_cli_entry cli_features[] = {
02022    { { "feature", "show", NULL },
02023    handle_showfeatures, "Lists configured features",
02024    showfeatures_help, NULL, &cli_show_features_deprecated },
02025 
02026    { { "show", "parkedcalls", NULL },
02027    handle_parkedcalls, "Lists parked calls",
02028    showparked_help },
02029 };
02030 
02031 /*! \brief Dump lot status */
02032 static int manager_parking_status( struct mansession *s, const struct message *m)
02033 {
02034    struct parkeduser *cur;
02035    const char *id = astman_get_header(m, "ActionID");
02036    char idText[256] = "";
02037 
02038    if (!ast_strlen_zero(id))
02039       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02040 
02041    astman_send_ack(s, m, "Parked calls will follow");
02042 
02043    ast_mutex_lock(&parking_lock);
02044 
02045    for (cur = parkinglot; cur; cur = cur->next) {
02046       astman_append(s, "Event: ParkedCall\r\n"
02047          "Exten: %d\r\n"
02048          "Channel: %s\r\n"
02049          "From: %s\r\n"
02050          "Timeout: %ld\r\n"
02051          "CallerID: %s\r\n"
02052          "CallerIDName: %s\r\n"
02053          "%s"
02054          "\r\n",
02055          cur->parkingnum, cur->chan->name, cur->peername,
02056          (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
02057          S_OR(cur->chan->cid.cid_num, ""),   /* XXX in other places it is <unknown> */
02058          S_OR(cur->chan->cid.cid_name, ""),
02059          idText);
02060    }
02061 
02062    astman_append(s,
02063       "Event: ParkedCallsComplete\r\n"
02064       "%s"
02065       "\r\n",idText);
02066 
02067    ast_mutex_unlock(&parking_lock);
02068 
02069    return RESULT_SUCCESS;
02070 }
02071 
02072 static char mandescr_park[] =
02073 "Description: Park a channel.\n"
02074 "Variables: (Names marked with * are required)\n"
02075 "  *Channel: Channel name to park\n"
02076 "  *Channel2: Channel to announce park info to (and return to if timeout)\n"
02077 "  Timeout: Number of milliseconds to wait before callback.\n";  
02078 
02079 static int manager_park(struct mansession *s, const struct message *m)
02080 {
02081    const char *channel = astman_get_header(m, "Channel");
02082    const char *channel2 = astman_get_header(m, "Channel2");
02083    const char *timeout = astman_get_header(m, "Timeout");
02084    char buf[BUFSIZ];
02085    int to = 0;
02086    int res = 0;
02087    int parkExt = 0;
02088    struct ast_channel *ch1, *ch2;
02089 
02090    if (ast_strlen_zero(channel)) {
02091       astman_send_error(s, m, "Channel not specified");
02092       return 0;
02093    }
02094 
02095    if (ast_strlen_zero(channel2)) {
02096       astman_send_error(s, m, "Channel2 not specified");
02097       return 0;
02098    }
02099 
02100    ch1 = ast_get_channel_by_name_locked(channel);
02101    if (!ch1) {
02102       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
02103       astman_send_error(s, m, buf);
02104       return 0;
02105    }
02106 
02107    ch2 = ast_get_channel_by_name_locked(channel2);
02108    if (!ch2) {
02109       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
02110       astman_send_error(s, m, buf);
02111       ast_channel_unlock(ch1);
02112       return 0;
02113    }
02114 
02115    if (!ast_strlen_zero(timeout)) {
02116       sscanf(timeout, "%d", &to);
02117    }
02118 
02119    res = ast_masq_park_call(ch1, ch2, to, &parkExt);
02120    if (!res) {
02121       ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
02122       astman_send_ack(s, m, "Park successful");
02123    } else {
02124       astman_send_error(s, m, "Park failure");
02125    }
02126 
02127    ast_channel_unlock(ch1);
02128    ast_channel_unlock(ch2);
02129 
02130    return 0;
02131 }
02132 
02133 
02134 int ast_pickup_call(struct ast_channel *chan)
02135 {
02136    struct ast_channel *cur = NULL;
02137    int res = -1;
02138 
02139    while ( (cur = ast_channel_walk_locked(cur)) != NULL) {
02140       if (!cur->pbx && 
02141          (cur != chan) &&
02142          (chan->pickupgroup & cur->callgroup) &&
02143          ((cur->_state == AST_STATE_RINGING) ||
02144           (cur->_state == AST_STATE_RING))) {
02145             break;
02146       }
02147       ast_channel_unlock(cur);
02148    }
02149    if (cur) {
02150       if (option_debug)
02151          ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
02152       res = ast_answer(chan);
02153       if (res)
02154          ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
02155       res = ast_queue_control(chan, AST_CONTROL_ANSWER);
02156       if (res)
02157          ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
02158       res = ast_channel_masquerade(cur, chan);
02159       if (res)
02160          ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);     /* Done */
02161       ast_channel_unlock(cur);
02162    } else   {
02163       if (option_debug)
02164          ast_log(LOG_DEBUG, "No call pickup possible...\n");
02165    }
02166    return res;
02167 }
02168 
02169 /*! \brief Add parking hints for all defined parking lots */
02170 static void park_add_hints(char *context, int start, int stop)
02171 {
02172    int numext;
02173    char device[AST_MAX_EXTENSION];
02174    char exten[10];
02175 
02176    for (numext = start; numext <= stop; numext++) {
02177       snprintf(exten, sizeof(exten), "%d", numext);
02178       snprintf(device, sizeof(device), "park:%s@%s", exten, context);
02179       ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
02180    }
02181 }
02182 
02183 
02184 static int load_config(void) 
02185 {
02186    int start = 0, end = 0;
02187    int res;
02188    struct ast_context *con = NULL;
02189    struct ast_config *cfg = NULL;
02190    struct ast_variable *var = NULL;
02191    char old_parking_ext[AST_MAX_EXTENSION];
02192    char old_parking_con[AST_MAX_EXTENSION] = "";
02193 
02194    if (!ast_strlen_zero(parking_con)) {
02195       strcpy(old_parking_ext, parking_ext);
02196       strcpy(old_parking_con, parking_con);
02197    } 
02198 
02199    /* Reset to defaults */
02200    strcpy(parking_con, "parkedcalls");
02201    strcpy(parking_con_dial, "park-dial");
02202    strcpy(parking_ext, "700");
02203    strcpy(pickup_ext, "*8");
02204    strcpy(parkmohclass, "default");
02205    courtesytone[0] = '\0';
02206    strcpy(xfersound, "beep");
02207    strcpy(xferfailsound, "pbx-invalid");
02208    parking_start = 701;
02209    parking_stop = 750;
02210    parkfindnext = 0;
02211    adsipark = 0;
02212    parkaddhints = 0;
02213 
02214    transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
02215    featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
02216    atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
02217 
02218    cfg = ast_config_load("features.conf");
02219    if (!cfg) {
02220       ast_log(LOG_WARNING,"Could not load features.conf\n");
02221       return AST_MODULE_LOAD_DECLINE;
02222    }
02223    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
02224       if (!strcasecmp(var->name, "parkext")) {
02225          ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
02226       } else if (!strcasecmp(var->name, "context")) {
02227          ast_copy_string(parking_con, var->value, sizeof(parking_con));
02228       } else if (!strcasecmp(var->name, "parkingtime")) {
02229          if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
02230             ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
02231             parkingtime = DEFAULT_PARK_TIME;
02232          } else
02233             parkingtime = parkingtime * 1000;
02234       } else if (!strcasecmp(var->name, "parkpos")) {
02235          if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
02236             ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of parking.conf\n", var->lineno);
02237          } else {
02238             parking_start = start;
02239             parking_stop = end;
02240          }
02241       } else if (!strcasecmp(var->name, "findslot")) {
02242          parkfindnext = (!strcasecmp(var->value, "next"));
02243       } else if (!strcasecmp(var->name, "parkinghints")) {
02244          parkaddhints = ast_true(var->value);
02245       } else if (!strcasecmp(var->name, "adsipark")) {
02246          adsipark = ast_true(var->value);
02247       } else if (!strcasecmp(var->name, "transferdigittimeout")) {
02248          if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
02249             ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
02250             transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
02251          } else
02252             transferdigittimeout = transferdigittimeout * 1000;
02253       } else if (!strcasecmp(var->name, "featuredigittimeout")) {
02254          if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
02255             ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
02256             featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
02257          }
02258       } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
02259          if ((sscanf(var->value, "%d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
02260             ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
02261             atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
02262          } else
02263             atxfernoanswertimeout = atxfernoanswertimeout * 1000;
02264       } else if (!strcasecmp(var->name, "courtesytone")) {
02265          ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
02266       }  else if (!strcasecmp(var->name, "parkedplay")) {
02267          if (!strcasecmp(var->value, "both"))
02268             parkedplay = 2;
02269          else if (!strcasecmp(var->value, "parked"))
02270             parkedplay = 1;
02271          else
02272             parkedplay = 0;
02273       } else if (!strcasecmp(var->name, "xfersound")) {
02274          ast_copy_string(xfersound, var->value, sizeof(xfersound));
02275       } else if (!strcasecmp(var->name, "xferfailsound")) {
02276          ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
02277       } else if (!strcasecmp(var->name, "pickupexten")) {
02278          ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
02279       } else if (!strcasecmp(var->name, "parkedmusicclass")) {
02280          ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass));
02281       }
02282    }
02283 
02284    unmap_features();
02285    for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
02286       if (remap_feature(var->name, var->value))
02287          ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
02288    }
02289 
02290    /* Map a key combination to an application*/
02291    ast_unregister_features();
02292    for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
02293       char *tmp_val = ast_strdupa(var->value);
02294       char *exten, *activateon, *activatedby, *app, *app_args, *moh_class; 
02295       struct ast_call_feature *feature;
02296 
02297       /* strsep() sets the argument to NULL if match not found, and it
02298        * is safe to use it with a NULL argument, so we don't check
02299        * between calls.
02300        */
02301       exten = strsep(&tmp_val,",");
02302       activatedby = strsep(&tmp_val,",");
02303       app = strsep(&tmp_val,",");
02304       app_args = strsep(&tmp_val,",");
02305       moh_class = strsep(&tmp_val,",");
02306 
02307       activateon = strsep(&activatedby, "/");   
02308 
02309       /*! \todo XXX var_name or app_args ? */
02310       if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
02311          ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
02312             app, exten, activateon, var->name);
02313          continue;
02314       }
02315 
02316       AST_LIST_LOCK(&feature_list);
02317       if ((feature = find_dynamic_feature(var->name))) {
02318          AST_LIST_UNLOCK(&feature_list);
02319          ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name);
02320          continue;
02321       }
02322       AST_LIST_UNLOCK(&feature_list);
02323             
02324       if (!(feature = ast_calloc(1, sizeof(*feature))))
02325          continue;               
02326 
02327       ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
02328       ast_copy_string(feature->app, app, FEATURE_APP_LEN);
02329       ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
02330       
02331       if (app_args) 
02332          ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
02333 
02334       if (moh_class)
02335          ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN);
02336          
02337       ast_copy_string(feature->exten, exten, sizeof(feature->exten));
02338       feature->operation = feature_exec_app;
02339       ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
02340 
02341       /* Allow caller and calle to be specified for backwards compatability */
02342       if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller"))
02343          ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
02344       else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee"))
02345          ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
02346       else {
02347          ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
02348             " must be 'self', or 'peer'\n", var->name);
02349          continue;
02350       }
02351 
02352       if (ast_strlen_zero(activatedby))
02353          ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
02354       else if (!strcasecmp(activatedby, "caller"))
02355          ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
02356       else if (!strcasecmp(activatedby, "callee"))
02357          ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
02358       else if (!strcasecmp(activatedby, "both"))
02359          ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
02360       else {
02361          ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
02362             " must be 'caller', or 'callee', or 'both'\n", var->name);
02363          continue;
02364       }
02365 
02366       ast_register_feature(feature);
02367          
02368       if (option_verbose >= 1)
02369          ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);  
02370    }   
02371    ast_config_destroy(cfg);
02372 
02373    /* Remove the old parking extension */
02374    if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) {
02375       if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar))
02376             notify_metermaids(old_parking_ext, old_parking_con);
02377       if (option_debug)
02378          ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con);
02379    }
02380    
02381    if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
02382       ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
02383       return -1;
02384    }
02385    res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar);
02386    if (parkaddhints)
02387       park_add_hints(parking_con, parking_start, parking_stop);
02388    if (!res)
02389       notify_metermaids(ast_parking_ext(), parking_con);
02390    return res;
02391 
02392 }
02393 
02394 static int reload(void)
02395 {
02396    return load_config();
02397 }
02398 
02399 static int load_module(void)
02400 {
02401    int res;
02402    
02403    memset(parking_ext, 0, sizeof(parking_ext));
02404    memset(parking_con, 0, sizeof(parking_con));
02405 
02406    if ((res = load_config()))
02407       return res;
02408    ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
02409    ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
02410    res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
02411    if (!res)
02412       res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
02413    if (!res) {
02414       ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls" );
02415       ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
02416          "Park a channel", mandescr_park); 
02417    }
02418 
02419    res |= ast_devstate_prov_add("Park", metermaidstate);
02420 
02421    return res;
02422 }
02423 
02424 
02425 static int unload_module(void)
02426 {
02427    ast_module_user_hangup_all();
02428 
02429    ast_manager_unregister("ParkedCalls");
02430    ast_manager_unregister("Park");
02431    ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
02432    ast_unregister_application(parkcall);
02433    ast_devstate_prov_del("Park");
02434    return ast_unregister_application(parkedcall);
02435 }
02436 
02437 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Features Resource",
02438       .load = load_module,
02439       .unload = unload_module,
02440       .reload = reload,
02441           );

Generated on Wed Aug 15 01:24:24 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.3