Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:53 2007

Asterisk developer's documentation :: Codename Pineapple


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

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen