diff options
-rw-r--r-- | apps/app_dial.c | 9 | ||||
-rw-r--r-- | apps/app_meetme.c | 163 | ||||
-rw-r--r-- | channels/chan_zap.c | 30 | ||||
-rw-r--r-- | channels/iax2-parser.c | 13 | ||||
-rw-r--r-- | devicestate.c | 2 | ||||
-rw-r--r-- | include/asterisk/devicestate.h | 2 | ||||
-rw-r--r-- | include/asterisk/pbx.h | 1 | ||||
-rw-r--r-- | pbx.c | 19 |
8 files changed, 220 insertions, 19 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c index 25fa15aba..f2834885d 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -728,10 +728,13 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_l if (ast_write(outgoing->chan, f)) ast_log(LOG_WARNING, "Unable to forward voice\n"); } - if (single && (f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_VIDUPDATE)) { + if (single && (f->frametype == AST_FRAME_CONTROL) && + ((f->subclass == AST_CONTROL_HOLD) || + (f->subclass == AST_CONTROL_UNHOLD) || + (f->subclass == AST_CONTROL_VIDUPDATE))) { if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", in->name,outgoing->chan->name); - ast_indicate(outgoing->chan, AST_CONTROL_VIDUPDATE); + ast_verbose(VERBOSE_PREFIX_3 "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name); + ast_indicate_data(outgoing->chan, f->subclass, f->data, f->datalen); } ast_frfree(f); } diff --git a/apps/app_meetme.c b/apps/app_meetme.c index 226d8fe12..f027420c6 100644 --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -145,11 +145,17 @@ enum { /*! If set, won't speak the extra prompt when the first person * enters the conference */ CONFFLAG_NOONLYPERSON = (1 << 22), - CONFFLAG_INTROUSERNOREVIEW = (1 << 23), /*! If set, user will be asked to record name on entry of conference * without review */ - CONFFLAG_STARTMUTED = (1 << 24) + CONFFLAG_INTROUSERNOREVIEW = (1 << 23), /*! If set, the user will be initially self-muted */ + CONFFLAG_STARTMUTED = (1 << 24), + /*! If set, the user is a shared line appearance station */ + CONFFLAG_SLA_STATION = (1 << 25), + /*! If set, the user is a shared line appearance trunk */ + CONFFLAG_SLA_TRUNK = (1 << 26), + /*! If set, the user has put us on hold */ + CONFFLAG_HOLD = (1 << 27) }; AST_APP_OPTIONS(meetme_opts, { @@ -332,7 +338,9 @@ struct ast_conf_user { int talking; /*!< Is user talking */ int zapchannel; /*!< Is a Zaptel channel */ char usrvalue[50]; /*!< Custom User Value */ - char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */ + char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */ + int control; /*! Queue Control for transmission */ + int dtmf; /*! Queue DTMF for transmission */ time_t jointime; /*!< Time the user joined the conference */ struct volume talk; struct volume listen; @@ -809,7 +817,7 @@ static int conf_cmd(int fd, int argc, char **argv) min = ((now - user->jointime) % 3600) / 60; sec = (now - user->jointime) % 60; if ( !concise ) - ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n", + ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n", user->user_no, S_OR(user->chan->cid.cid_num, "<unknown>"), S_OR(user->chan->cid.cid_name, "<no name>"), @@ -817,7 +825,8 @@ static int conf_cmd(int fd, int argc, char **argv) user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "", user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "", user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "", - istalking(user->talking), hr, min, sec); + istalking(user->talking), + user->userflags & CONFFLAG_HOLD ? " (On Hold) " : "", hr, min, sec); else ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n", user->user_no, @@ -980,6 +989,23 @@ static int conf_free(struct ast_conference *conf) return 0; } +static void conf_queue_dtmf(struct ast_conference *conf, int digit) +{ + struct ast_conf_user *user; + AST_LIST_TRAVERSE(&conf->userlist, user, list) { + user->dtmf = digit; + } +} + +static void conf_queue_control(struct ast_conference *conf, int control) +{ + struct ast_conf_user *user; + AST_LIST_TRAVERSE(&conf->userlist, user, list) { + user->control = control; + } +} + + static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags) { struct ast_conf_user *user = NULL; @@ -1087,6 +1113,8 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c /* This device changed state now - if this is the first user */ if (conf->users == 1) ast_device_state_changed("meetme:%s", conf->confno); + if (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK)) + ast_device_state_changed("SLA:%s", conf->confno + 4); ast_mutex_unlock(&conf->playlock); @@ -1549,6 +1577,17 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER)) careful_write(fd, f->data, f->datalen, 0); } + } else if ((f->frametype == AST_FRAME_DTMF) && + (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))) { + conf_queue_dtmf(conf, f->subclass); + } else if ((f->frametype == AST_FRAME_CONTROL) && + (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))) { + conf_queue_control(conf, f->subclass); + if (f->subclass == AST_CONTROL_HOLD) + confflags |= CONFFLAG_HOLD; + else if (f->subclass == AST_CONTROL_UNHOLD) + confflags &= ~CONFFLAG_HOLD; + user->userflags = confflags; } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) { char tmp[2]; @@ -1727,6 +1766,33 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c } ast_frfree(f); } else if (outfd > -1) { + if (user->control) { + switch(user->control) { + case AST_CONTROL_RINGING: + case AST_CONTROL_PROGRESS: + case AST_CONTROL_PROCEEDING: + ast_indicate(chan, user->control); + break; + case AST_CONTROL_ANSWER: + if (chan->_state != AST_STATE_UP) + ast_answer(chan); + break; + } + user->control = 0; + if (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK)) + ast_device_state_changed("SLA:%s", conf->confno + 4); + continue; + } + if (user->dtmf) { + memset(&fr, 0, sizeof(fr)); + fr.frametype = AST_FRAME_DTMF; + fr.subclass = user->dtmf; + if (ast_write(chan, &fr) < 0) { + ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno)); + } + user->dtmf = 0; + continue; + } res = read(outfd, buf, CONF_SIZE); if (res > 0) { memset(&fr, 0, sizeof(fr)); @@ -1867,6 +1933,8 @@ bailoutandtrynormal: /* This device changed state now */ if (!conf->users) /* If there are no more members */ ast_device_state_changed("meetme:%s", conf->confno); + if (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK)) + ast_device_state_changed("SLA:%s", conf->confno + 4); } free(user); AST_LIST_UNLOCK(&confs); @@ -2402,6 +2470,31 @@ static void invite_trunk(struct ast_channel *orig, struct ast_sla *sla) } +static int sla_checkforhold(struct ast_conference *conf, int hangup) +{ + struct ast_conf_user *user; + struct ast_channel *onhold=NULL; + int holdcount = 0; + int stationcount = 0; + int amonhold = 0; + AST_LIST_TRAVERSE(&conf->userlist, user, list) { + if (user->userflags & CONFFLAG_SLA_STATION) { + stationcount++; + if ((user->userflags & CONFFLAG_HOLD)) { + holdcount++; + onhold = user->chan; + } + } + } + if ((holdcount == 1) && (stationcount == 1)) { + amonhold = 1; + if (hangup) + ast_softhangup(onhold, AST_SOFTHANGUP_EXPLICIT); + } else if (holdcount && (stationcount == holdcount)) + amonhold = 1; + return amonhold; +} + /*! \brief The slas()/slat() application */ static int sla_exec(struct ast_channel *chan, void *data, int trunk) @@ -2435,29 +2528,31 @@ static int sla_exec(struct ast_channel *chan, void *data, int trunk) LOCAL_USER_ADD(u); - if (chan->_state != AST_STATE_UP) - ast_answer(chan); if (args.options) ast_app_parse_options(sla_opts, &confflags, NULL, args.options); ast_set_flag(&confflags, CONFFLAG_QUIET|CONFFLAG_DYNAMIC); if (trunk) - ast_set_flag(&confflags, CONFFLAG_WAITMARKED|CONFFLAG_MARKEDEXIT); + ast_set_flag(&confflags, CONFFLAG_WAITMARKED|CONFFLAG_MARKEDEXIT|CONFFLAG_SLA_TRUNK); else - ast_set_flag(&confflags, CONFFLAG_MARKEDUSER); + ast_set_flag(&confflags, CONFFLAG_MARKEDUSER|CONFFLAG_SLA_STATION); sla = ASTOBJ_CONTAINER_FIND(&slas, args.confno); if (sla) { snprintf(confno, sizeof(confno), "sla-%s", args.confno); cnf = find_conf(chan, confno, 1, dynamic, "", 1, &confflags); if (cnf) { + sla_checkforhold(cnf, 1); if (!cnf->users) { - if (trunk) + if (trunk) { + ast_indicate(chan, AST_CONTROL_RINGING); invite_stations(chan, sla); - else + } else invite_trunk(chan, sla); - } + } else if (chan->_state != AST_STATE_UP) + ast_answer(chan); + /* Run the conference */ res = conf_run(chan, cnf, confflags.flags); } else @@ -2808,6 +2903,44 @@ static int meetmestate(const char *data) return AST_DEVICE_INUSE; } +/*! \brief Callback for devicestate providers */ +static int slastate(const char *data) +{ + struct ast_conference *conf; + struct ast_sla *sla, *sla2; + + ast_log(LOG_DEBUG, "asked for sla state for '%s'\n", data); + + /* Find conference */ + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, conf, list) { + if (!strncmp(conf->confno, "sla-", 4) && !strcmp(data, conf->confno + 4)) + break; + } + AST_LIST_UNLOCK(&confs); + + /* Find conference */ + sla = sla2 = ASTOBJ_CONTAINER_FIND(&slas, data); + ASTOBJ_UNREF(sla2, sla_destroy); + + ast_log(LOG_DEBUG, "for '%s' conf = %p, sla = %p\n", data, conf, sla); + + if (!conf && !sla) + return AST_DEVICE_INVALID; + + /* SKREP to fill */ + if (!conf || !conf->users) + return AST_DEVICE_NOT_INUSE; + + if (conf && sla_checkforhold(conf, 0)) + return AST_DEVICE_ONHOLD; + + if ((conf->users == 1) && (AST_LIST_FIRST(&conf->userlist)->userflags & CONFFLAG_SLA_TRUNK)) + return AST_DEVICE_RINGING; + + return AST_DEVICE_INUSE; +} + static void load_config_meetme(void) { struct ast_config *cfg; @@ -2874,6 +3007,7 @@ static void parse_sla(const char *cat, struct ast_variable *v) if (sla) { ASTOBJ_UNMARK(sla); ASTOBJ_WRLOCK(sla); + ASTOBJ_CONTAINER_DESTROYALL(&sla->stations, station_destroy); while (v) { if (!strcasecmp(v->name, "trunk")) { char *c; @@ -2888,6 +3022,7 @@ static void parse_sla(const char *cat, struct ast_variable *v) v = v->next; } ASTOBJ_UNLOCK(sla); + ast_device_state_changed("SLA:%s", cat); } } @@ -2930,6 +3065,7 @@ static int unload_module(void *mod) res |= ast_unregister_application(app); ast_devstate_prov_del("Meetme"); + ast_devstate_prov_del("SLA"); STANDARD_HANGUP_LOCALUSERS; return res; @@ -2939,7 +3075,6 @@ static int load_module(void *mod) { int res; - load_config(); ASTOBJ_CONTAINER_INIT(&slas); res = ast_cli_register(&cli_show_confs); res |= ast_cli_register(&cli_sla_show); @@ -2953,6 +3088,8 @@ static int load_module(void *mod) res |= ast_register_application(appslat, slat_exec, synopslat, descripslat); res |= ast_devstate_prov_add("Meetme", meetmestate); + res |= ast_devstate_prov_add("SLA", slastate); + load_config(); return res; } diff --git a/channels/chan_zap.c b/channels/chan_zap.c index 3f806dc28..94d054c5f 100644 --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -515,6 +515,8 @@ struct zt_subchannel { unsigned int needcallerid:1; unsigned int needanswer:1; unsigned int needflash:1; + unsigned int needhold:1; + unsigned int needunhold:1; unsigned int linear:1; unsigned int inthreeway:1; ZT_CONFINFO curconf; @@ -3899,6 +3901,7 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast) /* Okay -- probably call waiting*/ if (ast_bridged_channel(p->owner)) ast_moh_stop(ast_bridged_channel(p->owner)); + p->subs[index].needunhold = 1; break; case AST_STATE_RESERVED: /* Start up dialtone */ @@ -4056,8 +4059,10 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast) /* Start music on hold if appropriate */ if (!p->subs[SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) ast_moh_start(ast_bridged_channel(p->subs[SUB_CALLWAIT].owner), NULL); + p->subs[SUB_CALLWAIT].needhold = 1; if (ast_bridged_channel(p->subs[SUB_REAL].owner)) ast_moh_stop(ast_bridged_channel(p->subs[SUB_REAL].owner)); + p->subs[SUB_REAL].needunhold = 1; } else if (!p->subs[SUB_THREEWAY].owner) { char cid_num[256]; char cid_name[256]; @@ -4116,6 +4121,7 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast) /* Start music on hold if appropriate */ if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) ast_moh_start(ast_bridged_channel(p->subs[SUB_THREEWAY].owner), NULL); + p->subs[SUB_THREEWAY].needhold = 1; } } } else { @@ -4153,6 +4159,7 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast) } if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner)) ast_moh_stop(ast_bridged_channel(p->subs[otherindex].owner)); + p->subs[otherindex].needunhold = 1; p->owner = p->subs[SUB_REAL].owner; if (ast->_state == AST_STATE_RINGING) { ast_log(LOG_DEBUG, "Enabling ringtone on real and threeway\n"); @@ -4167,6 +4174,7 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast) p->owner = p->subs[SUB_REAL].owner; if (p->subs[SUB_REAL].owner && ast_bridged_channel(p->subs[SUB_REAL].owner)) ast_moh_stop(ast_bridged_channel(p->subs[SUB_REAL].owner)); + p->subs[SUB_REAL].needunhold = 1; zt_enable_ec(p); } @@ -4354,6 +4362,7 @@ static struct ast_frame *__zt_exception(struct ast_channel *ast) p->owner = p->subs[SUB_REAL].owner; if (p->owner && ast_bridged_channel(p->owner)) ast_moh_stop(ast_bridged_channel(p->owner)); + p->subs[SUB_REAL].needunhold = 1; } switch (res) { case ZT_EVENT_ONHOOK: @@ -4397,6 +4406,7 @@ static struct ast_frame *__zt_exception(struct ast_channel *ast) p->cidcwexpire = 0; if (ast_bridged_channel(p->owner)) ast_moh_stop(ast_bridged_channel(p->owner)); + p->subs[SUB_REAL].needunhold = 1; } else ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n"); update_conf(p); @@ -4544,6 +4554,26 @@ struct ast_frame *zt_read(struct ast_channel *ast) return &p->subs[index].f; } + if (p->subs[index].needhold) { + /* Send answer frame if requested */ + p->subs[index].needhold = 0; + p->subs[index].f.frametype = AST_FRAME_CONTROL; + p->subs[index].f.subclass = AST_CONTROL_HOLD; + ast_mutex_unlock(&p->lock); + ast_log(LOG_DEBUG, "Sending hold on '%s'\n", ast->name); + return &p->subs[index].f; + } + + if (p->subs[index].needunhold) { + /* Send answer frame if requested */ + p->subs[index].needunhold = 0; + p->subs[index].f.frametype = AST_FRAME_CONTROL; + p->subs[index].f.subclass = AST_CONTROL_UNHOLD; + ast_mutex_unlock(&p->lock); + ast_log(LOG_DEBUG, "Sending unhold on '%s'\n", ast->name); + return &p->subs[index].f; + } + if (ast->rawreadformat == AST_FORMAT_SLINEAR) { if (!p->subs[index].linear) { p->subs[index].linear = 1; diff --git a/channels/iax2-parser.c b/channels/iax2-parser.c index 76339b096..b3d732657 100644 --- a/channels/iax2-parser.c +++ b/channels/iax2-parser.c @@ -444,7 +444,18 @@ void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, s "ANSWER ", "BUSY ", "TKOFFHK", - "OFFHOOK" }; + "OFFHOOK", + "CONGSTN", + "FLASH ", + "WINK ", + "OPTION ", + "RDKEY ", + "RDUNKEY", + "PROGRES", + "PROCDNG", + "HOLD ", + "UNHOLD ", + "VIDUPDT", }; struct ast_iax2_full_hdr *fh; char retries[20]; char class2[20]; diff --git a/devicestate.c b/devicestate.c index b0e5d5aec..40cd6c322 100644 --- a/devicestate.c +++ b/devicestate.c @@ -52,6 +52,8 @@ static const char *devstatestring[] = { /* 4 AST_DEVICE_INVALID */ "Invalid", /*!< Invalid - not known to Asterisk */ /* 5 AST_DEVICE_UNAVAILABLE */ "Unavailable", /*!< Unavailable (not registred) */ /* 6 AST_DEVICE_RINGING */ "Ringing" /*!< Ring, ring, ring */ + /* 7 AST_DEVICE_RINGINUSE */ "Ring+Inuse" /*!< Ring and in use */ + /* 8 AST_DEVICE_ONHOLD */ "On Hold" /*!< On Hold */ }; /*! \brief A device state provider (not a channel) */ diff --git a/include/asterisk/devicestate.h b/include/asterisk/devicestate.h index d93d6d996..97d24c16f 100644 --- a/include/asterisk/devicestate.h +++ b/include/asterisk/devicestate.h @@ -43,6 +43,8 @@ extern "C" { #define AST_DEVICE_RINGING 6 /*! Device is ringing *and* in use */ #define AST_DEVICE_RINGINUSE 7 +/*! Device is on hold */ +#define AST_DEVICE_ONHOLD 8 /*! \brief Devicestate watcher call back */ typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data); diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h index 08949a71d..8212d7cc0 100644 --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -52,6 +52,7 @@ enum ast_extension_states { AST_EXTENSION_BUSY = 1 << 1, /*!< All devices BUSY */ AST_EXTENSION_UNAVAILABLE = 1 << 2, /*!< All devices UNAVAILABLE/UNREGISTERED */ AST_EXTENSION_RINGING = 1 << 3, /*!< All devices RINGING */ + AST_EXTENSION_ONHOLD = 1 << 4, /*!< All devices ONHOLD */ }; @@ -206,7 +206,9 @@ static const struct cfextension_states { { AST_EXTENSION_BUSY, "Busy" }, { AST_EXTENSION_UNAVAILABLE, "Unavailable" }, { AST_EXTENSION_RINGING, "Ringing" }, - { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" } + { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" }, + { AST_EXTENSION_ONHOLD, "Hold" }, + { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" } }; int ast_pbx_outgoing_cdr_failed(void); @@ -1754,7 +1756,7 @@ static int ast_extension_state2(struct ast_exten *e) { char hint[AST_MAX_EXTENSION]; char *cur, *rest; - int allunavailable = 1, allbusy = 1, allfree = 1; + int allunavailable = 1, allbusy = 1, allfree = 1, allonhold = 1; int busy = 0, inuse = 0, ring = 0; if (!e) @@ -1769,37 +1771,48 @@ static int ast_extension_state2(struct ast_exten *e) case AST_DEVICE_NOT_INUSE: allunavailable = 0; allbusy = 0; + allonhold = 0; break; case AST_DEVICE_INUSE: inuse = 1; allunavailable = 0; allfree = 0; + allonhold = 0; break; case AST_DEVICE_RINGING: ring = 1; allunavailable = 0; allfree = 0; + allonhold = 0; break; case AST_DEVICE_RINGINUSE: inuse = 1; ring = 1; allunavailable = 0; allfree = 0; + allonhold = 0; + break; + case AST_DEVICE_ONHOLD: + allunavailable = 0; + allfree = 0; break; case AST_DEVICE_BUSY: allunavailable = 0; allfree = 0; + allonhold = 0; busy = 1; break; case AST_DEVICE_UNAVAILABLE: case AST_DEVICE_INVALID: allbusy = 0; allfree = 0; + allonhold = 0; break; default: allunavailable = 0; allbusy = 0; allfree = 0; + allonhold = 0; } } @@ -1811,6 +1824,8 @@ static int ast_extension_state2(struct ast_exten *e) return AST_EXTENSION_INUSE; if (allfree) return AST_EXTENSION_NOT_INUSE; + if (allonhold) + return AST_EXTENSION_ONHOLD; if (allbusy) return AST_EXTENSION_BUSY; if (allunavailable) |