00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 237321 $")
00031
00032 #include <fcntl.h>
00033 #include <sys/signal.h>
00034
00035 #include "asterisk/lock.h"
00036 #include "asterisk/channel.h"
00037 #include "asterisk/config.h"
00038 #include "asterisk/module.h"
00039 #include "asterisk/pbx.h"
00040 #include "asterisk/sched.h"
00041 #include "asterisk/io.h"
00042 #include "asterisk/rtp.h"
00043 #include "asterisk/acl.h"
00044 #include "asterisk/callerid.h"
00045 #include "asterisk/file.h"
00046 #include "asterisk/cli.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/musiconhold.h"
00049 #include "asterisk/manager.h"
00050 #include "asterisk/stringfields.h"
00051 #include "asterisk/devicestate.h"
00052
00053 static const char tdesc[] = "Local Proxy Channel Driver";
00054
00055 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
00056
00057 static struct ast_jb_conf g_jb_conf = {
00058 .flags = 0,
00059 .max_size = -1,
00060 .resync_threshold = -1,
00061 .impl = "",
00062 };
00063
00064 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause);
00065 static int local_digit_begin(struct ast_channel *ast, char digit);
00066 static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00067 static int local_call(struct ast_channel *ast, char *dest, int timeout);
00068 static int local_hangup(struct ast_channel *ast);
00069 static int local_answer(struct ast_channel *ast);
00070 static struct ast_frame *local_read(struct ast_channel *ast);
00071 static int local_write(struct ast_channel *ast, struct ast_frame *f);
00072 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00073 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00074 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00075 static int local_sendtext(struct ast_channel *ast, const char *text);
00076 static int local_devicestate(void *data);
00077 static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
00078
00079
00080 static const struct ast_channel_tech local_tech = {
00081 .type = "Local",
00082 .description = tdesc,
00083 .capabilities = -1,
00084 .requester = local_request,
00085 .send_digit_begin = local_digit_begin,
00086 .send_digit_end = local_digit_end,
00087 .call = local_call,
00088 .hangup = local_hangup,
00089 .answer = local_answer,
00090 .read = local_read,
00091 .write = local_write,
00092 .write_video = local_write,
00093 .exception = local_read,
00094 .indicate = local_indicate,
00095 .fixup = local_fixup,
00096 .send_html = local_sendhtml,
00097 .send_text = local_sendtext,
00098 .devicestate = local_devicestate,
00099 .bridged_channel = local_bridgedchannel,
00100 };
00101
00102 struct local_pvt {
00103 ast_mutex_t lock;
00104 unsigned int flags;
00105 char context[AST_MAX_CONTEXT];
00106 char exten[AST_MAX_EXTENSION];
00107 int reqformat;
00108 struct ast_jb_conf jb_conf;
00109 struct ast_channel *owner;
00110 struct ast_channel *chan;
00111 struct ast_module_user *u_owner;
00112 struct ast_module_user *u_chan;
00113 AST_LIST_ENTRY(local_pvt) list;
00114 };
00115
00116 #define LOCAL_GLARE_DETECT (1 << 0)
00117 #define LOCAL_CANCEL_QUEUE (1 << 1)
00118 #define LOCAL_ALREADY_MASQED (1 << 2)
00119 #define LOCAL_LAUNCHED_PBX (1 << 3)
00120 #define LOCAL_NO_OPTIMIZATION (1 << 4)
00121 #define LOCAL_BRIDGE (1 << 5)
00122 #define LOCAL_MOH_PASSTHRU (1 << 6)
00123
00124 static AST_LIST_HEAD_STATIC(locals, local_pvt);
00125
00126
00127 static int local_devicestate(void *data)
00128 {
00129 char *exten = ast_strdupa(data);
00130 char *context = NULL, *opts = NULL;
00131 int res;
00132 struct local_pvt *lp;
00133
00134 if (!(context = strchr(exten, '@'))) {
00135 ast_log(LOG_WARNING, "Someone used Local/%s somewhere without a @context. This is bad.\n", exten);
00136 return AST_DEVICE_INVALID;
00137 }
00138
00139 *context++ = '\0';
00140
00141
00142 if ((opts = strchr(context, '/')))
00143 *opts = '\0';
00144
00145 ast_debug(3, "Checking if extension %s@%s exists (devicestate)\n", exten, context);
00146
00147 res = ast_exists_extension(NULL, context, exten, 1, NULL);
00148 if (!res)
00149 return AST_DEVICE_INVALID;
00150
00151 res = AST_DEVICE_NOT_INUSE;
00152 AST_LIST_LOCK(&locals);
00153 AST_LIST_TRAVERSE(&locals, lp, list) {
00154 if (!strcmp(exten, lp->exten) && !strcmp(context, lp->context) && lp->owner) {
00155 res = AST_DEVICE_INUSE;
00156 break;
00157 }
00158 }
00159 AST_LIST_UNLOCK(&locals);
00160
00161 return res;
00162 }
00163
00164
00165
00166
00167 static struct local_pvt *local_pvt_destroy(struct local_pvt *pvt)
00168 {
00169 ast_mutex_destroy(&pvt->lock);
00170 ast_free(pvt);
00171 return NULL;
00172 }
00173
00174
00175 static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
00176 {
00177 struct local_pvt *p = bridge->tech_pvt;
00178 struct ast_channel *bridged = bridge;
00179
00180 if (!p) {
00181 ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning <none>\n",
00182 chan->name, bridge->name);
00183 return NULL;
00184 }
00185
00186 ast_mutex_lock(&p->lock);
00187
00188 if (ast_test_flag(p, LOCAL_BRIDGE)) {
00189
00190 bridged = (bridge == p->owner ? p->chan : p->owner);
00191
00192
00193 if (!bridged) {
00194 bridged = bridge;
00195 } else if (bridged->_bridge) {
00196 bridged = bridged->_bridge;
00197 }
00198 }
00199
00200 ast_mutex_unlock(&p->lock);
00201
00202 return bridged;
00203 }
00204
00205 static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f,
00206 struct ast_channel *us, int us_locked)
00207 {
00208 struct ast_channel *other = NULL;
00209
00210
00211 other = isoutbound ? p->owner : p->chan;
00212
00213 if (!other) {
00214 return 0;
00215 }
00216
00217
00218 if (us && us->generator && other->generator) {
00219 return 0;
00220 }
00221
00222
00223 ast_set_flag(p, LOCAL_GLARE_DETECT);
00224
00225
00226 while (other && ast_channel_trylock(other)) {
00227 ast_mutex_unlock(&p->lock);
00228 if (us && us_locked) {
00229 do {
00230 CHANNEL_DEADLOCK_AVOIDANCE(us);
00231 } while (ast_mutex_trylock(&p->lock));
00232 } else {
00233 usleep(1);
00234 ast_mutex_lock(&p->lock);
00235 }
00236 other = isoutbound ? p->owner : p->chan;
00237 }
00238
00239
00240
00241
00242 if (ast_test_flag(p, LOCAL_CANCEL_QUEUE)) {
00243
00244
00245 ast_mutex_unlock(&p->lock);
00246 p = local_pvt_destroy(p);
00247 if (other) {
00248 ast_channel_unlock(other);
00249 }
00250 return -1;
00251 }
00252
00253 if (other) {
00254 if (other->pbx || other->_bridge || !ast_strlen_zero(other->appl)) {
00255 ast_queue_frame(other, f);
00256 }
00257 ast_channel_unlock(other);
00258 }
00259
00260 ast_clear_flag(p, LOCAL_GLARE_DETECT);
00261
00262 return 0;
00263 }
00264
00265 static int local_answer(struct ast_channel *ast)
00266 {
00267 struct local_pvt *p = ast->tech_pvt;
00268 int isoutbound;
00269 int res = -1;
00270
00271 if (!p)
00272 return -1;
00273
00274 ast_mutex_lock(&p->lock);
00275 isoutbound = IS_OUTBOUND(ast, p);
00276 if (isoutbound) {
00277
00278 struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00279 res = local_queue_frame(p, isoutbound, &answer, ast, 1);
00280 } else
00281 ast_log(LOG_WARNING, "Huh? Local is being asked to answer?\n");
00282 if (!res)
00283 ast_mutex_unlock(&p->lock);
00284 return res;
00285 }
00286
00287 static void check_bridge(struct local_pvt *p, int isoutbound)
00288 {
00289 struct ast_channel_monitor *tmp;
00290 if (ast_test_flag(p, LOCAL_ALREADY_MASQED) || ast_test_flag(p, LOCAL_NO_OPTIMIZATION) || !p->chan || !p->owner || (p->chan->_bridge != ast_bridged_channel(p->chan)))
00291 return;
00292
00293
00294
00295
00296
00297
00298 if (isoutbound && p->chan->_bridge && AST_LIST_EMPTY(&p->owner->readq)) {
00299
00300
00301
00302
00303 if (!ast_channel_trylock(p->chan->_bridge)) {
00304 if (!ast_check_hangup(p->chan->_bridge)) {
00305 if (!ast_channel_trylock(p->owner)) {
00306 if (!ast_check_hangup(p->owner)) {
00307 if (p->owner->monitor && !p->chan->_bridge->monitor) {
00308
00309
00310
00311
00312
00313 tmp = p->owner->monitor;
00314 p->owner->monitor = p->chan->_bridge->monitor;
00315 p->chan->_bridge->monitor = tmp;
00316 }
00317 if (p->chan->audiohooks) {
00318 struct ast_audiohook_list *audiohooks_swapper;
00319 audiohooks_swapper = p->chan->audiohooks;
00320 p->chan->audiohooks = p->owner->audiohooks;
00321 p->owner->audiohooks = audiohooks_swapper;
00322 }
00323 ast_app_group_update(p->chan, p->owner);
00324 ast_channel_masquerade(p->owner, p->chan->_bridge);
00325 ast_set_flag(p, LOCAL_ALREADY_MASQED);
00326 }
00327 ast_channel_unlock(p->owner);
00328 }
00329 ast_channel_unlock(p->chan->_bridge);
00330 }
00331 }
00332
00333
00334
00335
00336 #if 0
00337 } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && AST_LIST_EMPTY(&p->chan->readq)) {
00338
00339 if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
00340 if (!ast_check_hangup(p->owner->_bridge)) {
00341 if (!ast_mutex_trylock(&p->chan->lock)) {
00342 if (!ast_check_hangup(p->chan)) {
00343 ast_channel_masquerade(p->chan, p->owner->_bridge);
00344 ast_set_flag(p, LOCAL_ALREADY_MASQED);
00345 }
00346 ast_mutex_unlock(&p->chan->lock);
00347 }
00348 }
00349 ast_mutex_unlock(&(p->owner->_bridge)->lock);
00350 }
00351 #endif
00352 }
00353 }
00354
00355 static struct ast_frame *local_read(struct ast_channel *ast)
00356 {
00357 return &ast_null_frame;
00358 }
00359
00360 static int local_write(struct ast_channel *ast, struct ast_frame *f)
00361 {
00362 struct local_pvt *p = ast->tech_pvt;
00363 int res = -1;
00364 int isoutbound;
00365
00366 if (!p)
00367 return -1;
00368
00369
00370 ast_mutex_lock(&p->lock);
00371 isoutbound = IS_OUTBOUND(ast, p);
00372 if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
00373 check_bridge(p, isoutbound);
00374 if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
00375 res = local_queue_frame(p, isoutbound, f, ast, 1);
00376 else {
00377 ast_debug(1, "Not posting to queue since already masked on '%s'\n", ast->name);
00378 res = 0;
00379 }
00380 if (!res)
00381 ast_mutex_unlock(&p->lock);
00382 return res;
00383 }
00384
00385 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00386 {
00387 struct local_pvt *p = newchan->tech_pvt;
00388
00389 if (!p)
00390 return -1;
00391
00392 ast_mutex_lock(&p->lock);
00393
00394 if ((p->owner != oldchan) && (p->chan != oldchan)) {
00395 ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
00396 ast_mutex_unlock(&p->lock);
00397 return -1;
00398 }
00399 if (p->owner == oldchan)
00400 p->owner = newchan;
00401 else
00402 p->chan = newchan;
00403 ast_mutex_unlock(&p->lock);
00404 return 0;
00405 }
00406
00407 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00408 {
00409 struct local_pvt *p = ast->tech_pvt;
00410 int res = 0;
00411 struct ast_frame f = { AST_FRAME_CONTROL, };
00412 int isoutbound;
00413
00414 if (!p)
00415 return -1;
00416
00417
00418 if (!ast_test_flag(p, LOCAL_MOH_PASSTHRU) && condition == AST_CONTROL_HOLD) {
00419 ast_moh_start(ast, data, NULL);
00420 } else if (!ast_test_flag(p, LOCAL_MOH_PASSTHRU) && condition == AST_CONTROL_UNHOLD) {
00421 ast_moh_stop(ast);
00422 } else {
00423
00424 ast_mutex_lock(&p->lock);
00425 isoutbound = IS_OUTBOUND(ast, p);
00426 f.subclass = condition;
00427 f.data.ptr = (void*)data;
00428 f.datalen = datalen;
00429 if (!(res = local_queue_frame(p, isoutbound, &f, ast, 1)))
00430 ast_mutex_unlock(&p->lock);
00431 }
00432
00433 return res;
00434 }
00435
00436 static int local_digit_begin(struct ast_channel *ast, char digit)
00437 {
00438 struct local_pvt *p = ast->tech_pvt;
00439 int res = -1;
00440 struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
00441 int isoutbound;
00442
00443 if (!p)
00444 return -1;
00445
00446 ast_mutex_lock(&p->lock);
00447 isoutbound = IS_OUTBOUND(ast, p);
00448 f.subclass = digit;
00449 if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00450 ast_mutex_unlock(&p->lock);
00451
00452 return res;
00453 }
00454
00455 static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00456 {
00457 struct local_pvt *p = ast->tech_pvt;
00458 int res = -1;
00459 struct ast_frame f = { AST_FRAME_DTMF_END, };
00460 int isoutbound;
00461
00462 if (!p)
00463 return -1;
00464
00465 ast_mutex_lock(&p->lock);
00466 isoutbound = IS_OUTBOUND(ast, p);
00467 f.subclass = digit;
00468 f.len = duration;
00469 if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00470 ast_mutex_unlock(&p->lock);
00471
00472 return res;
00473 }
00474
00475 static int local_sendtext(struct ast_channel *ast, const char *text)
00476 {
00477 struct local_pvt *p = ast->tech_pvt;
00478 int res = -1;
00479 struct ast_frame f = { AST_FRAME_TEXT, };
00480 int isoutbound;
00481
00482 if (!p)
00483 return -1;
00484
00485 ast_mutex_lock(&p->lock);
00486 isoutbound = IS_OUTBOUND(ast, p);
00487 f.data.ptr = (char *) text;
00488 f.datalen = strlen(text) + 1;
00489 if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00490 ast_mutex_unlock(&p->lock);
00491 return res;
00492 }
00493
00494 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00495 {
00496 struct local_pvt *p = ast->tech_pvt;
00497 int res = -1;
00498 struct ast_frame f = { AST_FRAME_HTML, };
00499 int isoutbound;
00500
00501 if (!p)
00502 return -1;
00503
00504 ast_mutex_lock(&p->lock);
00505 isoutbound = IS_OUTBOUND(ast, p);
00506 f.subclass = subclass;
00507 f.data.ptr = (char *)data;
00508 f.datalen = datalen;
00509 if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00510 ast_mutex_unlock(&p->lock);
00511 return res;
00512 }
00513
00514
00515
00516 static int local_call(struct ast_channel *ast, char *dest, int timeout)
00517 {
00518 struct local_pvt *p = ast->tech_pvt;
00519 int res;
00520 struct ast_var_t *varptr = NULL, *new;
00521 size_t len, namelen;
00522
00523 if (!p)
00524 return -1;
00525
00526 ast_mutex_lock(&p->lock);
00527
00528
00529
00530
00531
00532 p->chan->cid.cid_dnid = ast_strdup(p->owner->cid.cid_dnid);
00533 p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num);
00534 p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name);
00535 p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis);
00536 p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani);
00537 p->chan->cid.cid_pres = p->owner->cid.cid_pres;
00538 p->chan->cid.cid_ani2 = p->owner->cid.cid_ani2;
00539 p->chan->cid.cid_ton = p->owner->cid.cid_ton;
00540 p->chan->cid.cid_tns = p->owner->cid.cid_tns;
00541 ast_string_field_set(p->chan, language, p->owner->language);
00542 ast_string_field_set(p->chan, accountcode, p->owner->accountcode);
00543 ast_string_field_set(p->chan, musicclass, p->owner->musicclass);
00544 ast_cdr_update(p->chan);
00545 p->chan->cdrflags = p->owner->cdrflags;
00546
00547 if (!ast_exists_extension(NULL, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) {
00548 ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context);
00549 ast_mutex_unlock(&p->lock);
00550 return -1;
00551 }
00552
00553
00554
00555 AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
00556 namelen = strlen(varptr->name);
00557 len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
00558 if ((new = ast_calloc(1, len))) {
00559 memcpy(new, varptr, len);
00560 new->value = &(new->name[0]) + namelen + 1;
00561 AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
00562 }
00563 }
00564 ast_channel_datastore_inherit(p->owner, p->chan);
00565
00566
00567 if (!(res = ast_pbx_start(p->chan)))
00568 ast_set_flag(p, LOCAL_LAUNCHED_PBX);
00569
00570 ast_mutex_unlock(&p->lock);
00571 return res;
00572 }
00573
00574
00575 static int local_hangup(struct ast_channel *ast)
00576 {
00577 struct local_pvt *p = ast->tech_pvt;
00578 int isoutbound;
00579 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP, .data.uint32 = ast->hangupcause };
00580 struct ast_channel *ochan = NULL;
00581 int glaredetect = 0, res = 0;
00582
00583 if (!p)
00584 return -1;
00585
00586 ast_mutex_lock(&p->lock);
00587
00588 if (p->chan && ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE))
00589 ast_set_flag(p->chan, AST_FLAG_ANSWERED_ELSEWHERE);
00590 isoutbound = IS_OUTBOUND(ast, p);
00591 if (isoutbound) {
00592 const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
00593 if ((status) && (p->owner)) {
00594
00595 while (p->owner && ast_channel_trylock(p->owner)) {
00596 ast_mutex_unlock(&p->lock);
00597 if (ast) {
00598 ast_channel_unlock(ast);
00599 }
00600 usleep(1);
00601 if (ast) {
00602 ast_channel_lock(ast);
00603 }
00604 ast_mutex_lock(&p->lock);
00605 }
00606 if (p->owner) {
00607 pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
00608 ast_channel_unlock(p->owner);
00609 }
00610 }
00611 p->chan = NULL;
00612 ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
00613 ast_module_user_remove(p->u_chan);
00614 } else {
00615 ast_module_user_remove(p->u_owner);
00616 while (p->chan && ast_channel_trylock(p->chan)) {
00617 DEADLOCK_AVOIDANCE(&p->lock);
00618 }
00619 p->owner = NULL;
00620 if (p->chan) {
00621 ast_queue_hangup(p->chan);
00622 ast_channel_unlock(p->chan);
00623 }
00624 }
00625
00626 ast->tech_pvt = NULL;
00627
00628 if (!p->owner && !p->chan) {
00629
00630 glaredetect = ast_test_flag(p, LOCAL_GLARE_DETECT);
00631
00632
00633 if (glaredetect)
00634 ast_set_flag(p, LOCAL_CANCEL_QUEUE);
00635
00636 AST_LIST_LOCK(&locals);
00637 AST_LIST_REMOVE(&locals, p, list);
00638 AST_LIST_UNLOCK(&locals);
00639 ast_mutex_unlock(&p->lock);
00640
00641 if (!glaredetect) {
00642 p = local_pvt_destroy(p);
00643 }
00644 return 0;
00645 }
00646 if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX))
00647
00648 ochan = p->chan;
00649 else
00650 res = local_queue_frame(p, isoutbound, &f, NULL, 1);
00651 if (!res)
00652 ast_mutex_unlock(&p->lock);
00653 if (ochan)
00654 ast_hangup(ochan);
00655 return 0;
00656 }
00657
00658
00659 static struct local_pvt *local_alloc(const char *data, int format)
00660 {
00661 struct local_pvt *tmp = NULL;
00662 char *c = NULL, *opts = NULL;
00663
00664 if (!(tmp = ast_calloc(1, sizeof(*tmp))))
00665 return NULL;
00666
00667
00668 ast_mutex_init(&tmp->lock);
00669 ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
00670
00671 memcpy(&tmp->jb_conf, &g_jb_conf, sizeof(tmp->jb_conf));
00672
00673
00674 if ((opts = strchr(tmp->exten, '/'))) {
00675 *opts++ = '\0';
00676 if (strchr(opts, 'n'))
00677 ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION);
00678 if (strchr(opts, 'j')) {
00679 if (ast_test_flag(tmp, LOCAL_NO_OPTIMIZATION))
00680 ast_set_flag(&tmp->jb_conf, AST_JB_ENABLED);
00681 else {
00682 ast_log(LOG_ERROR, "You must use the 'n' option for chan_local "
00683 "to use the 'j' option to enable the jitterbuffer\n");
00684 }
00685 }
00686 if (strchr(opts, 'b')) {
00687 ast_set_flag(tmp, LOCAL_BRIDGE);
00688 }
00689 if (strchr(opts, 'm')) {
00690 ast_set_flag(tmp, LOCAL_MOH_PASSTHRU);
00691 }
00692 }
00693
00694
00695 if ((c = strchr(tmp->exten, '@')))
00696 *c++ = '\0';
00697
00698 ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context));
00699
00700 tmp->reqformat = format;
00701
00702 #if 0
00703
00704
00705
00706 if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
00707 ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
00708 tmp = local_pvt_destroy(tmp);
00709 } else {
00710 #endif
00711
00712 AST_LIST_LOCK(&locals);
00713 AST_LIST_INSERT_HEAD(&locals, tmp, list);
00714 AST_LIST_UNLOCK(&locals);
00715 #if 0
00716 }
00717 #endif
00718
00719 return tmp;
00720 }
00721
00722
00723 static struct ast_channel *local_new(struct local_pvt *p, int state)
00724 {
00725 struct ast_channel *tmp = NULL, *tmp2 = NULL;
00726 int randnum = ast_random() & 0xffff, fmt = 0;
00727 const char *t;
00728 int ama;
00729
00730
00731
00732 if (p->owner && p->owner->accountcode)
00733 t = p->owner->accountcode;
00734 else
00735 t = "";
00736
00737 if (p->owner)
00738 ama = p->owner->amaflags;
00739 else
00740 ama = 0;
00741 if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum))
00742 || !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) {
00743 if (tmp)
00744 ast_channel_free(tmp);
00745 if (tmp2)
00746 ast_channel_free(tmp2);
00747 ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
00748 return NULL;
00749 }
00750
00751 tmp2->tech = tmp->tech = &local_tech;
00752
00753 tmp->nativeformats = p->reqformat;
00754 tmp2->nativeformats = p->reqformat;
00755
00756
00757 fmt = ast_best_codec(p->reqformat);
00758 tmp->writeformat = fmt;
00759 tmp2->writeformat = fmt;
00760 tmp->rawwriteformat = fmt;
00761 tmp2->rawwriteformat = fmt;
00762 tmp->readformat = fmt;
00763 tmp2->readformat = fmt;
00764 tmp->rawreadformat = fmt;
00765 tmp2->rawreadformat = fmt;
00766
00767 tmp->tech_pvt = p;
00768 tmp2->tech_pvt = p;
00769
00770 p->owner = tmp;
00771 p->chan = tmp2;
00772 p->u_owner = ast_module_user_add(p->owner);
00773 p->u_chan = ast_module_user_add(p->chan);
00774
00775 ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00776 ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
00777 ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
00778 tmp->priority = 1;
00779 tmp2->priority = 1;
00780
00781 ast_jb_configure(tmp, &p->jb_conf);
00782
00783 return tmp;
00784 }
00785
00786
00787 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
00788 {
00789 struct local_pvt *p = NULL;
00790 struct ast_channel *chan = NULL;
00791
00792
00793 if ((p = local_alloc(data, format))) {
00794 if (!(chan = local_new(p, AST_STATE_DOWN))) {
00795 AST_LIST_LOCK(&locals);
00796 AST_LIST_REMOVE(&locals, p, list);
00797 AST_LIST_UNLOCK(&locals);
00798 p = local_pvt_destroy(p);
00799 }
00800 }
00801
00802 return chan;
00803 }
00804
00805
00806 static char *locals_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00807 {
00808 struct local_pvt *p = NULL;
00809
00810 switch (cmd) {
00811 case CLI_INIT:
00812 e->command = "local show channels";
00813 e->usage =
00814 "Usage: local show channels\n"
00815 " Provides summary information on active local proxy channels.\n";
00816 return NULL;
00817 case CLI_GENERATE:
00818 return NULL;
00819 }
00820
00821 if (a->argc != 3)
00822 return CLI_SHOWUSAGE;
00823
00824 AST_LIST_LOCK(&locals);
00825 if (!AST_LIST_EMPTY(&locals)) {
00826 AST_LIST_TRAVERSE(&locals, p, list) {
00827 ast_mutex_lock(&p->lock);
00828 ast_cli(a->fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
00829 ast_mutex_unlock(&p->lock);
00830 }
00831 } else
00832 ast_cli(a->fd, "No local channels in use\n");
00833 AST_LIST_UNLOCK(&locals);
00834
00835 return CLI_SUCCESS;
00836 }
00837
00838 static struct ast_cli_entry cli_local[] = {
00839 AST_CLI_DEFINE(locals_show, "List status of local channels"),
00840 };
00841
00842
00843 static int load_module(void)
00844 {
00845
00846 if (ast_channel_register(&local_tech)) {
00847 ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
00848 return AST_MODULE_LOAD_FAILURE;
00849 }
00850 ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00851 return AST_MODULE_LOAD_SUCCESS;
00852 }
00853
00854
00855 static int unload_module(void)
00856 {
00857 struct local_pvt *p = NULL;
00858
00859
00860 ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00861 ast_channel_unregister(&local_tech);
00862 if (!AST_LIST_LOCK(&locals)) {
00863
00864 AST_LIST_TRAVERSE(&locals, p, list) {
00865 if (p->owner)
00866 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
00867 }
00868 AST_LIST_UNLOCK(&locals);
00869 } else {
00870 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
00871 return -1;
00872 }
00873 return 0;
00874 }
00875
00876 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Local Proxy Channel (Note: used internally by other modules)");