/*! * \internal * \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly * identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the * local channel and the channel that instigated the park. */ static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten, struct transfer_channel_data *parked_channel_data) { char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1]; struct ast_channel *parkee; struct ast_channel *parkee_side_2; int cause; /* Fill the variable with the extension and context we want to call */ snprintf(destination, sizeof(destination), "%s@%s", exten, context); /* Now we request that chan_local prepare to call the destination */ parkee = ast_request("Local", ast_channel_nativeformats(parker), NULL, parker, destination, &cause); if (!parkee) { return NULL; } /* Before we actually dial out let's inherit appropriate information. */ ast_channel_lock_both(parker, parkee); ast_channel_req_accountcodes(parkee, parker, AST_CHANNEL_REQUESTOR_REPLACEMENT); ast_connected_line_copy_from_caller(ast_channel_connected(parkee), ast_channel_caller(parker)); ast_channel_inherit_variables(parker, parkee); ast_channel_datastore_inherit(parker, parkee); ast_channel_unlock(parker); parkee_side_2 = ast_local_get_peer(parkee); ast_assert(parkee_side_2 != NULL); ast_channel_unlock(parkee); /* We need to have the parker subscribe to the new local channel before hand. */ if (create_parked_subscription_full(parker, ast_channel_uniqueid(parkee_side_2), 1, parked_channel_data)) { ast_channel_unref(parkee_side_2); ast_hangup(parkee); return NULL; } ast_bridge_set_transfer_variables(parkee_side_2, ast_channel_name(parker), 0); ast_channel_unref(parkee_side_2); /* Since the above worked fine now we actually call it and return the channel */ if (ast_call(parkee, destination, 0)) { ast_hangup(parkee); return NULL; } return parkee; }
/*! \brief Thread used for running the Stasis application */ static void *snoop_stasis_thread(void *obj) { RAII_VAR(struct stasis_app_snoop *, snoop, obj, ao2_cleanup); struct ast_app *stasis = pbx_findapp("Stasis"); if (!stasis) { ast_hangup(snoop->chan); return NULL; } pbx_exec(snoop->chan, stasis, ast_str_buffer(snoop->app)); ast_hangup(snoop->chan); return NULL; }
/*! \brief Helper function that creates an outgoing channel and returns it immediately */ static struct ast_channel *dial_transfer(const struct ast_channel *caller, const char *exten, const char *context) { char destination[AST_MAX_EXTENSION+AST_MAX_CONTEXT+1] = ""; struct ast_channel *chan = NULL; int cause; /* Fill the variable with the extension and context we want to call */ snprintf(destination, sizeof(destination), "%s@%s", exten, context); /* Now we request that chan_local prepare to call the destination */ if (!(chan = ast_request("Local", caller->nativeformats, caller, destination, &cause))) { return NULL; } /* Before we actually dial out let's inherit the appropriate dialplan variables */ ast_channel_inherit_variables(caller, chan); /* Since the above worked fine now we actually call it and return the channel */ if (ast_call(chan, destination, 0)) { ast_hangup(chan); return NULL; } return chan; }
static void *bridge_channel_control_thread(void *data) { struct bridge_channel_control_thread_data *thread_data = data; struct ast_channel *bridge_channel = thread_data->bridge_channel; struct stasis_app_control *control = thread_data->control; struct stasis_forward *forward = thread_data->forward; ast_callid callid = ast_channel_callid(bridge_channel); char *bridge_id = ast_strdupa(thread_data->bridge_id); if (callid) { ast_callid_threadassoc_add(callid); } ast_free(thread_data); thread_data = NULL; stasis_app_control_execute_until_exhausted(bridge_channel, control); stasis_app_control_flush_queue(control); stasis_app_bridge_playback_channel_remove(bridge_id, control); stasis_forward_cancel(forward); ao2_cleanup(control); ast_hangup(bridge_channel); return NULL; }
static struct ast_channel *media_request_helper(struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, struct ast_channel_tech *tech, const char *role) { struct ast_channel *chan; ast_callid callid; RAII_VAR(struct ast_unreal_pvt *, pvt, NULL, ao2_cleanup); if (!(pvt = ast_unreal_alloc(sizeof(*pvt), ast_unreal_destructor, cap))) { return NULL; } ast_copy_string(pvt->name, data, sizeof(pvt->name)); ast_set_flag(pvt, AST_UNREAL_NO_OPTIMIZATION); callid = ast_read_threadstorage_callid(); chan = ast_unreal_new_channels(pvt, tech, AST_STATE_UP, AST_STATE_UP, NULL, NULL, assignedids, requestor, callid); if (!chan) { return NULL; } ast_answer(pvt->owner); ast_answer(pvt->chan); if (ast_channel_add_bridge_role(pvt->chan, role)) { ast_hangup(chan); return NULL; } return chan; }
static int offer_session(struct respoke_session *session) { if (session->channel) { return 0; } if (!(session->channel = channel_create(session, AST_STATE_RING, respoke_session_get_exten(session), NULL, NULL))) { return -1; } switch (ast_pbx_start(session->channel)) { case AST_PBX_CALL_LIMIT: ast_log(LOG_WARNING, "PBX call limit reached\n"); case AST_PBX_FAILED: ast_log(LOG_WARNING, "Failed to start PBX\n"); ast_channel_hangupcause_set(session->channel, AST_CAUSE_SWITCH_CONGESTION); ast_hangup(session->channel); return -1; case AST_PBX_SUCCESS: break; } ast_debug(3, "Started PBX on new RESPOKE channel %s\n", ast_channel_name(session->channel)); return 0; }
static struct ast_channel *announce_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause) { struct ast_channel *chan; const char *conf_name = data; /* Allocate a new private structure and then Asterisk channels */ struct announce_pvt *pvt = (struct announce_pvt *) ast_unreal_alloc(sizeof(*pvt), announce_pvt_destructor, cap); if (!pvt) { return NULL; } ast_set_flag(&pvt->base, AST_UNREAL_NO_OPTIMIZATION); ast_copy_string(pvt->base.name, conf_name, sizeof(pvt->base.name)); #if !defined(CS_AST_CHANNEL_CALLID_TYPEDEF) chan = ast_unreal_new_channels(&pvt->base, sccpconf_announce_get_tech(), AST_STATE_UP, AST_STATE_UP, NULL, NULL, assignedids, requestor, NULL); #else chan = ast_unreal_new_channels(&pvt->base, sccpconf_announce_get_tech(), AST_STATE_UP, AST_STATE_UP, NULL, NULL, assignedids, requestor, 0); #endif if (chan) { ast_answer(pvt->base.owner); ast_answer(pvt->base.chan); if (ast_channel_add_bridge_role(pvt->base.chan, "announcer")) { ast_hangup(chan); chan = NULL; } } ao2_cleanup(pvt); return chan; }
static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *context) { struct ast_channel *tmp; tmp = ast_channel_alloc(1); if (tmp) { snprintf(tmp->name, sizeof(tmp->name), "Phone/%s", i->dev + 5); tmp->type = type; tmp->fds[0] = i->fd; /* XXX Switching formats silently causes kernel panics XXX */ tmp->nativeformats = prefformat; tmp->pvt->rawreadformat = prefformat; tmp->pvt->rawwriteformat = prefformat; ast_setstate(tmp, state); if (state == AST_STATE_RING) tmp->rings = 1; tmp->pvt->pvt = i; tmp->pvt->send_digit = phone_digit; tmp->pvt->call = phone_call; tmp->pvt->hangup = phone_hangup; tmp->pvt->answer = phone_answer; tmp->pvt->read = phone_read; tmp->pvt->write = phone_write; tmp->pvt->exception = phone_exception; strncpy(tmp->context, context, sizeof(tmp->context)-1); if (strlen(i->ext)) strncpy(tmp->exten, i->ext, sizeof(tmp->exten)-1); else strncpy(tmp->exten, "s", sizeof(tmp->exten) - 1); if (strlen(i->language)) strncpy(tmp->language, i->language, sizeof(tmp->language)-1); if (strlen(i->callerid)) tmp->callerid = strdup(i->callerid); i->owner = tmp; ast_mutex_lock(&usecnt_lock); usecnt++; ast_mutex_unlock(&usecnt_lock); ast_update_use_count(); if (state != AST_STATE_DOWN) { if (state == AST_STATE_RING) { ioctl(tmp->fds[0], PHONE_RINGBACK); i->cpt = 1; } if (ast_pbx_start(tmp)) { ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); ast_hangup(tmp); } } } else ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); return tmp; }
static void *app_control_dial(struct stasis_app_control *control, struct ast_channel *chan, void *data) { RAII_VAR(struct ast_dial *, dial, ast_dial_create(), ast_dial_destroy); RAII_VAR(struct stasis_app_control_dial_data *, dial_data, data, ast_free); enum ast_dial_result res; char *tech, *resource; struct ast_channel *new_chan; struct ast_bridge *bridge; tech = dial_data->endpoint; if (!(resource = strchr(tech, '/'))) { return NULL; } *resource++ = '\0'; if (!dial) { ast_log(LOG_ERROR, "Failed to create dialing structure.\n"); return NULL; } if (ast_dial_append(dial, tech, resource) < 0) { ast_log(LOG_ERROR, "Failed to add %s/%s to dialing structure.\n", tech, resource); return NULL; } ast_dial_set_global_timeout(dial, dial_data->timeout); res = ast_dial_run(dial, NULL, 0); if (res != AST_DIAL_RESULT_ANSWERED || !(new_chan = ast_dial_answered_steal(dial))) { return NULL; } if (!(bridge = ast_bridge_basic_new())) { ast_log(LOG_ERROR, "Failed to create basic bridge.\n"); return NULL; } if (ast_bridge_impart(bridge, new_chan, NULL, NULL, AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { ast_hangup(new_chan); } else { stasis_app_control_add_channel_to_bridge(control, bridge); } return NULL; }
static struct ast_channel *announce_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause) { struct ast_channel *chan; const char *conf_name = data; RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup); RAII_VAR(struct announce_pvt *, pvt, NULL, ao2_cleanup); conference = ao2_find(conference_bridges, conf_name, OBJ_KEY); if (!conference) { return NULL; } ast_assert(conference->bridge != NULL); /* Allocate a new private structure and then Asterisk channels */ pvt = (struct announce_pvt *) ast_unreal_alloc(sizeof(*pvt), announce_pvt_destructor, cap); if (!pvt) { return NULL; } ast_set_flag(&pvt->base, AST_UNREAL_NO_OPTIMIZATION); ast_copy_string(pvt->base.name, conf_name, sizeof(pvt->base.name)); pvt->bridge = conference->bridge; ao2_ref(pvt->bridge, +1); chan = ast_unreal_new_channels(&pvt->base, conf_announce_get_tech(), AST_STATE_UP, AST_STATE_UP, NULL, NULL, assignedids, requestor, 0); if (chan) { ast_answer(pvt->base.owner); ast_answer(pvt->base.chan); if (ast_channel_add_bridge_role(pvt->base.chan, "announcer")) { ast_hangup(chan); chan = NULL; } } return chan; }
static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags) { struct ast_conference *prev=NULL, *cur; struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user)); int fd; struct zt_confinfo ztc; struct ast_frame *f; struct ast_channel *c; struct ast_frame fr; int outfd; int ms; int nfds; int res; int flags; int retryzap; int origfd; int musiconhold = 0; int firstpass = 0; int origquiet; int ret = -1; int x; int menu_active = 0; int using_pseudo = 0; struct ast_app *app; char *agifile; char *agifiledefault = "conf-background.agi"; char meetmesecs[30] = ""; char exitcontext[AST_MAX_EXTENSION] = ""; int dtmf; ZT_BUFFERINFO bi; char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET]; char *buf = __buf + AST_FRIENDLY_OFFSET; if (!user) { ast_log(LOG_ERROR, "Out of memory\n"); return(ret); } memset(user, 0, sizeof(struct ast_conf_user)); user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */ time(&user->jointime); if (conf->locked) { /* Sorry, but this confernce is locked! */ if (!ast_streamfile(chan, "conf-locked", chan->language)) ast_waitstream(chan, ""); goto outrun; } conf->users++; if (confflags & CONFFLAG_MARKEDUSER) conf->markedusers++; ast_mutex_lock(&conflock); if (conf->firstuser == NULL) { /* Fill the first new User struct */ user->user_no = 1; user->nextuser = NULL; user->prevuser = NULL; conf->firstuser = user; conf->lastuser = user; } else { /* Fill the new user struct */ user->user_no = conf->lastuser->user_no + 1; user->prevuser = conf->lastuser; user->nextuser = NULL; if (conf->lastuser->nextuser != NULL) { ast_log(LOG_WARNING, "Error in User Management!\n"); ast_mutex_unlock(&conflock); goto outrun; } else { conf->lastuser->nextuser = user; conf->lastuser = user; } } user->chan = chan; user->userflags = confflags; user->adminflags = 0; ast_mutex_unlock(&conflock); origquiet = confflags & CONFFLAG_QUIET; if (confflags & CONFFLAG_EXIT_CONTEXT) { if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) strncpy(exitcontext, agifile, sizeof(exitcontext) - 1); else if (!ast_strlen_zero(chan->macrocontext)) strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1); else strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1); } while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers == 0)) { confflags &= ~CONFFLAG_QUIET; confflags |= origquiet; /* XXX Announce that we're waiting on the conference lead to join */ if (!(confflags & CONFFLAG_QUIET)) { res = ast_streamfile(chan, "vm-dialout", chan->language); if (!res) res = ast_waitstream(chan, ""); } else res = 0; /* If we're waiting with hold music, set to silent mode */ if (!res) { confflags |= CONFFLAG_QUIET; ast_moh_start(chan, NULL); res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf); ast_moh_stop(chan); } if (res < 0) { ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name); goto outrun; } } if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) { if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) { if (ast_waitstream(chan, "") < 0) goto outrun; } else goto outrun; } /* Set it into linear mode (write) */ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name); goto outrun; } /* Set it into linear mode (read) */ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) { ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name); goto outrun; } ast_indicate(chan, -1); retryzap = strcasecmp(chan->type, "Zap"); zapretry: origfd = chan->fds[0]; if (retryzap) { fd = open("/dev/zap/pseudo", O_RDWR); if (fd < 0) { ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno)); goto outrun; } using_pseudo = 1; /* Make non-blocking */ flags = fcntl(fd, F_GETFL); if (flags < 0) { ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno)); close(fd); goto outrun; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno)); close(fd); goto outrun; } /* Setup buffering information */ memset(&bi, 0, sizeof(bi)); bi.bufsize = CONF_SIZE/2; bi.txbufpolicy = ZT_POLICY_IMMEDIATE; bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; bi.numbufs = 4; if (ioctl(fd, ZT_SET_BUFINFO, &bi)) { ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno)); close(fd); goto outrun; } x = 1; if (ioctl(fd, ZT_SETLINEAR, &x)) { ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno)); close(fd); goto outrun; } nfds = 1; } else { /* XXX Make sure we're not running on a pseudo channel XXX */ fd = chan->fds[0]; nfds = 0; } memset(&ztc, 0, sizeof(ztc)); /* Check to see if we're in a conference... */ ztc.chan = 0; if (ioctl(fd, ZT_GETCONF, &ztc)) { ast_log(LOG_WARNING, "Error getting conference\n"); close(fd); goto outrun; } if (ztc.confmode) { /* Whoa, already in a conference... Retry... */ if (!retryzap) { ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n"); retryzap = 1; goto zapretry; } } memset(&ztc, 0, sizeof(ztc)); /* Add us to the conference */ ztc.chan = 0; ztc.confno = conf->zapconf; if (confflags & CONFFLAG_MONITOR) ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER; else if (confflags & CONFFLAG_TALKER) ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER; else ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER; if (ioctl(fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference\n"); close(fd); goto outrun; } ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf); manager_event(EVENT_FLAG_CALL, "MeetmeJoin", "Channel: %s\r\n" "Uniqueid: %s\r\n" "Meetme: %s\r\n" "Usernum: %i\r\n", chan->name, chan->uniqueid, conf->confno, user->user_no); if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) { firstpass = 1; if (!(confflags & CONFFLAG_QUIET)) conf_play(conf, ENTER); } if (confflags & CONFFLAG_AGI) { /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND) or use default filename of conf-background.agi */ agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND"); if (!agifile) agifile = agifiledefault; if (!strcasecmp(chan->type,"Zap")) { /* Set CONFMUTE mode on Zap channel to mute DTMF tones */ x = 1; ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0); } /* Find a pointer to the agi app and execute the script */ app = pbx_findapp("agi"); if (app) { ret = pbx_exec(chan, app, agifile, 1); } else { ast_log(LOG_WARNING, "Could not find application (agi)\n"); ret = -2; } if (!strcasecmp(chan->type,"Zap")) { /* Remove CONFMUTE mode on Zap channel */ x = 0; ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0); } } else { if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) { /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */ x = 1; ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0); } for(;;) { outfd = -1; ms = -1; c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms); /* Update the struct with the actual confflags */ user->userflags = confflags; /* trying to add moh for single person conf */ if (confflags & CONFFLAG_MOH) { if (conf->users == 1) { if (musiconhold == 0) { ast_moh_start(chan, NULL); musiconhold = 1; } } else { if (musiconhold) { ast_moh_stop(chan); musiconhold = 0; } } } /* Leave if the last marked user left */ if (conf->markedusers == 0 && confflags & CONFFLAG_MARKEDEXIT) { ret = -1; break; } /* Check if the admin changed my modes */ if (user->adminflags) { /* Set the new modes */ if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) { ztc.confmode ^= ZT_CONF_TALKER; if (ioctl(fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n"); ret = -1; break; } } if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) { ztc.confmode |= ZT_CONF_TALKER; if (ioctl(fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n"); ret = -1; break; } } if (user->adminflags & ADMINFLAG_KICKME) { //You have been kicked. if (!ast_streamfile(chan, "conf-kicked", chan->language)) ast_waitstream(chan, ""); ret = 0; break; } } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) { ztc.confmode |= ZT_CONF_TALKER; if (ioctl(fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n"); ret = -1; break; } } if (c) { if (c->fds[0] != origfd) { if (using_pseudo) { /* Kill old pseudo */ close(fd); } ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n"); retryzap = 0; using_pseudo = 0; goto zapretry; } f = ast_read(c); if (!f) break; if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) { char tmp[2]; tmp[0] = f->subclass; tmp[1] = '\0'; if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->callerid)) { strncpy(chan->context, exitcontext, sizeof(chan->context) - 1); strncpy(chan->exten, tmp, sizeof(chan->exten) - 1); chan->priority = 0; ret = 0; break; } } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) { ret = 0; break; } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) { if (musiconhold) { ast_moh_stop(chan); } if ((confflags & CONFFLAG_ADMIN)) { /* Admin menu */ if (!menu_active) { menu_active = 1; /* Record this sound! */ if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) dtmf = ast_waitstream(chan, AST_DIGIT_ANY); else dtmf = 0; } else dtmf = f->subclass; if (dtmf) { switch(dtmf) { case '1': /* Un/Mute */ menu_active = 0; if (ztc.confmode & ZT_CONF_TALKER) { ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER; confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER; } else { ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER; confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER; } if (ioctl(fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n"); ret = -1; break; } if (ztc.confmode & ZT_CONF_TALKER) { if (!ast_streamfile(chan, "conf-unmuted", chan->language)) ast_waitstream(chan, ""); } else { if (!ast_streamfile(chan, "conf-muted", chan->language)) ast_waitstream(chan, ""); } break; case '2': /* Un/Lock the Conference */ menu_active = 0; if (conf->locked) { conf->locked = 0; if (!ast_streamfile(chan, "conf-unlockednow", chan->language)) ast_waitstream(chan, ""); } else { conf->locked = 1; if (!ast_streamfile(chan, "conf-lockednow", chan->language)) ast_waitstream(chan, ""); } break; default: menu_active = 0; /* Play an error message! */ if (!ast_streamfile(chan, "conf-errormenu", chan->language)) ast_waitstream(chan, ""); break; } } } else { /* User menu */ if (!menu_active) { menu_active = 1; /* Record this sound! */ if (!ast_streamfile(chan, "conf-usermenu", chan->language)) dtmf = ast_waitstream(chan, AST_DIGIT_ANY); else dtmf = 0; } else dtmf = f->subclass; if (dtmf) { switch(dtmf) { case '1': /* Un/Mute */ menu_active = 0; if (ztc.confmode & ZT_CONF_TALKER) { ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER; confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER; } else if (!(user->adminflags & ADMINFLAG_MUTED)) { ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER; confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER; } if (ioctl(fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n"); ret = -1; break; } if (ztc.confmode & ZT_CONF_TALKER) { if (!ast_streamfile(chan, "conf-unmuted", chan->language)) ast_waitstream(chan, ""); } else { if (!ast_streamfile(chan, "conf-muted", chan->language)) ast_waitstream(chan, ""); } break; default: menu_active = 0; /* Play an error message! */ if (!ast_streamfile(chan, "conf-errormenu", chan->language)) ast_waitstream(chan, ""); break; } } } if (musiconhold) { ast_moh_start(chan, NULL); } } else if (using_pseudo) { if (f->frametype == AST_FRAME_VOICE) { if (f->subclass == AST_FORMAT_SLINEAR) { /* Carefully write */ careful_write(fd, f->data, f->datalen); } else ast_log(LOG_WARNING, "Huh? Got a non-linear (%d) frame in the conference\n", f->subclass); } } ast_frfree(f); } else if (outfd > -1) { res = read(outfd, buf, CONF_SIZE); if (res > 0) { memset(&fr, 0, sizeof(fr)); fr.frametype = AST_FRAME_VOICE; fr.subclass = AST_FORMAT_SLINEAR; fr.datalen = res; fr.samples = res/2; fr.data = buf; fr.offset = AST_FRIENDLY_OFFSET; if (ast_write(chan, &fr) < 0) { ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno)); /* break; */ } } else ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno)); } } } if (using_pseudo) close(fd); else { /* Take out of conference */ /* Add us to the conference */ ztc.chan = 0; ztc.confno = 0; ztc.confmode = 0; if (ioctl(fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference\n"); } } if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) conf_play(conf, LEAVE); outrun: ast_mutex_lock(&conflock); if (user->user_no) { /* Only cleanup users who really joined! */ manager_event(EVENT_FLAG_CALL, "MeetmeLeave", "Channel: %s\r\n" "Uniqueid: %s\r\n" "Meetme: %s\r\n" "Usernum: %i\r\n", chan->name, chan->uniqueid, conf->confno, user->user_no); prev = NULL; conf->users--; if (confflags & CONFFLAG_MARKEDUSER) conf->markedusers--; cur = confs; if (!conf->users) { /* No more users -- close this one out */ while(cur) { if (cur == conf) { if (prev) prev->next = conf->next; else confs = conf->next; break; } prev = cur; cur = cur->next; } if (!cur) ast_log(LOG_WARNING, "Conference not found\n"); if (conf->chan) ast_hangup(conf->chan); else close(conf->fd); free(conf); } else { /* Remove the user struct */ if (user == conf->firstuser) { if (user->nextuser) { /* There is another entry */ user->nextuser->prevuser = NULL; } else { /* We are the only entry */ conf->lastuser = NULL; } /* In either case */ conf->firstuser = user->nextuser; } else if (user == conf->lastuser){ if (user->prevuser) user->prevuser->nextuser = NULL; else ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n"); conf->lastuser = user->prevuser; } else { if (user->nextuser) user->nextuser->prevuser = user->prevuser; else ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n"); if (user->prevuser) user->prevuser->nextuser = user->nextuser; else ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n"); } } /* Return the number of seconds the user was in the conf */ snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime)); pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs); } free(user); ast_mutex_unlock(&conflock); return ret; }
static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic) { struct ast_conference *cnf; struct zt_confinfo ztc; ast_mutex_lock(&conflock); cnf = confs; while(cnf) { if (!strcmp(confno, cnf->confno)) break; cnf = cnf->next; } if (!cnf && (make || dynamic)) { cnf = malloc(sizeof(struct ast_conference)); if (cnf) { /* Make a new one */ memset(cnf, 0, sizeof(struct ast_conference)); strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1); strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1); cnf->markedusers = 0; cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo"); if (cnf->chan) { cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */ } else { ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n"); cnf->fd = open("/dev/zap/pseudo", O_RDWR); if (cnf->fd < 0) { ast_log(LOG_WARNING, "Unable to open pseudo device\n"); free(cnf); cnf = NULL; goto cnfout; } } memset(&ztc, 0, sizeof(ztc)); /* Setup a new zap conference */ ztc.chan = 0; ztc.confno = -1; ztc.confmode = ZT_CONF_CONFANN; if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference\n"); if (cnf->chan) ast_hangup(cnf->chan); else close(cnf->fd); free(cnf); cnf = NULL; goto cnfout; } /* Fill the conference struct */ cnf->start = time(NULL); cnf->zapconf = ztc.confno; cnf->isdynamic = dynamic; cnf->firstuser = NULL; cnf->lastuser = NULL; cnf->locked = 0; if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno); cnf->next = confs; confs = cnf; } else ast_log(LOG_WARNING, "Out of memory\n"); } cnfout: ast_mutex_unlock(&conflock); return cnf; }
static inline int at_response_cusd (pvt_t* pvt, char* str, size_t len) { ssize_t res; int type; int dcs; char* text; char text_utf8[1024]; char text_base64[8192]; if (at_parse_cusd (pvt, str, len, &type, &text, &dcs)) { ast_verb (1, "[%s] Error parsing CUSD: '%.*s'\n", pvt->id, (int) len, str); return 0; } if (pvt->ussd_use_ucs2_decoding) { res = conv_ucs2_8bit_hexstr_to_utf8 (text, strlen (text), text_utf8, sizeof (text_utf8)); if (res > 0) { text = text_utf8; } else { ast_log (LOG_ERROR, "[%s] Error parsing CUSD (convert UCS-2 to UTF-8): %s\n", pvt->id, text); return -1; } } else { if (dcs == 0) { res = conv_latin1_8bit_hexstr_to_utf8 (text, strlen (text), text_utf8, sizeof (text_utf8)); } // else if (dcs == 15) // !!!!! else { res = conv_latin1_7bit_hexstr_to_utf8 (text, strlen (text), text_utf8, sizeof (text_utf8)); } if (res > 0) { text = text_utf8; } else { ast_log (LOG_ERROR, "[%s] Error parsing CUSD (convert 8bit/7bit hexstring to UTF-8): %s\n", pvt->id, text); return -1; } } ast_verb (1, "[%s] Got USSD response: '%s'\n", pvt->id, text); ast_base64encode (text_base64, (unsigned char *) text, strlen (text), sizeof (text_base64)); #ifdef __MANAGER__ manager_event_new_ussd (pvt, text); manager_event_new_ussd_base64 (pvt, text_base64); #endif #ifdef __ALLOW_LOCAL_CHANNELS__ struct ast_channel* channel; snprintf (pvt->d_send_buf, sizeof (pvt->d_send_buf), "ussd@%s", pvt->context); if (channel = channel_local_request (pvt, pvt->d_send_buf, pvt->id, "ussd")) { pbx_builtin_setvar_helper (channel, "USSD", text); pbx_builtin_setvar_helper (channel, "USSD_BASE64", text_base64); if (ast_pbx_start (channel)) { ast_hangup (channel); ast_log (LOG_ERROR, "[%s] Unable to start pbx on incoming ussd\n", pvt->id); } } #endif /* __ALLOW_LOCAL_CHANNELS__ */ return 0; }
static inline int at_response_cmgr (pvt_t* pvt, char* str, size_t len) { at_queue_t* e; ssize_t res; char* from_number; char from_number_utf8[1024]; char* text; char text_utf8[1024]; char text_base64[8192]; if ((e = at_fifo_queue_head (pvt)) && e->res == RES_CMGR) { if (pvt->auto_delete_sms && e->ptype == 1) { if (at_send_cmgd (pvt, e->param.num, 0) || at_fifo_queue_add (pvt, CMD_AT_CMGD, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error sending CMGD to delete SMS message\n", pvt->id); } } at_fifo_queue_rem (pvt); pvt->incoming_sms = 0; if (at_parse_cmgr (pvt, str, len, &from_number, &text)) { ast_log (LOG_ERROR, "[%s] Error parsing SMS message, disconnecting\n", pvt->id); return 0; } ast_debug (1, "[%s] Successfully read SMS message\n", pvt->id); if (pvt->use_ucs2_encoding) { res = conv_ucs2_8bit_hexstr_to_utf8 (text, strlen (text), text_utf8, sizeof (text_utf8)); if (res > 0) { text = text_utf8; } else { ast_log (LOG_ERROR, "[%s] Error parsing SMS (convert UCS-2 to UTF-8): %s\n", pvt->id, text); } res = conv_ucs2_8bit_hexstr_to_utf8 (from_number, strlen (from_number), from_number_utf8, sizeof (from_number_utf8)); if (res > 0) { from_number = from_number_utf8; } else { ast_log (LOG_ERROR, "[%s] Error parsing SMS from_number (convert UCS-2 to UTF-8): %s\n", pvt->id, from_number); } } ast_base64encode (text_base64, (unsigned char *) text, strlen (text), sizeof (text_base64)); ast_verb (1, "[%s] Got SMS from %s: '%s'\n", pvt->id, from_number, text); #ifdef __MANAGER__ manager_event_new_sms (pvt, from_number, text); manager_event_new_sms_base64 (pvt, from_number, text_base64); #endif #ifdef __ALLOW_LOCAL_CHANNELS__ struct ast_channel* channel; snprintf (pvt->d_send_buf, sizeof (pvt->d_send_buf), "sms@%s", pvt->context); if (channel = channel_local_request (pvt, pvt->d_send_buf, pvt->id, from_number)) { pbx_builtin_setvar_helper (channel, "SMS", text); pbx_builtin_setvar_helper (channel, "SMS_BASE64", text_base64); if (ast_pbx_start (channel)) { ast_hangup (channel); ast_log (LOG_ERROR, "[%s] Unable to start pbx on incoming sms\n", pvt->id); } } #endif /* __ALLOW_LOCAL_CHANNELS__ */ } else if (e) { ast_log (LOG_ERROR, "[%s] Received '+CMGR' when expecting '%s' response to '%s', ignoring\n", pvt->id, at_res2str (e->res), at_cmd2str (e->cmd)); } else { ast_log (LOG_ERROR, "[%s] Received unexpected '+CMGR'\n", pvt->id); } return 0; }
/*! \brief Internal built in feature for attended transfers */ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) { char exten[AST_MAX_EXTENSION] = ""; struct ast_channel *chan = NULL; struct ast_bridge *attended_bridge = NULL; struct ast_bridge_features caller_features, called_features; enum ast_bridge_channel_state attended_bridge_result; struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt; const char *context = (attended_transfer && !ast_strlen_zero(attended_transfer->context) ? attended_transfer->context : bridge_channel->chan->context); /* Grab the extension to transfer to */ if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) { ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); return 0; } /* Get a channel that is the destination we wish to call */ if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) { ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); return 0; } /* Create a bridge to use to talk to the person we are calling */ if (!(attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) { ast_hangup(chan); ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); return 0; } /* Setup our called features structure so that if they hang up we immediately get thrown out of the bridge */ ast_bridge_features_init(&called_features); ast_bridge_features_set_flag(&called_features, AST_BRIDGE_FLAG_DISSOLVE); /* This is how this is going down, we are imparting the channel we called above into this bridge first */ ast_bridge_impart(attended_bridge, chan, NULL, &called_features); /* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */ ast_bridge_features_init(&caller_features); ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP, (attended_transfer && !ast_strlen_zero(attended_transfer->complete) ? attended_transfer->complete : "*1"), NULL); ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->threeway) ? attended_transfer->threeway : "*2"), attended_threeway_transfer, NULL); ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->abort) ? attended_transfer->abort : "*3"), attended_abort_transfer, NULL); /* But for the caller we want to join the bridge in a blocking fashion so we don't spin around in this function doing nothing while waiting */ attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features); /* Since the above returned the caller features structure is of no more use */ ast_bridge_features_cleanup(&caller_features); /* Drop the channel we are transferring to out of the above bridge since it has ended */ if ((attended_bridge_result != AST_BRIDGE_CHANNEL_STATE_HANGUP) && !ast_bridge_depart(attended_bridge, chan)) { /* If the user wants to turn this into a threeway transfer then do so, otherwise they take our place */ if (attended_bridge_result == AST_BRIDGE_CHANNEL_STATE_DEPART) { /* We want to impart them upon the bridge and just have us return to it as normal */ ast_bridge_impart(bridge, chan, NULL, NULL); } else { ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL); } } else { ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); } /* Now that all channels are out of it we can destroy the bridge and the called features structure */ ast_bridge_features_cleanup(&called_features); ast_bridge_destroy(attended_bridge); return 0; }
static void *dialstring(void *string) { struct ast_channel *channel; char *bufptr,*destptr; int ms=10000; /* ms affects number of rings */ int cnt=0,first; char tech[256]; char tele[256]; char filename[256]; int answered=0; for(first=0, bufptr=(char *)string, destptr=tech; *bufptr&&cnt<256; cnt++){ if(*bufptr=='/' && !first) { *destptr=0; destptr=tele; first=1; } else if(*bufptr==',') { *destptr=0; destptr=filename; } else { *destptr=*bufptr; destptr++; } bufptr++; } *destptr=0; ast_log(LOG_DEBUG, "Printing string arg: %s Eos\n", (char *)string); if(strlen(tech)+strlen(tele)+strlen(filename) > 256) { ast_log(LOG_ERROR, "Autodial:Error string too long\n"); free(string); pthread_exit(NULL); } ast_log(LOG_DEBUG, "Autodial Tech %s(%d) Tele %s(%d) Filename %s(%d)\n",tech, (int)strlen(tech), tele, (int)strlen(tele), filename, (int)strlen(filename)); channel=ast_request(tech,AST_FORMAT_SLINEAR,tele); if(channel!=NULL){ ast_call(channel,tele,10000); } else { ast_log(LOG_ERROR, "Autodial:Sorry unable to obtain channel\n"); free(string); pthread_exit(NULL); } if(channel->_state==AST_STATE_UP) ast_log(LOG_DEBUG, "Autodial:Line is Up\n"); while(ms>0){ struct ast_frame *f; ms=ast_waitfor(channel,ms); f=ast_read(channel); if(!f){ ast_log(LOG_DEBUG, "Autodial:Hung Up\n"); break; } if (f->frametype==AST_FRAME_CONTROL) { if (f->subclass==AST_CONTROL_ANSWER) { ast_log(LOG_DEBUG, "Autodial:Phone Answered\n"); if (channel->_state==AST_STATE_UP) { char res; ast_streamfile(channel,filename,0); /* Press Five for snooze */ res=ast_waitstream(channel, "37"); if(res=='3'){ answered=1; set_snooze_alarm((char *)string,60); ast_streamfile(channel,"demo-thanks",0); ast_waitstream(channel, ""); } else if(res=='7'){ answered=1; ast_streamfile(channel,"demo-thanks",0); ast_waitstream(channel, ""); } ast_stopstream(channel); ms=0; } } else if (f->subclass==AST_CONTROL_RINGING) ast_log(LOG_DEBUG, "Autodial:Phone Ringing end\n"); } ast_frfree(f); } if(!answered) set_snooze_alarm((char *) string, 5); free(string); ast_hangup(channel); ast_log(LOG_DEBUG, "Autodial:Hung up channel\n"); pthread_exit(NULL); return NULL; }
/*! * \internal * \brief Determine if an extension is a parking extension */ static int parking_is_exten_park(const char *context, const char *exten) { struct ast_exten *exten_obj; struct pbx_find_info info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */ const char *app_at_exten; ast_debug(4, "Checking if %s@%s is a parking exten\n", exten, context); exten_obj = pbx_find_extension(NULL, NULL, &info, context, exten, 1, NULL, NULL, E_MATCH); if (!exten_obj) { return 0; } app_at_exten = ast_get_extension_app(exten_obj); if (!app_at_exten || strcasecmp(PARK_APPLICATION, app_at_exten)) { return 0; } return 1; } /*! * \internal * \since 12.0.0 * \brief Perform a blind transfer to a parking lot * * In general, most parking features should work to call this function. This will safely * park either a channel in the bridge with \ref bridge_channel or will park the entire * bridge if more than one channel is in the bridge. It will create the correct data to * pass to the \ref AstBridging Bridging API to safely park the channel. * * \param bridge_channel The bridge_channel representing the channel performing the park * \param context The context to blind transfer to * \param exten The extension to blind transfer to * \param parked_channel_cb Optional callback executed prior to sending the parked channel into the bridge * \param parked_channel_data Data for the parked_channel_cb * * \retval 0 on success * \retval non-zero on error */ static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel, const char *context, const char *exten, transfer_channel_cb parked_channel_cb, struct transfer_channel_data *parked_channel_data) { RAII_VAR(struct ast_bridge_channel *, other, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, other_chan, NULL, ast_channel_cleanup); struct ast_exten *e; struct pbx_find_info find_info = { .stacklen = 0 }; int peer_count; if (ast_strlen_zero(context) || ast_strlen_zero(exten)) { return -1; } if (!bridge_channel->in_bridge) { return -1; } if (!parking_is_exten_park(context, exten)) { return -1; } ast_bridge_channel_lock_bridge(bridge_channel); peer_count = bridge_channel->bridge->num_channels; if (peer_count == 2) { other = ast_bridge_channel_peer(bridge_channel); ao2_ref(other, +1); other_chan = other->chan; ast_channel_ref(other_chan); } ast_bridge_unlock(bridge_channel->bridge); if (peer_count < 2) { /* There is nothing to do if there is no one to park. */ return -1; } /* With a multiparty bridge, we need to do a regular blind transfer. We link the * existing bridge to the parking lot with a Local channel rather than * transferring others. */ if (peer_count > 2) { struct ast_channel *transfer_chan = NULL; transfer_chan = park_local_transfer(bridge_channel->chan, context, exten, parked_channel_data); if (!transfer_chan) { return -1; } ast_channel_ref(transfer_chan); if (parked_channel_cb) { parked_channel_cb(transfer_chan, parked_channel_data, AST_BRIDGE_TRANSFER_MULTI_PARTY); } if (ast_bridge_impart(bridge_channel->bridge, transfer_chan, NULL, NULL, AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { ast_hangup(transfer_chan); ast_channel_unref(transfer_chan); return -1; } ast_channel_unref(transfer_chan); return 0; } /* Subscribe to park messages with the other channel entering */ if (create_parked_subscription_full(bridge_channel->chan, ast_channel_uniqueid(other->chan), 1, parked_channel_data)) { return -1; } if (parked_channel_cb) { parked_channel_cb(other_chan, parked_channel_data, AST_BRIDGE_TRANSFER_SINGLE_PARTY); } e = pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH); /* Write the park frame with the intended recipient and other data out to the bridge. */ ast_bridge_channel_write_park(bridge_channel, ast_channel_uniqueid(other_chan), ast_channel_uniqueid(bridge_channel->chan), e ? ast_get_extension_app_data(e) : NULL); return 0; } /*! * \internal * \since 12.0.0 * \brief Perform a direct park on a channel in a bridge * * \note This will be called from within the \ref AstBridging Bridging API * * \param bridge_channel The bridge_channel representing the channel to be parked * \param uuid_parkee The UUID of the channel being parked * \param uuid_parker The UUID of the channel performing the park * \param app_data Application parseable data to pass to the parking application */ static int parking_park_bridge_channel(struct ast_bridge_channel *bridge_channel, const char *uuid_parkee, const char *uuid_parker, const char *app_data) { RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge *, original_bridge, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup); if (strcmp(ast_channel_uniqueid(bridge_channel->chan), uuid_parkee)) { /* We aren't the parkee, so ignore this action. */ return -1; } parker = ast_channel_get_by_name(uuid_parker); if (!parker) { ast_log(LOG_NOTICE, "Channel with uuid %s left before we could start parking the call. Parking canceled.\n", uuid_parker); publish_parked_call_failure(bridge_channel->chan); return -1; } if (!(parking_bridge = park_application_setup(bridge_channel->chan, parker, app_data, NULL))) { publish_parked_call_failure(bridge_channel->chan); return -1; } ast_bridge_set_transfer_variables(bridge_channel->chan, ast_channel_name(parker), 0); /* bridge_channel must be locked so we can get a reference to the bridge it is currently on */ ao2_lock(bridge_channel); original_bridge = bridge_channel->bridge; if (!original_bridge) { ao2_unlock(bridge_channel); publish_parked_call_failure(bridge_channel->chan); return -1; } ao2_ref(original_bridge, +1); /* Cleaned by RAII_VAR */ ao2_unlock(bridge_channel); if (ast_bridge_move(parking_bridge, original_bridge, bridge_channel->chan, NULL, 1)) { ast_log(LOG_ERROR, "Failed to move %s into the parking bridge.\n", ast_channel_name(bridge_channel->chan)); return -1; } return 0; }
struct ast_channel *stasis_app_control_snoop(struct ast_channel *chan, enum stasis_app_snoop_direction spy, enum stasis_app_snoop_direction whisper, const char *app, const char *app_args, const char *snoop_id) { RAII_VAR(struct stasis_app_snoop *, snoop, NULL, ao2_cleanup); struct ast_format_cap *caps; pthread_t thread; struct ast_assigned_ids assignedids = { .uniqueid = snoop_id, }; if (spy == STASIS_SNOOP_DIRECTION_NONE && whisper == STASIS_SNOOP_DIRECTION_NONE) { return NULL; } snoop = ao2_alloc_options(sizeof(*snoop), snoop_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!snoop) { return NULL; } /* Allocate a buffer to store the Stasis application and arguments in */ snoop->app = ast_str_create(64); if (!snoop->app) { return NULL; } ast_str_set(&snoop->app, 0, "%s", app); if (!ast_strlen_zero(app_args)) { ast_str_append(&snoop->app, 0, ",%s", app_args); } /* Set up a timer for the Snoop channel so it wakes up at a specific interval */ snoop->timer = ast_timer_open(); if (!snoop->timer) { return NULL; } ast_timer_set_rate(snoop->timer, 1000 / SNOOP_INTERVAL); /* Determine which signed linear format should be used */ snoop_determine_format(chan, snoop); /* Allocate a Snoop channel and set up various parameters */ snoop->chan = ast_channel_alloc(1, AST_STATE_UP, "", "", "", "", "", &assignedids, NULL, 0, "Snoop/%s-%08x", ast_channel_uniqueid(chan), (unsigned)ast_atomic_fetchadd_int((int *)&chan_idx, +1)); if (!snoop->chan) { return NULL; } ast_copy_string(snoop->uniqueid, ast_channel_uniqueid(chan), sizeof(snoop->uniqueid)); /* To keep the channel valid on the Snoop structure until it is destroyed we bump the ref up here */ ast_channel_ref(snoop->chan); ast_channel_tech_set(snoop->chan, &snoop_tech); ao2_ref(snoop, +1); ast_channel_tech_pvt_set(snoop->chan, snoop); ast_channel_set_fd(snoop->chan, 0, ast_timer_fd(snoop->timer)); /* The format on the Snoop channel will be this signed linear format, and it will never change */ caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (!caps) { ast_channel_unlock(snoop->chan); ast_hangup(snoop->chan); return NULL; } ast_format_cap_append(caps, snoop->spy_format, 0); ast_channel_nativeformats_set(snoop->chan, caps); ao2_ref(caps, -1); ast_channel_set_writeformat(snoop->chan, snoop->spy_format); ast_channel_set_rawwriteformat(snoop->chan, snoop->spy_format); ast_channel_set_readformat(snoop->chan, snoop->spy_format); ast_channel_set_rawreadformat(snoop->chan, snoop->spy_format); ast_channel_unlock(snoop->chan); if (spy != STASIS_SNOOP_DIRECTION_NONE) { if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_SPY, spy, &snoop->spy_direction, &snoop->spy)) { ast_hangup(snoop->chan); return NULL; } snoop->spy_samples = ast_format_get_sample_rate(snoop->spy_format) / (1000 / SNOOP_INTERVAL); snoop->spy_active = 1; } /* If whispering is enabled set up the audiohook */ if (whisper != STASIS_SNOOP_DIRECTION_NONE) { if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_WHISPER, whisper, &snoop->whisper_direction, &snoop->whisper)) { ast_hangup(snoop->chan); return NULL; } snoop->whisper_active = 1; } /* Create the thread which services the Snoop channel */ ao2_ref(snoop, +1); if (ast_pthread_create_detached_background(&thread, NULL, snoop_stasis_thread, snoop)) { ao2_cleanup(snoop); /* No other thread is servicing this channel so we can immediately hang it up */ ast_hangup(snoop->chan); return NULL; } publish_chanspy_message(snoop, 1); /* The caller of this has a reference as well */ return ast_channel_ref(snoop->chan); }
/*! \brief Initiate new call, part of PBX interface * dest is the dial string */ static int local_call(struct ast_channel *ast, const char *dest, int timeout) { struct local_pvt *p = ast_channel_tech_pvt(ast); int pvt_locked = 0; struct ast_channel *owner = NULL; struct ast_channel *chan = NULL; int res; char *reduced_dest = ast_strdupa(dest); char *slash; const char *chan_cid; if (!p) { return -1; } /* since we are letting go of channel locks that were locked coming into * this function, then we need to give the tech pvt a ref */ ao2_ref(p, 1); ast_channel_unlock(ast); ast_unreal_lock_all(&p->base, &chan, &owner); pvt_locked = 1; if (owner != ast) { res = -1; goto return_cleanup; } if (!owner || !chan) { res = -1; goto return_cleanup; } ast_unreal_call_setup(owner, chan); /* * If the local channel has /n on the end of it, we need to lop * that off for our argument to setting up the CC_INTERFACES * variable. */ if ((slash = strrchr(reduced_dest, '/'))) { *slash = '\0'; } ast_set_cc_interfaces_chanvar(chan, reduced_dest); ao2_unlock(p); pvt_locked = 0; ast_channel_unlock(owner); chan_cid = S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL); if (chan_cid) { chan_cid = ast_strdupa(chan_cid); } ast_channel_unlock(chan); res = -1; switch (p->type) { case LOCAL_CALL_ACTION_DIALPLAN: if (!ast_exists_extension(NULL, p->context, p->exten, 1, chan_cid)) { ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->exten, p->context); } else { publish_local_bridge_message(p); /* Start switch on sub channel */ res = ast_pbx_start(chan); } break; case LOCAL_CALL_ACTION_BRIDGE: publish_local_bridge_message(p); ast_answer(chan); res = ast_bridge_impart(p->action.bridge.join, chan, p->action.bridge.swap, p->action.bridge.features, AST_BRIDGE_IMPART_CHAN_INDEPENDENT); ao2_ref(p->action.bridge.join, -1); p->action.bridge.join = NULL; ao2_cleanup(p->action.bridge.swap); p->action.bridge.swap = NULL; p->action.bridge.features = NULL; break; case LOCAL_CALL_ACTION_MASQUERADE: publish_local_bridge_message(p); ast_answer(chan); res = ast_channel_move(p->action.masq, chan); if (!res) { /* Chan is now an orphaned zombie. Destroy it. */ ast_hangup(chan); } p->action.masq = ast_channel_unref(p->action.masq); break; } if (!res) { ao2_lock(p); ast_set_flag(&p->base, AST_UNREAL_CARETAKER_THREAD); ao2_unlock(p); } /* we already unlocked them, clear them here so the cleanup label won't touch them. */ owner = ast_channel_unref(owner); chan = ast_channel_unref(chan); return_cleanup: if (p) { if (pvt_locked) { ao2_unlock(p); } ao2_ref(p, -1); } if (chan) { ast_channel_unlock(chan); ast_channel_unref(chan); } /* * owner is supposed to be == to ast, if it is, don't unlock it * because ast must exit locked */ if (owner) { if (owner != ast) { ast_channel_unlock(owner); ast_channel_lock(ast); } ast_channel_unref(owner); } else { /* we have to exit with ast locked */ ast_channel_lock(ast); } return res; }
/* Needs to be called with d->lock */ sccp_channel_t * sccp_dev_allocate_channel(sccp_device_t * d, sccp_line_t * l, int outgoing, char * dial) { sccp_channel_t * c = NULL; struct ast_channel * tmp = NULL; pthread_t t; int callId; if (!d->session) { ast_log(LOG_ERROR, "Tried to open channel on device without a session\n"); return NULL; } // If there is no current line, then we can't make a call in, or out. if (!d->currentLine) { ast_log(LOG_ERROR, "Tried to open channel on a device with no selected line\n"); return NULL; } if (l == NULL) l = d->currentLine; ast_mutex_lock(&callCountLock); callId = callCount++; ast_mutex_unlock(&callCountLock); c = malloc(sizeof(sccp_channel_t)); memset(c, 0, sizeof(sccp_channel_t)); c->callid = callId; c->line = l; ast_mutex_lock(&l->lock); l->channelCount++; ast_mutex_unlock(&l->lock); ast_log(LOG_DEBUG, "After: #Channel ->lnext = %p, c = %p, channels = %p\n", c->lnext, c, chans); tmp = sccp_new_channel(c, AST_STATE_OFFHOOK); ast_log(LOG_DEBUG, "New channel name is: %s\n", tmp->name); ast_log(LOG_DEBUG, "After: #Channel ->lnext = %p, c = %p, channels = %p\n", c->lnext, c, chans); ast_mutex_lock(&chanlock); c->lnext = chans; chans = c; ast_mutex_unlock(&chanlock); c->owner = tmp; c->next = l->channels; l->channels = c; l->activeChannel = c; if (outgoing) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); c->isOutgoing = 1; d->active_channel = c; ast_log(LOG_DEBUG, "After: #Channel ->lnext = %p, c = %p, channels = %p\n", c->lnext, c, chans); sccp_dev_set_speaker(d, StationSpeakerOn); sccp_channel_set_callstate(c, TsOffHook); sccp_dev_statusprompt_set(d, c, NULL, 0); sccp_dev_set_keyset(d, c, KEYMODE_OFFHOOK); sccp_dev_set_sptone(d, "InsideDialTone"); if (dial) { strncpy(tmp->exten, dial, AST_MAX_EXTENSION); if (ast_pbx_start(tmp)) { ast_log(LOG_WARNING, "PBX exited non-zero\n"); sccp_dev_statusprompt_set(l->device, c, "PBX Error", 10); sccp_dev_set_sptone(l->device, "ReorderTone"); ast_indicate(tmp, AST_CONTROL_CONGESTION); } ast_log(LOG_DEBUG, "After: #Channel ->lnext = %p, c = %p, channels = %p\n", c->lnext, c, chans); } else if (pthread_create(&t, &attr, sccp_start_channel, tmp)) { ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno)); ast_hangup(tmp); free(c); return NULL; } } else { // It's an incoming call. } ast_log(LOG_DEBUG, "After: #Channel ->lnext = %p, c = %p, chans = %p\n", c->lnext, c, chans); return c; }
static int chanavail_exec(struct ast_channel *chan, void *data) { int res=-1, inuse=-1, option_state=0, priority_jump=0; int status; struct localuser *u; char *info, tmp[512], trychan[512], *peers, *tech, *number, *rest, *cur, *options, *stringp; struct ast_channel *tempchan; if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "ChanIsAvail requires an argument (Zap/1&Zap/2)\n"); return -1; } LOCAL_USER_ADD(u); info = ast_strdupa(data); stringp = info; strsep(&stringp, "|"); options = strsep(&stringp, "|"); if (options) { if (strchr(options, 's')) option_state = 1; if (strchr(options, 'j')) priority_jump = 1; } peers = info; if (peers) { cur = peers; do { /* remember where to start next time */ rest = strchr(cur, '&'); if (rest) { *rest = 0; rest++; } tech = cur; number = strchr(tech, '/'); if (!number) { ast_log(LOG_WARNING, "ChanIsAvail argument takes format ([technology]/[device])\n"); LOCAL_USER_REMOVE(u); return -1; } *number = '\0'; number++; if (option_state) { /* If the pbx says in use then don't bother trying further. This is to permit testing if someone's on a call, even if the channel can permit more calls (ie callwaiting, sip calls, etc). */ snprintf(trychan, sizeof(trychan), "%s/%s",cur,number); status = inuse = ast_device_state(trychan); } if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status))) { pbx_builtin_setvar_helper(chan, "AVAILCHAN", tempchan->name); /* Store the originally used channel too */ snprintf(tmp, sizeof(tmp), "%s/%s", tech, number); pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", tmp); snprintf(tmp, sizeof(tmp), "%d", status); pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp); ast_hangup(tempchan); tempchan = NULL; res = 1; break; } else { snprintf(tmp, sizeof(tmp), "%d", status); pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp); } cur = rest; } while (cur); } if (res < 1) { pbx_builtin_setvar_helper(chan, "AVAILCHAN", ""); pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", ""); if (priority_jump || option_priority_jumping) { if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { LOCAL_USER_REMOVE(u); return -1; } } } LOCAL_USER_REMOVE(u); return 0; }
int ast_unreal_hangup(struct ast_unreal_pvt *p, struct ast_channel *ast) { int hangup_chan = 0; int res = 0; int cause; struct ast_channel *owner = NULL; struct ast_channel *chan = NULL; /* the pvt isn't going anywhere, it has a ref */ ast_channel_unlock(ast); /* lock everything */ ast_unreal_lock_all(p, &chan, &owner); if (ast != chan && ast != owner) { res = -1; goto unreal_hangup_cleanup; } cause = ast_channel_hangupcause(ast); if (ast == p->chan) { /* Outgoing side is hanging up. */ ast_clear_flag(p, AST_UNREAL_CARETAKER_THREAD); p->chan = NULL; if (p->owner) { const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS"); if (status) { ast_channel_hangupcause_set(p->owner, cause); pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status); } ast_queue_hangup_with_cause(p->owner, cause); } } else { /* Owner side is hanging up. */ p->owner = NULL; if (p->chan) { if (cause == AST_CAUSE_ANSWERED_ELSEWHERE) { ast_channel_hangupcause_set(p->chan, AST_CAUSE_ANSWERED_ELSEWHERE); ast_debug(2, "%s has AST_CAUSE_ANSWERED_ELSEWHERE set.\n", ast_channel_name(p->chan)); } if (!ast_test_flag(p, AST_UNREAL_CARETAKER_THREAD)) { /* * Need to actually hangup p->chan since nothing else is taking * care of it. */ hangup_chan = 1; } else { ast_queue_hangup_with_cause(p->chan, cause); } } } /* this is one of our locked channels, doesn't matter which */ ast_channel_tech_pvt_set(ast, NULL); ao2_ref(p, -1); unreal_hangup_cleanup: ao2_unlock(p); if (owner) { ast_channel_unlock(owner); ast_channel_unref(owner); } if (chan) { ast_channel_unlock(chan); if (hangup_chan) { ast_hangup(chan); } ast_channel_unref(chan); } /* leave with the channel locked that came in */ ast_channel_lock(ast); return res; }