Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:55 2009

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