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