From 12ad49e444d6108b7312a46b389090d51b670a7f Mon Sep 17 00:00:00 2001 From: bbryant Date: Thu, 9 Sep 2010 18:50:13 +0000 Subject: Fixes an issue with dialplan pattern matching where the specificity for pattern ranges and pattern special characters was inconsistent. (closes issue #16903) Reported by: Nick_Lewis Patches: pbx.c-specificity.patch uploaded by Nick Lewis (license 657) Tested by: Nick_Lewis git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.6.2@285710 f38db490-d61c-443f-a65b-d21fe96a405b --- channels/chan_dahdi.c | 15 +- channels/chan_iax2.c | 3 +- channels/chan_local.c | 20 +++ channels/chan_misdn.c | 43 +++--- channels/chan_phone.c | 80 ++++++---- channels/chan_sip.c | 369 ++++++++++++++++++++++++++++++----------------- channels/chan_usbradio.c | 161 ++++++++++----------- channels/console_video.c | 239 +++++++++++++++--------------- 8 files changed, 528 insertions(+), 402 deletions(-) (limited to 'channels') diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index fb45564e3..8026ca574 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -3601,7 +3601,16 @@ static int dahdi_call(struct ast_channel *ast, char *rdest, int timeout) l = NULL; n = NULL; if (!p->hidecallerid) { - l = ast->cid.cid_num; + /* If we get to the end of this loop without breaking, there's no + * numeric calleridnum. This is done instead of testing for + * "unknown" or the thousands of other ways that the calleridnum + * could be invalid. */ + for (l = ast->cid.cid_num; l && *l; l++) { + if (strchr("0123456789", *l)) { + l = ast->cid.cid_num; + break; + } + } if (!p->hidecalleridname) { n = ast->cid.cid_name; } @@ -7372,7 +7381,7 @@ static int dahdi_indicate(struct ast_channel *chan, int condition, const void *d ast_debug(1,"Received AST_CONTROL_PROGRESS on %s\n",chan->name); #ifdef HAVE_PRI p->digital = 0; /* Digital-only calls isn't allows any inband progress messages */ - if (!p->progress && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) + if (!p->progress && !p->alerting && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) && p->pri && !p->outgoing) { if (p->pri->pri) { if (!pri_grab(p, p->pri)) { @@ -13542,7 +13551,7 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_CONFIG_ERR: - ast_log(LOG_WARNING, "PRI Error on span %d: %s\n", pri->trunkgroup, e->err.err); + ast_log(LOG_WARNING, "PRI Error on span %d: %s\n", pri->span, e->err.err); break; case PRI_EVENT_RESTART_ACK: chanpos = pri_find_principle(pri, e->restartack.channel); diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index e9e86bd76..4b4367845 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -6809,13 +6809,14 @@ static int manager_iax2_show_registry(struct mansession *s, const struct message astman_append(s, "Event: RegistryEntry\r\n" + "%s" "Host: %s\r\n" "DNSmanager: %s\r\n" "Username: %s\r\n" "Perceived: %s\r\n" "Refresh: %d\r\n" "State: %s\r\n" - "\r\n", host, (reg->dnsmgr) ? "Y" : "N", reg->username, perceived, + "\r\n", idtext, host, (reg->dnsmgr) ? "Y" : "N", reg->username, perceived, reg->refresh, regstate2str(reg->regstate)); total++; diff --git a/channels/chan_local.c b/channels/chan_local.c index a26b8f718..46451776f 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -384,6 +384,26 @@ static void check_bridge(struct local_pvt *p) p->chan->audiohooks = p->owner->audiohooks; p->owner->audiohooks = audiohooks_swapper; } + + /* If any Caller ID was set, preserve it after masquerade like above. We must check + * to see if Caller ID was set because otherwise we'll mistakingly copy info not + * set from the dialplan and will overwrite the real channel Caller ID. The reason + * for this whole preswapping action is because the Caller ID is set on the channel + * thread (which is the to be masqueraded away local channel) before both local + * channels are optimized away. + */ + if (p->owner->cid.cid_dnid || p->owner->cid.cid_num || + p->owner->cid.cid_name || p->owner->cid.cid_ani || + p->owner->cid.cid_rdnis || p->owner->cid.cid_pres || + p->owner->cid.cid_ani2 || p->owner->cid.cid_ton || + p->owner->cid.cid_tns) { + + struct ast_callerid tmpcid; + tmpcid = p->owner->cid; + p->owner->cid = p->chan->_bridge->cid; + p->chan->_bridge->cid = tmpcid; + } + ast_app_group_update(p->chan, p->owner); ast_channel_masquerade(p->owner, p->chan->_bridge); ast_set_flag(p, LOCAL_ALREADY_MASQED); diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c index cd48ef43b..718051231 100644 --- a/channels/chan_misdn.c +++ b/channels/chan_misdn.c @@ -3033,15 +3033,14 @@ static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame static struct ast_frame *misdn_read(struct ast_channel *ast) { struct chan_list *tmp; - fd_set rrfs; - struct timeval tv = { 0, 20000 }; int len, t; + struct pollfd pfd = { .fd = -1, .events = POLLIN }; if (!ast) { chan_misdn_log(1, 0, "misdn_read called without ast\n"); return NULL; } - if (!(tmp = MISDN_ASTERISK_TECH_PVT(ast))) { + if (!(tmp = MISDN_ASTERISK_TECH_PVT(ast))) { chan_misdn_log(1, 0, "misdn_read called without ast->pvt\n"); return NULL; } @@ -3051,20 +3050,18 @@ static struct ast_frame *misdn_read(struct ast_channel *ast) return NULL; } - FD_ZERO(&rrfs); - FD_SET(tmp->pipe[0], &rrfs); - - if (!(t = select(FD_SETSIZE, &rrfs, NULL, NULL, &tv))) { - chan_misdn_log(3, tmp->bc->port, "read Select Timed out\n"); - len = 160; - } + pfd.fd = tmp->pipe[0]; + t = ast_poll(&pfd, 1, 20); if (t < 0) { - chan_misdn_log(-1, tmp->bc->port, "Select Error (err=%s)\n", strerror(errno)); + chan_misdn_log(-1, tmp->bc->port, "poll() error (err=%s)\n", strerror(errno)); return NULL; } - if (FD_ISSET(tmp->pipe[0], &rrfs)) { + if (!t) { + chan_misdn_log(3, tmp->bc->port, "poll() timed out\n"); + len = 160; + } else if (pfd.revents & POLLIN) { len = read(tmp->pipe[0], tmp->ast_rd_buf, sizeof(tmp->ast_rd_buf)); if (len <= 0) { @@ -5172,26 +5169,22 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) if (ch->ast) ast_queue_frame(ch->ast, &frame); } else { - fd_set wrfs; - struct timeval tv = { 0, 0 }; + struct pollfd pfd = { .fd = ch->pipe[1], .events = POLLOUT }; int t; - FD_ZERO(&wrfs); - FD_SET(ch->pipe[1], &wrfs); + t = ast_poll(&pfd, 1, 0); - t = select(FD_SETSIZE, NULL, &wrfs, NULL, &tv); - - if (!t) { - chan_misdn_log(9, bc->port, "Select Timed out\n"); + if (t < 0) { + chan_misdn_log(-1, bc->port, "poll() error (err=%s)\n", strerror(errno)); break; } - - if (t < 0) { - chan_misdn_log(-1, bc->port, "Select Error (err=%s)\n", strerror(errno)); + + if (!t) { + chan_misdn_log(9, bc->port, "poll() timed out\n"); break; } - - if (FD_ISSET(ch->pipe[1], &wrfs)) { + + if (pfd.revents & POLLOUT) { chan_misdn_log(9, bc->port, "writing %d bytes to asterisk\n", bc->bframe_len); if (write(ch->pipe[1], bc->bframe, bc->bframe_len) <= 0) { chan_misdn_log(0, bc->port, "Write returned <=0 (err=%s) --> hanging up channel\n", strerror(errno)); diff --git a/channels/chan_phone.c b/channels/chan_phone.c index 295d2eb89..904207345 100644 --- a/channels/chan_phone.c +++ b/channels/chan_phone.c @@ -1011,12 +1011,12 @@ static void phone_check_exception(struct phone_pvt *i) static void *do_monitor(void *data) { - fd_set rfds, efds; - int n, res; + struct pollfd *fds = NULL; + int nfds = 0, inuse_fds = 0, res; struct phone_pvt *i; int tonepos = 0; /* The tone we're playing this round */ - struct timeval wait = {0,0}; + struct timeval tv = { 0, 0 }; int dotone; /* This thread monitors all the frame relay interfaces which are not yet in use (and thus do not have a separate thread) indefinitely */ @@ -1030,33 +1030,38 @@ static void *do_monitor(void *data) } /* Build the stuff we're going to select on, that is the socket of every phone_pvt that does not have an associated owner channel */ - n = -1; - FD_ZERO(&rfds); - FD_ZERO(&efds); i = iflist; dotone = 0; - while (i) { - if (FD_ISSET(i->fd, &rfds)) - ast_log(LOG_WARNING, "Descriptor %d appears twice (%s)?\n", i->fd, i->dev); + inuse_fds = 0; + for (i = iflist; i; i = i->next) { if (!i->owner) { /* This needs to be watched, as it lacks an owner */ - FD_SET(i->fd, &rfds); - FD_SET(i->fd, &efds); - if (i->fd > n) - n = i->fd; + if (inuse_fds == nfds) { + void *tmp = ast_realloc(fds, (nfds + 1) * sizeof(*fds)); + if (!tmp) { + /* Avoid leaking */ + continue; + } + fds = tmp; + nfds++; + } + fds[inuse_fds].fd = i->fd; + fds[inuse_fds].events = POLLIN | POLLERR; + fds[inuse_fds].revents = 0; + inuse_fds++; + if (i->dialtone && i->mode != MODE_SIGMA) { /* Remember we're going to have to come back and play more dialtones */ - if (ast_tvzero(wait)) { + if (ast_tvzero(tv)) { /* If we're due for a dialtone, play one */ - if (write(i->fd, DialTone + tonepos, 240) != 240) + if (write(i->fd, DialTone + tonepos, 240) != 240) { ast_log(LOG_WARNING, "Dial tone write error\n"); + } } dotone++; } } - - i = i->next; } /* Okay, now that we know what to do, release the interface lock */ ast_mutex_unlock(&iflock); @@ -1065,26 +1070,28 @@ static void *do_monitor(void *data) if (dotone && i && i->mode != MODE_SIGMA) { /* If we're ready to recycle the time, set it to 30 ms */ tonepos += 240; - if (tonepos >= sizeof(DialTone)) - tonepos = 0; - if (ast_tvzero(wait)) { - wait = ast_tv(30000, 0); + if (tonepos >= sizeof(DialTone)) { + tonepos = 0; } - res = ast_select(n + 1, &rfds, NULL, &efds, &wait); + if (ast_tvzero(tv)) { + tv = ast_tv(0, 30000); + } + res = ast_poll2(fds, inuse_fds, &tv); } else { - res = ast_select(n + 1, &rfds, NULL, &efds, NULL); - wait = ast_tv(0,0); + res = ast_poll(fds, inuse_fds, -1); + tv = ast_tv(0, 0); tonepos = 0; } /* Okay, select has finished. Let's see what happened. */ if (res < 0) { - ast_debug(1, "select return %d: %s\n", res, strerror(errno)); + ast_debug(1, "poll returned %d: %s\n", res, strerror(errno)); continue; } /* If there are no fd's changed, just continue, it's probably time to play some more dialtones */ - if (!res) + if (!res) { continue; + } /* Alright, lock the interface list again, and let's look and see what has happened */ if (ast_mutex_lock(&iflock)) { @@ -1092,15 +1099,27 @@ static void *do_monitor(void *data) continue; } - i = iflist; - for(; i; i=i->next) { - if (FD_ISSET(i->fd, &rfds)) { + for (i = iflist; i; i = i->next) { + int j; + /* Find the record */ + for (j = 0; j < inuse_fds; j++) { + if (fds[j].fd == i->fd) { + break; + } + } + + /* Not found? */ + if (j == inuse_fds) { + continue; + } + + if (fds[j].revents & POLLIN) { if (i->owner) { continue; } phone_mini_packet(i); } - if (FD_ISSET(i->fd, &efds)) { + if (fds[j].revents & POLLERR) { if (i->owner) { continue; } @@ -1110,7 +1129,6 @@ static void *do_monitor(void *data) ast_mutex_unlock(&iflock); } return NULL; - } static int restart_monitor() diff --git a/channels/chan_sip.c b/channels/chan_sip.c index dc8e98713..2768b3a4c 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -948,13 +948,6 @@ static const struct cfsip_options { */ #define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO" -/*! \brief SIP Extensions we support - \note This should be generated based on the previous array - in combination with settings. - \todo We should not have "timer" if it's disabled in the configuration file. -*/ -#define SUPPORTED_EXTENSIONS "replaces, timer" - /*! \brief Standard SIP unsecure port for UDP and TCP from RFC 3261. DO NOT CHANGE THIS */ #define STANDARD_SIP_PORT 5060 /*! \brief Standard SIP TLS port from RFC 3261. DO NOT CHANGE THIS */ @@ -3104,6 +3097,32 @@ cleanup: return NULL; } +/* this func is used with ao2_callback to unlink/delete all marked + peers */ +static int peer_is_marked(void *peerobj, void *arg, int flags) +{ + struct sip_peer *peer = peerobj; + return peer->the_mark ? CMP_MATCH : 0; +} + + +/* \brief Unlink all marked peers from ao2 containers */ +static void unlink_marked_peers_from_tables(void) +{ + ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL, + "initiating callback to remove marked peers"); + ao2_t_callback(peers_by_ip, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL, + "initiating callback to remove marked peers"); +} + +/* \brief Unlink single peer from all ao2 containers */ +static void unlink_peer_from_tables(struct sip_peer *peer) +{ + ao2_t_unlink(peers, peer, "ao2_unlink of peer from peers table"); + if (peer->addr.sin_addr.s_addr) { + ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table"); + } +} /*! * helper functions to unreference various types of objects. @@ -3754,34 +3773,34 @@ static int retrans_pkt(const void *data) struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL; int reschedule = DEFAULT_RETRANS; int xmitres = 0; - + /* Lock channel PVT */ sip_pvt_lock(pkt->owner); if (pkt->retrans < MAX_RETRANS) { pkt->retrans++; - if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */ + if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */ if (sipdebug) - ast_debug(4, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method); + ast_debug(4, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method); } else { - int siptimer_a; - - if (sipdebug) - ast_debug(4, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method); - if (!pkt->timer_a) - pkt->timer_a = 2 ; - else - pkt->timer_a = 2 * pkt->timer_a; - - /* For non-invites, a maximum of 4 secs */ - siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */ - if (pkt->method != SIP_INVITE && siptimer_a > 4000) - siptimer_a = 4000; - - /* Reschedule re-transmit */ + int siptimer_a; + + if (sipdebug) + ast_debug(4, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method); + if (!pkt->timer_a) + pkt->timer_a = 2 ; + else + pkt->timer_a = 2 * pkt->timer_a; + + /* For non-invites, a maximum of 4 secs */ + siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */ + if (pkt->method != SIP_INVITE && siptimer_a > 4000) + siptimer_a = 4000; + + /* Reschedule re-transmit */ reschedule = siptimer_a; - ast_debug(4, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid); - } + ast_debug(4, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid); + } if (sip_debug_test_pvt(pkt->owner)) { const struct sockaddr_in *dst = sip_real_dst(pkt->owner); @@ -3793,12 +3812,13 @@ static int retrans_pkt(const void *data) append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data->str); xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); - sip_pvt_unlock(pkt->owner); - if (xmitres == XMIT_ERROR) + if (xmitres == XMIT_ERROR) { ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid); - else + } else { + sip_pvt_unlock(pkt->owner); return reschedule; - } + } + } /* Too many retries */ if (pkt->owner && pkt->method != SIP_OPTIONS && xmitres == 0) { if (pkt->is_fatal || sipdebug) /* Tell us if it's critical or if we're debugging */ @@ -3808,13 +3828,13 @@ static int retrans_pkt(const void *data) } else if (pkt->method == SIP_OPTIONS && sipdebug) { ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) -- See doc/sip-retransmit.txt.\n", pkt->owner->callid); - } + } if (xmitres == XMIT_ERROR) { ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission on Call ID %s\n", pkt->owner->callid); append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)"); - } else + } else { append_history(pkt->owner, "MaxRetries", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)"); - + } pkt->retransid = -1; if (pkt->is_fatal) { @@ -3824,9 +3844,9 @@ static int retrans_pkt(const void *data) sip_pvt_lock(pkt->owner); } - if (pkt->owner->owner && !pkt->owner->owner->hangupcause) + if (pkt->owner->owner && !pkt->owner->owner->hangupcause) pkt->owner->owner->hangupcause = AST_CAUSE_NO_USER_RESPONSE; - + if (pkt->owner->owner) { sip_alreadygone(pkt->owner); ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet (see doc/sip-retransmit.txt).\n", pkt->owner->callid); @@ -3846,7 +3866,7 @@ static int retrans_pkt(const void *data) if (pkt->method == SIP_BYE) { /* We're not getting answers on SIP BYE's. Tear down the call anyway. */ - if (pkt->owner->owner) + if (pkt->owner->owner) ast_channel_unlock(pkt->owner->owner); append_history(pkt->owner, "ByeFailure", "Remote peer doesn't respond to bye. Destroying call anyway."); pvt_set_needdestroy(pkt->owner, "no response to BYE"); @@ -3985,10 +4005,6 @@ static int __sip_autodestruct(const void *data) } } - if (p->subscribed == MWI_NOTIFICATION) - if (p->relatedpeer) - p->relatedpeer = unref_peer(p->relatedpeer, "__sip_autodestruct: unref peer p->relatedpeer"); /* Remove link to peer. If it's realtime, make sure it's gone from memory) */ - /* Reset schedule ID */ p->autokillid = -1; @@ -6090,7 +6106,6 @@ static int sip_hangup(struct ast_channel *ast) if (p->invitestate == INV_CALLING) { /* We can't send anything in CALLING state */ ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - __sip_pretend_ack(p); /* Do we need a timer here if we don't hear from them at all? Yes we do or else we will get hung dialogs and those are no fun. */ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); append_history(p, "DELAY", "Not sending cancel, waiting for timeout"); @@ -8130,10 +8145,10 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action int vportno = -1; /*!< RTP Video port number */ int tportno = -1; /*!< RTP Text port number */ int udptlportno = -1; /*!< UDPTL Image port number */ - struct sockaddr_in sin; /*!< media socket address */ - struct sockaddr_in vsin; /*!< video socket address */ - struct sockaddr_in isin; /*!< image socket address */ - struct sockaddr_in tsin; /*!< text socket address */ + struct sockaddr_in sin = { 0, }; /*!< media socket address */ + struct sockaddr_in vsin = { 0, }; /*!< video socket address */ + struct sockaddr_in isin = { 0, }; /*!< image socket address */ + struct sockaddr_in tsin = { 0, }; /*!< text socket address */ /* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */ int peercapability = 0, peernoncodeccapability = 0; @@ -8648,7 +8663,10 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action ast_set_write_format(p->owner, p->owner->writeformat); } - if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && sin.sin_addr.s_addr && (!sendonly || sendonly == -1)) { + if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) + && (sin.sin_addr.s_addr || vsin.sin_addr.s_addr || + isin.sin_addr.s_addr || tsin.sin_addr.s_addr) + && (!sendonly || sendonly == -1)) { ast_queue_control(p->owner, AST_CONTROL_UNHOLD); /* Activate a re-invite */ ast_queue_frame(p->owner, &ast_null_frame); @@ -8664,7 +8682,9 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (sip_cfg.notifyhold) sip_peer_hold(p, FALSE); ast_clear_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */ - } else if (!sin.sin_addr.s_addr || (sendonly && sendonly != -1)) { + } else if (!(sin.sin_addr.s_addr || vsin.sin_addr.s_addr || + isin.sin_addr.s_addr || tsin.sin_addr.s_addr) + || (sendonly && sendonly != -1)) { int already_on_hold = ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); ast_queue_control_data(p->owner, AST_CONTROL_HOLD, S_OR(p->mohsuggest, NULL), @@ -9096,6 +9116,20 @@ static void ts_ast_rtp_destroy(void *data) } #endif +/*! \brief Add "Supported" header to sip message. Since some options may + * be disabled in the config, the sip_pvt must be inspected to determine what + * is supported for this dialog. */ +static int add_supported_header(struct sip_pvt *pvt, struct sip_request *req) +{ + int res; + if (st_get_mode(pvt) != SESSION_TIMER_MODE_REFUSE) { + res = add_header(req, "Supported", "replaces, timer"); + } else { + res = add_header(req, "Supported", "replaces"); + } + return res; +} + /*! \brief Add header to SIP message */ static int add_header(struct sip_request *req, const char *var, const char *value) { @@ -9507,14 +9541,13 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg if (!ast_strlen_zero(global_useragent)) add_header(resp, "Server", global_useragent); add_header(resp, "Allow", ALLOWED_METHODS); - add_header(resp, "Supported", SUPPORTED_EXTENSIONS); + add_supported_header(p, resp); /* If this is an invite, add Session-Timers related headers if the feature is active for this session */ if (p->method == SIP_INVITE && p->stimer && p->stimer->st_active == TRUE && p->stimer->st_active_peer_ua == TRUE) { char se_hdr[256]; snprintf(se_hdr, sizeof(se_hdr), "%d;refresher=%s", p->stimer->st_interval, strefresher2str(p->stimer->st_ref)); - add_header(resp, "Require", "timer"); add_header(resp, "Session-Expires", se_hdr); } @@ -10607,7 +10640,7 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int old reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1); add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); + add_supported_header(p, &req); if (sipdebug) { if (oldsdp == TRUE) add_header(&req, "X-asterisk-Info", "SIP re-invite (Session-Timers)"); @@ -10983,7 +11016,7 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init) } add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); + add_supported_header(p, &req); if(p->notify_headers) { char buf[512]; @@ -11315,27 +11348,36 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int tim int need = strlen(caller->cid.cid_num) + strlen(p->fromdomain) + sizeof("sip:@"); local_target = alloca(need); snprintf(local_target, need, "sip:%s@%s", caller->cid.cid_num, p->fromdomain); - local_display = ast_strdupa(caller->cid.cid_name); + if (!(ast_strlen_zero(caller->cid.cid_name))) { + local_display = ast_strdupa(caller->cid.cid_name); + } ast_channel_unlock(caller); caller = NULL; } + /* We create a fake call-id which the phone will send back in an INVITE + * Replaces header which we can grab and do some magic with. */ + if (sip_cfg.pedanticsipchecking) { + ast_str_append(&tmp, 0, "\n", + p->exten, p->callid, p->theirtag, p->tag); + } else { + ast_str_append(&tmp, 0, "\n", + p->exten, p->callid); + } + ast_str_append(&tmp, 0, + "\n" + /* See the limitations of this above. Luckily the phone seems to still be + happy when these values are not correct. */ + "%s\n" + "\n" + "\n" + "\n" + "%s\n" + "\n" + "\n", + local_display, local_target, local_target, mto, mto); + } else { + ast_str_append(&tmp, 0, "\n", p->exten); } - - /* We create a fake call-id which the phone will send back in an INVITE - Replaces header which we can grab and do some magic with. */ - ast_str_append(&tmp, 0, - "\n" - "\n" - /* See the limitations of this above. Luckily the phone seems to still be - happy when these values are not correct. */ - "%s\n" - "\n" - "\n" - "\n" - "%s\n" - "\n" - "\n", - p->exten, p->callid, local_display, local_target, local_target, mto, mto); } else { ast_str_append(&tmp, 0, "\n", p->exten); } @@ -11424,7 +11466,7 @@ static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *messa add_header(&req, "Subscription-state", terminate ? "terminated;reason=noresource" : "active"); add_header(&req, "Content-Type", "message/sipfrag;version=2.0"); add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); + add_supported_header(p, &req); snprintf(tmp, sizeof(tmp), "SIP/2.0 %s\r\n", message); add_content(&req, tmp); @@ -11976,7 +12018,7 @@ static int transmit_refer(struct sip_pvt *p, const char *dest) add_header(&req, "Refer-To", referto); add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); + add_supported_header(p, &req); if (!ast_strlen_zero(p->our_contact)) add_header(&req, "Referred-By", p->our_contact); @@ -12114,7 +12156,6 @@ static int expire_register(const void *data) peer->expire = -1; peer->portinuri = 0; - memset(&peer->addr, 0, sizeof(peer->addr)); destroy_association(peer); /* remove registration data from storage */ set_socket_transport(&peer->socket, peer->default_outbound_transport); @@ -12136,12 +12177,13 @@ static int expire_register(const void *data) if (peer->selfdestruct || ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) { - ao2_t_unlink(peers, peer, "ao2_unlink of peer from peers table"); - if (peer->addr.sin_addr.s_addr) { - ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table"); - } + unlink_peer_from_tables(peer); } + /* Only clear the addr after we check for destruction. The addr must remain + * in order to unlink from the peers_by_ip container correctly */ + memset(&peer->addr, 0, sizeof(peer->addr)); + unref_peer(peer, "removing peer ref for expire_register"); return 0; @@ -15131,14 +15173,6 @@ static int dialog_needdestroy(void *dialogobj, void *arg, int flags) return 0; } -/* this func is used with ao2_callback to unlink/delete all marked - peers */ -static int peer_is_marked(void *peerobj, void *arg, int flags) -{ - struct sip_peer *peer = peerobj; - return peer->the_mark ? CMP_MATCH : 0; -} - /*! \brief Remove temporary realtime objects from memory (CLI) */ /*! \todo XXXX Propably needs an overhaul after removal of the devices */ static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) @@ -15241,8 +15275,7 @@ static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli } ao2_iterator_destroy(&i); if (pruned) { - ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL, - "initiating callback to remove marked peers"); + unlink_marked_peers_from_tables(); ast_cli(a->fd, "%d peers pruned.\n", pruned); } else ast_cli(a->fd, "No peers found to prune.\n"); @@ -17642,7 +17675,11 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) } } -/*! \brief Check pending actions on SIP call */ +/*! \brief Check pending actions on SIP call + * + * \note both sip_pvt and sip_pvt's owner channel (if present) + * must be locked for this function. + */ static void check_pendings(struct sip_pvt *p) { if (ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { @@ -17657,6 +17694,9 @@ static void check_pendings(struct sip_pvt *p) if (p->pendinginvite) return; + if (p->owner) { + ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); + } /* Perhaps there is an SD change INVITE outstanding */ transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE); } @@ -17682,12 +17722,21 @@ static void check_pendings(struct sip_pvt *p) static int sip_reinvite_retry(const void *data) { struct sip_pvt *p = (struct sip_pvt *) data; + struct ast_channel *owner; sip_pvt_lock(p); /* called from schedule thread which requires a lock */ + while ((owner = p->owner) && ast_channel_trylock(owner)) { + sip_pvt_unlock(p); + usleep(1); + sip_pvt_lock(p); + } ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); p->waitid = -1; check_pendings(p); sip_pvt_unlock(p); + if (owner) { + ast_channel_unlock(owner); + } dialog_unref(p, "unref the dialog ptr from sip_reinvite_retry, because it held a dialog ptr"); return 0; } @@ -17840,7 +17889,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru /* Check for Session-Timers related headers */ if (st_get_mode(p) != SESSION_TIMER_MODE_REFUSE && p->outgoing_call == TRUE && !reinvite) { p_hdrval = (char*)get_header(req, "Session-Expires"); - if (!ast_strlen_zero(p_hdrval)) { + if (!ast_strlen_zero(p_hdrval)) { /* UAS supports Session-Timers */ enum st_refresher tmp_st_ref = SESSION_TIMER_REFRESHER_AUTO; int tmp_st_interval = 0; @@ -19970,7 +20019,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int struct sip_pvt *subscription = NULL; replace_id += 7; /* Worst case we are looking at \0 */ - if ((subscription = get_sip_pvt_byid_locked(replace_id, NULL, NULL)) == NULL) { + if ((subscription = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) { ast_log(LOG_NOTICE, "Unable to find subscription with call-id: %s\n", replace_id); transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req); error = 1; @@ -20215,10 +20264,10 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int } /* Session-Timers */ - if (p->sipoptions & SIP_OPT_TIMER) { + if ((p->sipoptions & SIP_OPT_TIMER) && !ast_strlen_zero(get_header(req, "Session-Expires"))) { /* The UAC has requested session-timers for this session. Negotiate the session refresh interval and who will be the refresher */ - ast_debug(2, "Incoming INVITE with 'timer' option enabled\n"); + ast_debug(2, "Incoming INVITE with 'timer' option supported and \"Session-Expires\" header.\n"); /* Allocate Session-Timers struct w/in the dialog */ if (!p->stimer) @@ -20226,17 +20275,15 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int /* Parse the Session-Expires header */ p_uac_se_hdr = get_header(req, "Session-Expires"); - if (!ast_strlen_zero(p_uac_se_hdr)) { - rtn = parse_session_expires(p_uac_se_hdr, &uac_max_se, &st_ref); - if (rtn != 0) { - transmit_response_reliable(p, "400 Session-Expires Invalid Syntax", req); - p->invitestate = INV_COMPLETED; - if (!p->lastinvite) { - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - res = -1; - goto request_invite_cleanup; + rtn = parse_session_expires(p_uac_se_hdr, &uac_max_se, &st_ref); + if (rtn != 0) { + transmit_response_reliable(p, "400 Session-Expires Invalid Syntax", req); + p->invitestate = INV_COMPLETED; + if (!p->lastinvite) { + sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); } + res = -1; + goto request_invite_cleanup; } /* Parse the Min-SE header */ @@ -21303,7 +21350,6 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, int firststate = AST_EXTENSION_REMOVED; struct sip_peer *authpeer = NULL; const char *eventheader = get_header(req, "Event"); /* Get Event package name */ - const char *acceptheader = get_header(req, "Accept"); int resubscribe = (p->subscribed != NONE); char *temp, *event; struct ao2_iterator i; @@ -21429,51 +21475,94 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */ unsigned int pidf_xml; + const char *accept; + int start = 0; + enum subscriptiontype subscribed = NONE; + const char *unknown_acceptheader = NULL; if (authpeer) /* We do not need the authpeer any more */ unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 2)"); /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */ - pidf_xml = strstr(acceptheader, "application/pidf+xml") ? 1 : 0; - - /* Older versions of Polycom firmware will claim pidf+xml, but really - * they only support xpidf+xml. */ - if (pidf_xml && strstr(p->useragent, "Polycom")) { - p->subscribed = XPIDF_XML; - } else if (pidf_xml) { - p->subscribed = PIDF_XML; /* RFC 3863 format */ - } else if (strstr(acceptheader, "application/dialog-info+xml")) { - p->subscribed = DIALOG_INFO_XML; - /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ - } else if (strstr(acceptheader, "application/cpim-pidf+xml")) { - p->subscribed = CPIM_PIDF_XML; /* RFC 3863 format */ - } else if (strstr(acceptheader, "application/xpidf+xml")) { - p->subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */ - } else if (ast_strlen_zero(acceptheader)) { + accept = __get_header(req, "Accept", &start); + while ((subscribed == NONE) && !ast_strlen_zero(accept)) { + pidf_xml = strstr(accept, "application/pidf+xml") ? 1 : 0; + + /* Older versions of Polycom firmware will claim pidf+xml, but really + * they only support xpidf+xml. */ + if (pidf_xml && strstr(p->useragent, "Polycom")) { + subscribed = XPIDF_XML; + } else if (pidf_xml) { + subscribed = PIDF_XML; /* RFC 3863 format */ + } else if (strstr(accept, "application/dialog-info+xml")) { + subscribed = DIALOG_INFO_XML; + /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ + } else if (strstr(accept, "application/cpim-pidf+xml")) { + subscribed = CPIM_PIDF_XML; /* RFC 3863 format */ + } else if (strstr(accept, "application/xpidf+xml")) { + subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */ + } else { + unknown_acceptheader = accept; + } + /* check to see if there is another Accept header present */ + accept = __get_header(req, "Accept", &start); + } + + if (!start) { if (p->subscribed == NONE) { /* if the subscribed field is not already set, and there is no accept header... */ transmit_response(p, "489 Bad Event", req); - - ast_log(LOG_WARNING, "SUBSCRIBE failure: no Accept header: pvt: stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", - p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri); + ast_log(LOG_WARNING,"SUBSCRIBE failure: no Accept header: pvt: " + "stateid: %d, laststate: %d, dialogver: %d, subscribecont: " + "'%s', subscribeuri: '%s'\n", + p->stateid, + p->laststate, + p->dialogver, + p->subscribecontext, + p->subscribeuri); pvt_set_needdestroy(p, "no Accept header"); return 0; } /* if p->subscribed is non-zero, then accept is not obligatory; according to rfc 3265 section 3.1.3, at least. so, we'll just let it ride, keeping the value from a previous subscription, and not abort the subscription */ - } else { + } else if (subscribed == NONE) { /* Can't find a format for events that we know about */ char mybuf[200]; - snprintf(mybuf, sizeof(mybuf), "489 Bad Event (format %s)", acceptheader); + if (!ast_strlen_zero(unknown_acceptheader)) { + snprintf(mybuf, sizeof(mybuf), "489 Bad Event (format %s)", unknown_acceptheader); + } else { + snprintf(mybuf, sizeof(mybuf), "489 Bad Event"); + } transmit_response(p, mybuf, req); - - ast_log(LOG_WARNING, "SUBSCRIBE failure: unrecognized format: '%s' pvt: subscribed: %d, stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", - acceptheader, (int)p->subscribed, p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri); + ast_log(LOG_WARNING,"SUBSCRIBE failure: unrecognized format:" + "'%s' pvt: subscribed: %d, stateid: %d, laststate: %d," + "dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", + unknown_acceptheader, + (int)p->subscribed, + p->stateid, + p->laststate, + p->dialogver, + p->subscribecontext, + p->subscribeuri); pvt_set_needdestroy(p, "unrecognized format"); return 0; + } else { + p->subscribed = subscribed; + } + } else if (!strcmp(event, "message-summary")) { + int start = 0; + int found_supported = 0; + const char *acceptheader; + + acceptheader = __get_header(req, "Accept", &start); + while (!found_supported && !ast_strlen_zero(acceptheader)) { + found_supported = strcmp(acceptheader, "application/simple-message-summary") ? 0 : 1; + if (!found_supported && (option_debug > 2)) { + ast_log(LOG_DEBUG, "Received SIP mailbox subscription for unknown format: %s\n", acceptheader); + } + acceptheader = __get_header(req, "Accept", &start); } - } else if (!strcmp(event, "message-summary")) { - if (!ast_strlen_zero(acceptheader) && strcmp(acceptheader, "application/simple-message-summary")) { + if (start && !found_supported) { /* Format requested that we do not support */ transmit_response(p, "406 Not Acceptable", req); ast_debug(2, "Received SIP mailbox subscription for unknown format: %s\n", acceptheader); @@ -21482,6 +21571,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 3)"); return 0; } + /* Looks like they actually want a mailbox status This version of Asterisk supports mailbox subscriptions The subscribed URI needs to exist in the dial plan @@ -22954,7 +23044,7 @@ enum st_mode st_get_mode(struct sip_pvt *p) static int sip_poke_noanswer(const void *data) { struct sip_peer *peer = (struct sip_peer *)data; - + peer->pokeexpire = -1; if (peer->lastms > -1) { @@ -22973,12 +23063,15 @@ static int sip_poke_noanswer(const void *data) peer->call = dialog_unref(peer->call, "unref dialog peer->call"); /* peer->call = sip_destroy(peer->call);*/ } - - peer->lastms = -1; - ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + + /* Don't send a devstate change if nothing changed. */ + if (peer->lastms > -1) { + peer->lastms = -1; + ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + } /* Try again quickly */ - AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched, + AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer, unref_peer(_data, "removing poke peer ref"), unref_peer(peer, "removing poke peer ref"), @@ -23772,6 +23865,8 @@ static void set_peer_defaults(struct sip_peer *peer) peer->timer_t1 = global_t1; peer->timer_b = global_timer_b; clear_peer_mailboxes(peer); + peer->transports = default_transports; + peer->default_outbound_transport = default_primary_transport; } /*! \brief Create temporary peer (used in autocreatepeer mode) */ @@ -23914,6 +24009,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str /* If we have realm authentication information, remove them (reload) */ clear_realm_authentication(peer->auth); peer->auth = NULL; + /* clear the transport information. We will detect if a default value is required after parsing the config */ peer->default_outbound_transport = 0; peer->transports = 0; @@ -25145,6 +25241,8 @@ static int reload_config(enum channelreloadreason reason) ast_netsock_set_qos(sipsock, global_tos_sip, global_cos_sip, "SIP"); } } + } else { + ast_netsock_set_qos(sipsock, global_tos_sip, global_cos_sip, "SIP"); } if (stunaddr.sin_addr.s_addr != 0) { ast_debug(1, "stun to %s:%d\n", @@ -25803,9 +25901,8 @@ static int sip_do_reload(enum channelreloadreason reason) start_poke = time(0); /* Prune peers who still are supposed to be deleted */ - ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL, - "callback to remove marked peers"); - + unlink_marked_peers_from_tables(); + ast_debug(4, "--------------- Done destroying pruned peers\n"); /* Send qualify (OPTIONS) to all peers */ diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index 4a0984cdd..f2eae27c4 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -644,7 +644,7 @@ static struct chan_usbradio_pvt usbradio_default = { /* DECLARE FUNCTION PROTOTYPES */ -static void store_txtoctype(struct chan_usbradio_pvt *o, char *s); +static void store_txtoctype(struct chan_usbradio_pvt *o, const char *s); static int hidhdwconfig(struct chan_usbradio_pvt *o); static int set_txctcss_level(struct chan_usbradio_pvt *o); static void pmrdump(struct chan_usbradio_pvt *o); @@ -1118,8 +1118,7 @@ static void *hidthread(void *arg) struct usb_device *usb_dev; struct usb_dev_handle *usb_handle; struct chan_usbradio_pvt *o = (struct chan_usbradio_pvt *) arg; - struct timeval to; - fd_set rfds; + struct pollfd pfd = { .events = POLLIN }; usb_dev = hid_device_init(o->devstr); if (usb_dev == NULL) { @@ -1155,63 +1154,49 @@ static void *hidthread(void *arg) traceusb1(("hidthread: Starting normally on %s!!\n",o->name)); lastrx = 0; // popen - while(!o->stophid) - { - to.tv_sec = 0; - to.tv_usec = 50000; // maw sph + while (!o->stophid) { + pfd.fd = o->pttkick[0]; + pfd.revents = 0; - FD_ZERO(&rfds); - FD_SET(o->pttkick[0],&rfds); - /* ast_select emulates linux behaviour in terms of timeout handling */ - res = ast_select(o->pttkick[0] + 1, &rfds, NULL, NULL, &to); + res = ast_poll(&pfd, 1, 50); if (res < 0) { - ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno)); + ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno)); usleep(10000); continue; } - if (FD_ISSET(o->pttkick[0],&rfds)) - { + if (pfd.revents & POLLIN) { char c; - if (read(o->pttkick[0],&c,1) < 0) { + if (read(o->pttkick[0], &c, 1) < 0) { ast_log(LOG_ERROR, "read() failed: %s\n", strerror(errno)); } } - if(o->wanteeprom) - { + if (o->wanteeprom) { ast_mutex_lock(&o->eepromlock); - if (o->eepromctl == 1) /* to read */ - { + if (o->eepromctl == 1) { /* to read */ /* if CS okay */ - if (!get_eeprom(usb_handle,o->eeprom)) - { - if (o->eeprom[EEPROM_MAGIC_ADDR] != EEPROM_MAGIC) - { - ast_log(LOG_NOTICE,"UNSUCCESSFUL: EEPROM MAGIC NUMBER BAD on channel %s\n",o->name); - } - else - { + if (!get_eeprom(usb_handle, o->eeprom)) { + if (o->eeprom[EEPROM_MAGIC_ADDR] != EEPROM_MAGIC) { + ast_log(LOG_NOTICE, "UNSUCCESSFUL: EEPROM MAGIC NUMBER BAD on channel %s\n", o->name); + } else { o->rxmixerset = o->eeprom[EEPROM_RXMIXERSET]; - o->txmixaset = o->eeprom[EEPROM_TXMIXASET]; + o->txmixaset = o->eeprom[EEPROM_TXMIXASET]; o->txmixbset = o->eeprom[EEPROM_TXMIXBSET]; - memcpy(&o->rxvoiceadj,&o->eeprom[EEPROM_RXVOICEADJ],sizeof(float)); - memcpy(&o->rxctcssadj,&o->eeprom[EEPROM_RXCTCSSADJ],sizeof(float)); + memcpy(&o->rxvoiceadj, &o->eeprom[EEPROM_RXVOICEADJ], sizeof(float)); + memcpy(&o->rxctcssadj, &o->eeprom[EEPROM_RXCTCSSADJ], sizeof(float)); o->txctcssadj = o->eeprom[EEPROM_TXCTCSSADJ]; o->rxsquelchadj = o->eeprom[EEPROM_RXSQUELCHADJ]; ast_log(LOG_NOTICE,"EEPROM Loaded on channel %s\n",o->name); } - } - else - { - ast_log(LOG_NOTICE,"USB Adapter has no EEPROM installed or Checksum BAD on channel %s\n",o->name); + } else { + ast_log(LOG_NOTICE, "USB Adapter has no EEPROM installed or Checksum BAD on channel %s\n", o->name); } hid_set_outputs(usb_handle,bufsave); - } - if (o->eepromctl == 2) /* to write */ - { + } + if (o->eepromctl == 2) { /* to write */ put_eeprom(usb_handle,o->eeprom); hid_set_outputs(usb_handle,bufsave); - ast_log(LOG_NOTICE,"USB Parameters written to EEPROM on %s\n",o->name); + ast_log(LOG_NOTICE, "USB Parameters written to EEPROM on %s\n", o->name); } o->eepromctl = 0; ast_mutex_unlock(&o->eepromlock); @@ -1219,38 +1204,43 @@ static void *hidthread(void *arg) buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl; hid_get_inputs(usb_handle,buf); keyed = !(buf[o->hid_io_cor_loc] & o->hid_io_cor); - if (keyed != o->rxhidsq) - { - if(o->debuglevel)printf("chan_usbradio() hidthread: update rxhidsq = %d\n",keyed); + if (keyed != o->rxhidsq) { + if (o->debuglevel) { + printf("chan_usbradio() hidthread: update rxhidsq = %d\n", keyed); + } o->rxhidsq=keyed; } /* if change in tx state as controlled by xpmr */ - txtmp=o->pmrChan->txPttOut; - - if (o->lasttx != txtmp) - { - o->pmrChan->txPttHid=o->lasttx = txtmp; - if(o->debuglevel)printf("hidthread: tx set to %d\n",txtmp); - buf[o->hid_gpio_loc] = 0; - if (!o->invertptt) - { - if (txtmp) buf[o->hid_gpio_loc] = o->hid_io_ptt; + txtmp = o->pmrChan->txPttOut; + + if (o->lasttx != txtmp) { + o->pmrChan->txPttHid = o->lasttx = txtmp; + if (o->debuglevel) { + ast_debug(0, "hidthread: tx set to %d\n", txtmp); } - else - { - if (!txtmp) buf[o->hid_gpio_loc] = o->hid_io_ptt; + buf[o->hid_gpio_loc] = 0; + if (!o->invertptt) { + if (txtmp) { + buf[o->hid_gpio_loc] = o->hid_io_ptt; + } + } else { + if (!txtmp) { + buf[o->hid_gpio_loc] = o->hid_io_ptt; + } } buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl; - memcpy(bufsave,buf,sizeof(buf)); - hid_set_outputs(usb_handle,buf); + memcpy(bufsave, buf, sizeof(buf)); + hid_set_outputs(usb_handle, buf); } time(&o->lasthidtime); } buf[o->hid_gpio_loc] = 0; - if (o->invertptt) buf[o->hid_gpio_loc] = o->hid_io_ptt; + if (o->invertptt) { + buf[o->hid_gpio_loc] = o->hid_io_ptt; + } buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl; - hid_set_outputs(usb_handle,buf); + hid_set_outputs(usb_handle, buf); pthread_exit(0); } @@ -1451,37 +1441,29 @@ static void *sound_thread(void *arg) */ read(o->sounddev, ign, sizeof(ign)); for (;;) { - fd_set rfds, wfds; - int maxfd, res; - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_SET(o->sndcmd[0], &rfds); - maxfd = o->sndcmd[0]; /* pipe from the main process */ - if (o->cursound > -1 && o->sounddev < 0) + struct pollfd pfd[2] = { { .fd = o->sndcmd[0], .events = POLLIN }, { .fd = o->sounddev } }; + int res; + + if (o->cursound > -1 && o->sounddev < 0) { setformat(o, O_RDWR); /* need the channel, try to reopen */ - else if (o->cursound == -1 && o->owner == NULL) - { + } else if (o->cursound == -1 && o->owner == NULL) { setformat(o, O_CLOSE); /* can close */ } if (o->sounddev > -1) { if (!o->owner) { /* no one owns the audio, so we must drain it */ - FD_SET(o->sounddev, &rfds); - maxfd = MAX(o->sounddev, maxfd); + pfd[1].events = POLLIN; } if (o->cursound > -1) { - FD_SET(o->sounddev, &wfds); - maxfd = MAX(o->sounddev, maxfd); + pfd[1].events |= POLLOUT; } } - /* ast_select emulates linux behaviour in terms of timeout handling */ - res = ast_select(maxfd + 1, &rfds, &wfds, NULL, NULL); + res = ast_poll(pfd, o->sounddev > -1 ? 2 : 1, -1); if (res < 1) { - ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno)); + ast_log(LOG_WARNING, "poll failed: %s\n", strerror(errno)); sleep(1); continue; } - if (FD_ISSET(o->sndcmd[0], &rfds)) { + if (pfd[0].revents & POLLIN) { /* read which sound to play from the pipe */ int i, what = -1; @@ -1494,14 +1476,17 @@ static void *sound_thread(void *arg) break; } } - if (sounds[i].ind == -1) + if (sounds[i].ind == -1) { ast_log(LOG_WARNING, "invalid sound index: %d\n", what); + } } if (o->sounddev > -1) { - if (FD_ISSET(o->sounddev, &rfds)) /* read and ignore errors */ + if (pfd[1].revents & POLLIN) { /* read and ignore errors */ read(o->sounddev, ign, sizeof(ign)); - if (FD_ISSET(o->sounddev, &wfds)) + } + if (pfd[1].revents & POLLOUT) { send_sound(o); + } } } return NULL; /* Never reached */ @@ -2694,7 +2679,7 @@ static void store_callerid(struct chan_usbradio_pvt *o, char *s) } #endif -static void store_rxdemod(struct chan_usbradio_pvt *o, char *s) +static void store_rxdemod(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no")){ o->rxdemod = RX_AUDIO_NONE; @@ -2713,7 +2698,7 @@ static void store_rxdemod(struct chan_usbradio_pvt *o, char *s) } -static void store_txmixa(struct chan_usbradio_pvt *o, char *s) +static void store_txmixa(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no")){ o->txmixa = TX_OUT_OFF; @@ -2737,7 +2722,7 @@ static void store_txmixa(struct chan_usbradio_pvt *o, char *s) //ast_log(LOG_WARNING, "set txmixa = %s\n", s); } -static void store_txmixb(struct chan_usbradio_pvt *o, char *s) +static void store_txmixb(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no")){ o->txmixb = TX_OUT_OFF; @@ -2762,7 +2747,7 @@ static void store_txmixb(struct chan_usbradio_pvt *o, char *s) } /* */ -static void store_rxcdtype(struct chan_usbradio_pvt *o, char *s) +static void store_rxcdtype(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no")){ o->rxcdtype = CD_IGNORE; @@ -2787,7 +2772,7 @@ static void store_rxcdtype(struct chan_usbradio_pvt *o, char *s) } /* */ -static void store_rxsdtype(struct chan_usbradio_pvt *o, char *s) +static void store_rxsdtype(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no") || !strcasecmp(s,"SD_IGNORE")){ o->rxsdtype = SD_IGNORE; @@ -2809,7 +2794,7 @@ static void store_rxsdtype(struct chan_usbradio_pvt *o, char *s) } /* */ -static void store_rxgain(struct chan_usbradio_pvt *o, char *s) +static void store_rxgain(struct chan_usbradio_pvt *o, const char *s) { float f; sscanf(s, "%30f", &f); @@ -2818,7 +2803,7 @@ static void store_rxgain(struct chan_usbradio_pvt *o, char *s) } /* */ -static void store_rxvoiceadj(struct chan_usbradio_pvt *o, char *s) +static void store_rxvoiceadj(struct chan_usbradio_pvt *o, const char *s) { float f; sscanf(s, "%30f", &f); @@ -2827,7 +2812,7 @@ static void store_rxvoiceadj(struct chan_usbradio_pvt *o, char *s) } /* */ -static void store_rxctcssadj(struct chan_usbradio_pvt *o, char *s) +static void store_rxctcssadj(struct chan_usbradio_pvt *o, const char *s) { float f; sscanf(s, "%30f", &f); @@ -2836,7 +2821,7 @@ static void store_rxctcssadj(struct chan_usbradio_pvt *o, char *s) } /* */ -static void store_txtoctype(struct chan_usbradio_pvt *o, char *s) +static void store_txtoctype(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no") || !strcasecmp(s,"TOC_NONE")){ o->txtoctype = TOC_NONE; diff --git a/channels/console_video.c b/channels/console_video.c index 88bf807d5..c26ac983f 100644 --- a/channels/console_video.c +++ b/channels/console_video.c @@ -234,34 +234,34 @@ struct video_out_desc { * and contain all configurtion info. */ struct video_desc { - char codec_name[64]; /* the codec we use */ + char codec_name[64]; /* the codec we use */ - int stayopen; /* set if gui starts manually */ - pthread_t vthread; /* video thread */ - ast_mutex_t dec_lock; /* sync decoder and video thread */ - int shutdown; /* set to shutdown vthread */ - struct ast_channel *owner; /* owner channel */ + int stayopen; /* set if gui starts manually */ + pthread_t vthread; /* video thread */ + ast_mutex_t dec_lock; /* sync decoder and video thread */ + int shutdown; /* set to shutdown vthread */ + struct ast_channel *owner; /* owner channel */ - struct fbuf_t enc_in; /* encoder input buffer, allocated in video_out_init() */ + struct fbuf_t enc_in; /* encoder input buffer, allocated in video_out_init() */ - char keypad_file[256]; /* image for the keypad */ - char keypad_font[256]; /* font for the keypad */ + char keypad_file[256]; /* image for the keypad */ + char keypad_font[256]; /* font for the keypad */ - char sdl_videodriver[256]; + char sdl_videodriver[256]; - struct fbuf_t rem_dpy; /* display remote video, no buffer (it is in win[WIN_REMOTE].bmp) */ - struct fbuf_t loc_dpy; /* display local source, no buffer (managed by SDL in bmp[1]) */ + struct fbuf_t rem_dpy; /* display remote video, no buffer (it is in win[WIN_REMOTE].bmp) */ + struct fbuf_t loc_dpy; /* display local source, no buffer (managed by SDL in bmp[1]) */ /* geometry of the thumbnails for all video sources. */ - struct fbuf_t src_dpy[MAX_VIDEO_SOURCES]; /* no buffer allocated here */ + struct fbuf_t src_dpy[MAX_VIDEO_SOURCES]; /* no buffer allocated here */ - int frame_freeze; /* flag to freeze the incoming frame */ + int frame_freeze; /* flag to freeze the incoming frame */ /* local information for grabbers, codecs, gui */ - struct gui_info *gui; - struct video_dec_desc *in; /* remote video descriptor */ - struct video_out_desc out; /* local video descriptor */ + struct gui_info *gui; + struct video_dec_desc *in; /* remote video descriptor */ + struct video_out_desc out; /* local video descriptor */ }; static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p); @@ -387,8 +387,9 @@ static struct fbuf_t *grabber_read(struct video_device *dev, int fps) */ static void grabber_move(struct video_device *dev, int dx, int dy) { - if (dev->grabber && dev->grabber->move) - dev->grabber->move(dev->grabber_data, dx, dy); + if (dev->grabber && dev->grabber->move) { + dev->grabber->move(dev->grabber_data, dx, dy); + } } /* @@ -508,33 +509,32 @@ static int video_out_init(struct video_desc *env) /* now setup the parameters for the encoder. * XXX should be codec-specific */ - { - AVCodecContext *enc_ctx = avcodec_alloc_context(); - v->enc_ctx = enc_ctx; - enc_ctx->pix_fmt = enc_in->pix_fmt; - enc_ctx->width = enc_in->w; - enc_ctx->height = enc_in->h; - /* XXX rtp_callback ? - * rtp_mode so ffmpeg inserts as many start codes as possible. - */ - enc_ctx->rtp_mode = 1; - enc_ctx->rtp_payload_size = v->mtu / 2; // mtu/2 - enc_ctx->bit_rate = v->bitrate; - enc_ctx->bit_rate_tolerance = enc_ctx->bit_rate/2; - enc_ctx->qmin = v->qmin; /* should be configured */ - enc_ctx->time_base = (AVRational){1, v->fps}; - enc_ctx->gop_size = v->fps*5; // emit I frame every 5 seconds - - v->enc->enc_init(v->enc_ctx); - - if (avcodec_open(enc_ctx, v->codec) < 0) { - ast_log(LOG_WARNING, "Unable to initialize the encoder %d\n", - codec); - av_free(enc_ctx); - v->enc_ctx = NULL; - return video_out_uninit(env); + { + AVCodecContext *enc_ctx = avcodec_alloc_context(); + v->enc_ctx = enc_ctx; + enc_ctx->pix_fmt = enc_in->pix_fmt; + enc_ctx->width = enc_in->w; + enc_ctx->height = enc_in->h; + /* XXX rtp_callback ? + * rtp_mode so ffmpeg inserts as many start codes as possible. + */ + enc_ctx->rtp_mode = 1; + enc_ctx->rtp_payload_size = v->mtu / 2; // mtu/2 + enc_ctx->bit_rate = v->bitrate; + enc_ctx->bit_rate_tolerance = enc_ctx->bit_rate/2; + enc_ctx->qmin = v->qmin; /* should be configured */ + enc_ctx->time_base = (AVRational){1, v->fps}; + enc_ctx->gop_size = v->fps*5; // emit I frame every 5 seconds + + v->enc->enc_init(v->enc_ctx); + + if (avcodec_open(enc_ctx, v->codec) < 0) { + ast_log(LOG_WARNING, "Unable to initialize the encoder %d\n", codec); + av_free(enc_ctx); + v->enc_ctx = NULL; + return video_out_uninit(env); + } } - } /* * Allocate enough for the encoded bitstream. As we are compressing, * we hope that the output is never larger than the input size. @@ -637,9 +637,9 @@ static void my_scale(struct fbuf_t *in, AVPicture *p_in, p_in = fill_pict(in, &my_p_in); if (p_out == NULL) p_out = fill_pict(out, &my_p_out); - - /*if win_w is different from zero then we must change - the size of the scaled buffer (the position is already + + /*if win_w is different from zero then we must change + the size of the scaled buffer (the position is already encoded into the out parameter)*/ if (out->win_w) { /* picture in picture enabled */ eff_w=out->win_w; @@ -650,26 +650,26 @@ static void my_scale(struct fbuf_t *in, AVPicture *p_in, img_convert(p_out, out->pix_fmt, p_in, in->pix_fmt, in->w, in->h); #else /* XXX replacement */ - { - struct SwsContext *convert_ctx; - - convert_ctx = sws_getContext(in->w, in->h, in->pix_fmt, - eff_w, eff_h, out->pix_fmt, - SWS_BICUBIC, NULL, NULL, NULL); - if (convert_ctx == NULL) { - ast_log(LOG_ERROR, "FFMPEG::convert_cmodel : swscale context initialization failed"); - return; + { + struct SwsContext *convert_ctx; + + convert_ctx = sws_getContext(in->w, in->h, in->pix_fmt, + eff_w, eff_h, out->pix_fmt, + SWS_BICUBIC, NULL, NULL, NULL); + if (convert_ctx == NULL) { + ast_log(LOG_ERROR, "FFMPEG::convert_cmodel : swscale context initialization failed"); + return; + } + if (0) + ast_log(LOG_WARNING, "in %d %dx%d out %d %dx%d\n", + in->pix_fmt, in->w, in->h, out->pix_fmt, eff_w, eff_h); + sws_scale(convert_ctx, + p_in->data, p_in->linesize, + in->w, in->h, /* src slice */ + p_out->data, p_out->linesize); + + sws_freeContext(convert_ctx); } - if (0) - ast_log(LOG_WARNING, "in %d %dx%d out %d %dx%d\n", - in->pix_fmt, in->w, in->h, out->pix_fmt, eff_w, eff_h); - sws_scale(convert_ctx, - p_in->data, p_in->linesize, - in->w, in->h, /* src slice */ - p_out->data, p_out->linesize); - - sws_freeContext(convert_ctx); - } #endif /* XXX replacement */ } @@ -873,18 +873,20 @@ static void *video_thread(void *arg) } } sdl_setup(env); - if (!ast_strlen_zero(save_display)) + if (!ast_strlen_zero(save_display)) { setenv("DISPLAY", save_display, 1); + } ast_mutex_init(&env->dec_lock); /* used to sync decoder and renderer */ if (grabber_open(&env->out)) { ast_log(LOG_WARNING, "cannot open local video source\n"); - } + } - if (env->out.device_num) + if (env->out.device_num) { env->out.devices[env->out.device_primary].status_index |= IS_PRIMARY | IS_SECONDARY; - + } + /* even if no device is connected, we must call video_out_init, * as some of the data structures it initializes are * used in get_video_frames() @@ -893,14 +895,14 @@ static void *video_thread(void *arg) /* Writes intial status of the sources. */ if (env->gui) { - for (i = 0; i < env->out.device_num; i++) { - print_message(env->gui->thumb_bd_array[i].board, - src_msgs[env->out.devices[i].status_index]); - } + for (i = 0; i < env->out.device_num; i++) { + print_message(env->gui->thumb_bd_array[i].board, + src_msgs[env->out.devices[i].status_index]); + } } for (;;) { - struct timeval t = { 0, 50000 }; /* XXX 20 times/sec */ + struct timespec t = { 0, 50000000 }; /* XXX 20 times/sec */ struct ast_frame *p, *f; struct ast_channel *chan; int fd; @@ -908,13 +910,14 @@ static void *video_thread(void *arg) /* determine if video format changed */ if (count++ % 10 == 0) { - if (env->out.sendvideo && env->out.devices) - sprintf(buf, "%s %s %dx%d @@ %dfps %dkbps", + if (env->out.sendvideo && env->out.devices) { + snprintf(buf, sizeof(buf), "%s %s %dx%d @@ %dfps %dkbps", env->out.devices[env->out.device_primary].name, env->codec_name, env->enc_in.w, env->enc_in.h, - env->out.fps, env->out.bitrate/1000); - else - sprintf(buf, "hold"); + env->out.fps, env->out.bitrate / 1000); + } else { + sprintf(buf, "hold"); + } caption = buf; } @@ -923,36 +926,36 @@ static void *video_thread(void *arg) * otherwise the drag will not work */ if (env->gui) eventhandler(env, caption); - + /* sleep for a while */ - ast_select(0, NULL, NULL, NULL, &t); + nanosleep(&t, NULL); if (env->in) { - struct video_dec_desc *v = env->in; - - /* - * While there is something to display, call the decoder and free - * the buffer, possibly enabling the receiver to store new data. - */ - while (v->dec_in_dpy) { - struct fbuf_t *tmp = v->dec_in_dpy; /* store current pointer */ - - /* decode the frame, but show it only if not frozen */ - if (v->d_callbacks->dec_run(v, tmp) && !env->frame_freeze) - show_frame(env, WIN_REMOTE); - tmp->used = 0; /* mark buffer as free */ - tmp->ebit = 0; - ast_mutex_lock(&env->dec_lock); - if (++v->dec_in_dpy == &v->dec_in[N_DEC_IN]) /* advance to next, circular */ - v->dec_in_dpy = &v->dec_in[0]; - - if (v->dec_in_cur == NULL) /* receiver was idle, enable it... */ - v->dec_in_cur = tmp; /* using the slot just freed */ - else if (v->dec_in_dpy == v->dec_in_cur) /* this was the last slot */ - v->dec_in_dpy = NULL; /* nothing more to display */ - ast_mutex_unlock(&env->dec_lock); + struct video_dec_desc *v = env->in; + + /* + * While there is something to display, call the decoder and free + * the buffer, possibly enabling the receiver to store new data. + */ + while (v->dec_in_dpy) { + struct fbuf_t *tmp = v->dec_in_dpy; /* store current pointer */ + + /* decode the frame, but show it only if not frozen */ + if (v->d_callbacks->dec_run(v, tmp) && !env->frame_freeze) + show_frame(env, WIN_REMOTE); + tmp->used = 0; /* mark buffer as free */ + tmp->ebit = 0; + ast_mutex_lock(&env->dec_lock); + if (++v->dec_in_dpy == &v->dec_in[N_DEC_IN]) /* advance to next, circular */ + v->dec_in_dpy = &v->dec_in[0]; + + if (v->dec_in_cur == NULL) /* receiver was idle, enable it... */ + v->dec_in_cur = tmp; /* using the slot just freed */ + else if (v->dec_in_dpy == v->dec_in_cur) /* this was the last slot */ + v->dec_in_dpy = NULL; /* nothing more to display */ + ast_mutex_unlock(&env->dec_lock); + } } - } if (env->shutdown) break; @@ -988,7 +991,7 @@ static void *video_thread(void *arg) for (p = f; p; p = AST_LIST_NEXT(p, frame_list)) { if (write(fd, &blah, l) != l) ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d: %s!\n", - chan->name, f->frametype, f->subclass, strerror(errno)); + chan->name, f->frametype, f->subclass, strerror(errno)); } } ast_channel_unlock(chan); @@ -1194,13 +1197,13 @@ int console_video_cli(struct video_desc *env, const char *var, int fd) if (env == NULL) return 1; /* unrecognised */ - if (!strcasecmp(var, "videodevice")) { + if (!strcasecmp(var, "videodevice")) { ast_cli(fd, "videodevice is [%s]\n", env->out.devices[env->out.device_primary].name); - } else if (!strcasecmp(var, "videocodec")) { + } else if (!strcasecmp(var, "videocodec")) { ast_cli(fd, "videocodec is [%s]\n", env->codec_name); - } else if (!strcasecmp(var, "sendvideo")) { + } else if (!strcasecmp(var, "sendvideo")) { ast_cli(fd, "sendvideo is [%s]\n", env->out.sendvideo ? "on" : "off"); - } else if (!strcasecmp(var, "video_size")) { + } else if (!strcasecmp(var, "video_size")) { int in_w = 0, in_h = 0; if (env->in) { in_w = env->in->dec_out.w; @@ -1212,22 +1215,22 @@ int console_video_cli(struct video_desc *env, const char *var, int fd) env->loc_dpy.w, env->loc_dpy.h, env->rem_dpy.w, env->rem_dpy.h, in_w, in_h); - } else if (!strcasecmp(var, "bitrate")) { + } else if (!strcasecmp(var, "bitrate")) { ast_cli(fd, "bitrate is [%d]\n", env->out.bitrate); - } else if (!strcasecmp(var, "qmin")) { + } else if (!strcasecmp(var, "qmin")) { ast_cli(fd, "qmin is [%d]\n", env->out.qmin); - } else if (!strcasecmp(var, "fps")) { + } else if (!strcasecmp(var, "fps")) { ast_cli(fd, "fps is [%d]\n", env->out.fps); - } else if (!strcasecmp(var, "startgui")) { + } else if (!strcasecmp(var, "startgui")) { env->stayopen = 1; console_video_start(env, NULL); - } else if (!strcasecmp(var, "stopgui") && env->stayopen != 0) { + } else if (!strcasecmp(var, "stopgui") && env->stayopen != 0) { env->stayopen = 0; if (env->gui && env->owner) ast_cli_command(-1, "console hangup"); else /* not in a call */ console_video_uninit(env); - } else { + } else { return 1; /* unrecognised */ } return 0; /* recognised */ -- cgit v1.2.3