Mon Mar 31 07:38:04 2008

Asterisk developer's documentation


res_features.c

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

Generated on Mon Mar 31 07:38:04 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1