/* * app_changrab.c * Copyright (c) 2004-2007 Anthony Minessale II * * Thanks to Clod Patry for his help. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1.48 $") static char *app = "ChanGrab"; static char *tdesc = "Take over an existing channel and bridge to it."; static char *synopsis = "ChanGrab(|[flags])\n" "\nTake over the specified channel (ending any call it is currently\n" "involved in.) and bridge that channel to the caller.\n\n" "Flags:\n\n" " -- 'b' Indicates that you want to grab the channel that the\n" " specified channel is Bridged to.\n\n" " -- 'r' Only incercept the channel if the channel has not\n" " been answered yet\n"; /* Prototypes */ int load_module(void); int unload_module(void); char *description(void); static struct ast_channel *my_ast_get_channel_by_name_locked(char *channame) { struct ast_channel *chan; chan = ast_channel_walk_locked(NULL); while (chan) { if (!strncasecmp(chan->name, channame, strlen(channame))) return chan; ast_mutex_unlock(&chan->lock); chan = ast_channel_walk_locked(chan); } return NULL; } static int changrab_exec(struct ast_channel *chan, void *data) { int res = 0; struct ast_channel *newchan; struct ast_channel *oldchan; struct ast_frame *f; struct ast_bridge_config config; char *info = NULL; char *flags = NULL; if (!data || ast_strlen_zero(data)) { ast_log(LOG_WARNING, "changrab requires an argument (chan)\n"); return -1; } if ((info = ast_strdupa((char *) data))) { if ((flags = strchr(info, '|'))) { *flags = '\0'; flags++; } } if ((oldchan = my_ast_get_channel_by_name_locked(info))) { ast_mutex_unlock(&oldchan->lock); } else { ast_log(LOG_WARNING, "No Such Channel: %s\n", (char *) data); return -1; } if (flags && oldchan->_bridge && strchr(flags, 'b')) { oldchan = oldchan->_bridge; } if (flags && strchr(flags, 'r') && oldchan->_state == AST_STATE_UP) { return -1; } newchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "ChanGrab/%s", oldchan->name); newchan->readformat = oldchan->readformat; newchan->writeformat = oldchan->writeformat; ast_channel_masquerade(newchan, oldchan); if ((f = ast_read(newchan))) { ast_frfree(f); memset(&config, 0, sizeof(struct ast_bridge_config)); ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); if (newchan->_state != AST_STATE_UP) { ast_answer(newchan); } chan->appl = "Bridged Call"; res = ast_bridge_call(chan, newchan, &config); ast_hangup(newchan); } return res ? 0 : -1; } struct ast_bridge_thread_obj { struct ast_bridge_config bconfig; struct ast_channel *chan; struct ast_channel *peer; }; static void *ast_bridge_call_thread(void *data) { struct ast_bridge_thread_obj *tobj = data; tobj->chan->appl = "Redirected Call"; tobj->chan->data = tobj->peer->name; tobj->peer->appl = "Redirected Call"; tobj->peer->data = tobj->chan->name; if (tobj->chan->cdr) { ast_cdr_reset(tobj->chan->cdr, 0); ast_cdr_setdestchan(tobj->chan->cdr, tobj->peer->name); } if (tobj->peer->cdr) { ast_cdr_reset(tobj->peer->cdr, 0); ast_cdr_setdestchan(tobj->peer->cdr, tobj->chan->name); } ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig); ast_hangup(tobj->chan); ast_hangup(tobj->peer); tobj->chan = tobj->peer = NULL; free(tobj); tobj = NULL; return NULL; } static void ast_bridge_call_thread_launch(struct ast_channel *chan, struct ast_channel *peer) { pthread_t thread; pthread_attr_t attr; int result; struct ast_bridge_thread_obj *tobj; if ((tobj = malloc(sizeof(struct ast_bridge_thread_obj)))) { memset(tobj, 0, sizeof(struct ast_bridge_thread_obj)); tobj->chan = chan; tobj->peer = peer; result = pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); result = ast_pthread_create(&thread, &attr, ast_bridge_call_thread, tobj); result = pthread_attr_destroy(&attr); } } #define CGUSAGE "Usage: changrab [-[bB]] @ [pri]\n" static int changrab_cli(int fd, int argc, char *argv[]) { char *chan_name_1, *chan_name_2 = NULL, *context, *exten, *flags = NULL; char *pria = NULL; struct ast_channel *xferchan_1, *xferchan_2; int pri = 0, x = 1; if (argc < 3) { ast_cli(fd, CGUSAGE); return -1; } chan_name_1 = argv[x++]; if (chan_name_1[0] == '-') { flags = ast_strdupa(chan_name_1); if (flags && (strchr(flags, 'h'))) { chan_name_1 = argv[x++]; if ((xferchan_1 = my_ast_get_channel_by_name_locked(chan_name_1))) { ast_mutex_unlock(&xferchan_1->lock); ast_hangup(xferchan_1); ast_verbose("OK, good luck!\n"); return 0; } else return -1; } else if (flags && (strchr(flags, 'm') || strchr(flags, 'M'))) { chan_name_1 = argv[x++]; if ((xferchan_1 = my_ast_get_channel_by_name_locked(chan_name_1))) { ast_mutex_unlock(&xferchan_1->lock); strchr(flags, 'm') ? ast_moh_start(xferchan_1, NULL, NULL) : ast_moh_stop(xferchan_1); } else return 1; return 0; } if (argc < 4) { ast_cli(fd, CGUSAGE); return -1; } chan_name_1 = argv[x++]; } exten = ast_strdupa(argv[x++]); if ((context = strchr(exten, '@'))) { *context = 0; context++; if (!(context && exten)) { ast_cli(fd, CGUSAGE); return -1; } if ((pria = strchr(context, ':'))) { *pria = '\0'; pria++; pri = atoi(pria); } else { pri = argv[x] ? atoi(argv[x++]) : 1; } if (!pri) pri = 1; } else if (strchr(exten, '/')) { chan_name_2 = exten; } xferchan_1 = my_ast_get_channel_by_name_locked(chan_name_1); if (!xferchan_1) { ast_log(LOG_WARNING, "No Such Channel: %s\n", chan_name_1); return -1; } ast_mutex_unlock(&xferchan_1->lock); if (flags && strchr(flags, 'b')) { if (ast_bridged_channel(xferchan_1)) { xferchan_1 = ast_bridged_channel(xferchan_1); } } if (chan_name_2) { struct ast_frame *f; struct ast_channel *newchan_1, *newchan_2; if (!(newchan_1 = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "ChanGrab/%s", xferchan_1->name))) { ast_log(LOG_WARNING, "Memory Error!\n"); ast_hangup(newchan_1); return -1; } else { newchan_1->readformat = xferchan_1->readformat; newchan_1->writeformat = xferchan_1->writeformat; ast_channel_masquerade(newchan_1, xferchan_1); if ((f = ast_read(newchan_1))) { ast_frfree(f); } else { ast_hangup(newchan_1); return -1; } } if (!(xferchan_2 = my_ast_get_channel_by_name_locked(chan_name_2))) { ast_log(LOG_WARNING, "No Such Channel: %s\n", chan_name_2); ast_hangup(newchan_1); return -1; } ast_mutex_unlock(&xferchan_2->lock); if (flags && strchr(flags, 'B')) { if (ast_bridged_channel(xferchan_2)) { xferchan_2 = ast_bridged_channel(xferchan_2); } } if (!(newchan_2 = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "ChanGrab/%s", xferchan_2->name))) { ast_log(LOG_WARNING, "Memory Error!\n"); ast_hangup(newchan_1); return -1; } else { newchan_2->readformat = xferchan_2->readformat; newchan_2->writeformat = xferchan_2->writeformat; ast_channel_masquerade(newchan_2, xferchan_2); if ((f = ast_read(newchan_2))) { ast_frfree(f); } else { ast_hangup(newchan_1); ast_hangup(newchan_2); return -1; } } ast_bridge_call_thread_launch(newchan_1, newchan_2); } else { ast_verbose("Transferring_to context %s, extension %s, priority %d\n", context, exten, pri); ast_async_goto(xferchan_1, context, exten, pri); if (xferchan_1) ast_mutex_unlock(&xferchan_1->lock); } return 0; } struct fast_originate_helper { char tech[256]; char data[256]; int timeout; char app[256]; char appdata[256]; char cid_name[256]; char cid_num[256]; char context[256]; char exten[256]; char idtext[256]; int priority; struct ast_variable *vars; }; #define USAGE "Usage: originate @ [pri] [callerid] [timeout]\n" static void *originate(void *arg) { struct fast_originate_helper *in = arg; int reason = 0; struct ast_channel *chan = NULL; ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL, NULL, NULL, &chan); /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */ if (chan) { ast_mutex_unlock(&chan->lock); } free(in); return NULL; } static int originate_cli(int fd, int argc, char *argv[]) { char *chan_name_1, *context, *exten, *tech, *data, *callerid; int pri = 0, to = 60000; struct fast_originate_helper *in; pthread_t thread; pthread_attr_t attr; int result; char *num = NULL; if (argc < 3) { ast_cli(fd, USAGE); return -1; } chan_name_1 = argv[1]; exten = ast_strdupa(argv[2]); if ((context = strchr(exten, '@'))) { *context = 0; context++; } if (!(context && exten)) { ast_cli(fd, CGUSAGE); return -1; } pri = argv[3] ? atoi(argv[3]) : 1; if (!pri) pri = 1; tech = ast_strdupa(chan_name_1); if ((data = strchr(tech, '/'))) { *data = '\0'; data++; } if (!(tech && data)) { ast_cli(fd, USAGE); return -1; } in = malloc(sizeof(struct fast_originate_helper)); if (!in) { ast_cli(fd, "No Memory!\n"); return -1; } memset(in, 0, sizeof(struct fast_originate_helper)); callerid = (argc > 4) ? argv[4] : NULL; to = (argc > 5) ? atoi(argv[5]) : 60000; strncpy(in->tech, tech, sizeof(in->tech)); strncpy(in->data, data, sizeof(in->data)); in->timeout = to; if (callerid) { if ((num = strchr(callerid, ':'))) { *num = '\0'; num++; strncpy(in->cid_num, num, sizeof(in->cid_num)); } strncpy(in->cid_name, callerid, sizeof(in->cid_name)); } strncpy(in->context, context, sizeof(in->context)); strncpy(in->exten, exten, sizeof(in->exten)); in->priority = pri; ast_cli(fd, "Originating Call %s/%s %s %s %d\n", in->tech, in->data, in->context, in->exten, in->priority); result = pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); result = ast_pthread_create(&thread, &attr, originate, in); result = pthread_attr_destroy(&attr); return 0; } static char *complete_exten_at_context(const char *line, const char *word, int pos, int state) { char *ret = NULL; int which = 0; #ifdef BROKEN_READLINE /* * Fix arguments, *word is a new allocated structure, REMEMBER to * free *word when you want to return from this function ... */ if (fix_complete_args(line, &word, &pos)) { ast_log(LOG_ERROR, "Out of free memory\n"); return NULL; } #endif /* * exten@context completion ... */ if (pos == 2) { struct ast_context *c; struct ast_exten *e; char *context = NULL, *exten = NULL, *delim = NULL; /* now, parse values from word = exten@context */ if ((delim = strchr(word, (int) '@'))) { /* check for duplicity ... */ if (delim != strrchr(word, (int) '@')) { #ifdef BROKEN_READLINE free(word); #endif return NULL; } *delim = '\0'; exten = strdup(word); context = strdup(delim + 1); *delim = '@'; } else { exten = strdup(word); } #ifdef BROKEN_READLINE free(word); #endif if (ast_lock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); free(context); free(exten); return NULL; } /* find our context ... */ c = ast_walk_contexts(NULL); while (c) { /* our context? */ if ((!context || !strlen(context)) || /* if no input, all contexts ... */ (context && !strncmp(ast_get_context_name(c), context, strlen(context)))) { /* if input, compare ... */ /* try to complete extensions ... */ e = ast_walk_context_extensions(c, NULL); while (e) { if (!strncasecmp(ast_get_context_name(c), "macro-", 6) || !strncasecmp(ast_get_extension_name(e), "_", 1)) { e = ast_walk_context_extensions(c, e); continue; } /* our extension? */ if ((!exten || !strlen(exten)) || /* if not input, all extensions ... */ (exten && !strncmp(ast_get_extension_name(e), exten, strlen(exten)))) { /* if input, compare ... */ if (++which > state) { /* If there is an extension then return * exten@context. */ if (exten) { ret = malloc(strlen(ast_get_extension_name(e)) + strlen(ast_get_context_name(c)) + 2); if (ret) sprintf(ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c)); } free(exten); free(context); ast_unlock_contexts(); return ret; } } e = ast_walk_context_extensions(c, e); } } c = ast_walk_contexts(c); } ast_unlock_contexts(); free(exten); free(context); return NULL; } /* * Complete priority ... */ if (pos == 3) { char *delim, *exten, *context, *dupline, *duplinet, *ec; struct ast_context *c; dupline = strdup(line); if (!dupline) { #ifdef BROKEN_READLINE free(word); #endif return NULL; } duplinet = dupline; strsep(&duplinet, " "); /* skip 'remove' */ strsep(&duplinet, " "); /* skip 'extension */ if (!(ec = strsep(&duplinet, " "))) { free(dupline); #ifdef BROKEN_READLINE free(word); #endif return NULL; } /* wrong exten@context format? */ if (!(delim = strchr(ec, (int) '@')) || (strchr(ec, (int) '@') != strrchr(ec, (int) '@'))) { #ifdef BROKEN_READLINE free(word); #endif free(dupline); return NULL; } /* check if there is exten and context too ... */ *delim = '\0'; if ((!strlen(ec)) || (!strlen(delim + 1))) { #ifdef BROKEN_READLINE free(word); #endif free(dupline); return NULL; } exten = strdup(ec); context = strdup(delim + 1); free(dupline); if (ast_lock_contexts()) { ast_log(LOG_ERROR, "Failed to lock context list\n"); #ifdef BROKEN_READLINE free(word); #endif free(exten); free(context); return NULL; } /* walk contexts */ c = ast_walk_contexts(NULL); while (c) { if (!strcmp(ast_get_context_name(c), context)) { struct ast_exten *e; /* walk extensions */ free(context); e = ast_walk_context_extensions(c, NULL); while (e) { if (!strcmp(ast_get_extension_name(e), exten)) { struct ast_exten *priority; char buffer[10]; free(exten); priority = ast_walk_extension_priorities(e, NULL); /* serve priorities */ do { snprintf(buffer, 10, "%u", ast_get_extension_priority(priority)); if (!strncmp(word, buffer, strlen(word))) { if (++which > state) { #ifdef BROKEN_READLINE free(word); #endif ast_unlock_contexts(); return strdup(buffer); } } priority = ast_walk_extension_priorities(e, priority); } while (priority); #ifdef BROKEN_READLINE free(word); #endif ast_unlock_contexts(); return NULL; } e = ast_walk_context_extensions(c, e); } #ifdef BROKEN_READLINE free(word); #endif free(exten); ast_unlock_contexts(); return NULL; } c = ast_walk_contexts(c); } #ifdef BROKEN_READLINE free(word); #endif free(exten); free(context); ast_unlock_contexts(); return NULL; } #ifdef BROKEN_READLINE free(word); #endif return NULL; } static char *complete_ch_helper(const char *line, const char *word, int pos, int state) { struct ast_channel *c; int which = 0; char *ret; c = ast_channel_walk_locked(NULL); while (c) { if (!strncasecmp(word, c->name, strlen(word))) { if (++which > state) break; } ast_mutex_unlock(&c->lock); c = ast_channel_walk_locked(c); } if (c) { ret = strdup(c->name); ast_mutex_unlock(&c->lock); } else ret = NULL; return ret; } static char *complete_cg(const char *line, const char *word, int pos, int state) { if (pos == 1) { return complete_ch_helper(line, word, pos, state); } else if (pos >= 2) { return complete_exten_at_context(line, word, pos, state); } return NULL; } static char *complete_org(const char *line, const char *word, int pos, int state) { if (pos >= 2) { return complete_exten_at_context(line, word, pos, state); } return NULL; } static struct ast_cli_entry cli_changrab = { {"changrab", NULL}, changrab_cli, "ChanGrab", "ChanGrab", complete_cg }; static struct ast_cli_entry cli_originate = { {"cgoriginate", NULL}, originate_cli, "Originate", "Originate", complete_org }; int unload_module(void) { ast_cli_unregister(&cli_changrab); ast_cli_unregister(&cli_originate); return ast_unregister_application(app); } int load_module(void) { ast_cli_register(&cli_changrab); ast_cli_register(&cli_originate); return ast_register_application(app, changrab_exec, tdesc, synopsis); } char *description(void) { return tdesc; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Take over an existing channel and bridge to it.");